Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
300
examples/scipix/tests/integration/cache_tests.rs
Normal file
300
examples/scipix/tests/integration/cache_tests.rs
Normal file
@@ -0,0 +1,300 @@
|
||||
// Cache integration tests
|
||||
//
|
||||
// Tests caching behavior, hit/miss ratios, similarity search, and persistence
|
||||
//
|
||||
// Note: These tests use mock test infrastructure.
|
||||
// Real OCR processing requires ONNX models to be configured.
|
||||
|
||||
use super::*;
|
||||
use crate::common::{CacheStats, OutputFormat};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_hit_miss_behavior() {
|
||||
let test_server = TestServer::with_cache()
|
||||
.await
|
||||
.expect("Failed to start test server with cache");
|
||||
|
||||
let image = images::generate_simple_equation("x^2");
|
||||
image.save("/tmp/cache_test_1.png").unwrap();
|
||||
|
||||
// First request - should miss cache
|
||||
let result1 = test_server
|
||||
.process_image("/tmp/cache_test_1.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Get cache stats
|
||||
let _stats = test_server
|
||||
.cache_stats()
|
||||
.await
|
||||
.expect("Failed to get cache stats");
|
||||
|
||||
// Second request - should hit cache
|
||||
let result2 = test_server
|
||||
.process_image("/tmp/cache_test_1.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Verify results match
|
||||
assert_eq!(result1.latex, result2.latex, "Cached result should match");
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_similarity_lookup() {
|
||||
let test_server = TestServer::with_cache()
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
// Create original image
|
||||
let image1 = images::generate_simple_equation("a + b");
|
||||
image1.save("/tmp/similarity_1.png").unwrap();
|
||||
|
||||
// Create similar image (slightly different rendering)
|
||||
let mut image2 = images::generate_simple_equation("a + b");
|
||||
images::add_slight_variation(&mut image2, 0.05);
|
||||
image2.save("/tmp/similarity_2.png").unwrap();
|
||||
|
||||
// Process first image
|
||||
let result1 = test_server
|
||||
.process_image("/tmp/similarity_1.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Process similar image
|
||||
let result2 = test_server
|
||||
.process_image("/tmp/similarity_2.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Results should be similar
|
||||
let similarity = latex::calculate_similarity(&result1.latex, &result2.latex);
|
||||
assert!(
|
||||
similarity > 0.9,
|
||||
"Similar images should produce similar results"
|
||||
);
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_eviction() {
|
||||
// Start server with small cache size
|
||||
let test_server = TestServer::with_cache_size(3)
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
// Create and process 5 different images
|
||||
for i in 0..5 {
|
||||
let eq = format!("x + {}", i);
|
||||
let image = images::generate_simple_equation(&eq);
|
||||
let path = format!("/tmp/eviction_{}.png", i);
|
||||
image.save(&path).unwrap();
|
||||
|
||||
test_server
|
||||
.process_image(&path, OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
}
|
||||
|
||||
// Get cache stats
|
||||
let stats = test_server
|
||||
.cache_stats()
|
||||
.await
|
||||
.expect("Failed to get cache stats");
|
||||
assert!(stats.current_size <= 3, "Cache should not exceed max size");
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_persistence() {
|
||||
let cache_dir = "/tmp/scipix_cache_persist";
|
||||
std::fs::create_dir_all(cache_dir).unwrap();
|
||||
|
||||
// Start server with persistent cache
|
||||
let test_server = TestServer::with_persistent_cache(cache_dir)
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
// Process image
|
||||
let image = images::generate_simple_equation("persistent");
|
||||
image.save("/tmp/persist_test.png").unwrap();
|
||||
|
||||
let result1 = test_server
|
||||
.process_image("/tmp/persist_test.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Shutdown server
|
||||
test_server.shutdown().await;
|
||||
|
||||
// Start new server with same cache directory
|
||||
let test_server2 = TestServer::with_persistent_cache(cache_dir)
|
||||
.await
|
||||
.expect("Failed to start second test server");
|
||||
|
||||
// Process same image - should hit persistent cache
|
||||
let result2 = test_server2
|
||||
.process_image("/tmp/persist_test.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Results should match
|
||||
assert_eq!(
|
||||
result1.latex, result2.latex,
|
||||
"Persistent cache should restore results"
|
||||
);
|
||||
|
||||
test_server2.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_invalidation() {
|
||||
let test_server = TestServer::with_cache()
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
// Process image
|
||||
let image = images::generate_simple_equation("invalidate");
|
||||
image.save("/tmp/invalidate_test.png").unwrap();
|
||||
|
||||
let result1 = test_server
|
||||
.process_image("/tmp/invalidate_test.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Invalidate cache
|
||||
test_server
|
||||
.invalidate_cache()
|
||||
.await
|
||||
.expect("Cache invalidation failed");
|
||||
|
||||
// Process again - should miss cache
|
||||
let result2 = test_server
|
||||
.process_image("/tmp/invalidate_test.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Results should match but processing should take time
|
||||
assert_eq!(result1.latex, result2.latex, "Results should still match");
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_hit_ratio() {
|
||||
let test_server = TestServer::with_cache()
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
// Create test images
|
||||
let equations = vec!["a", "b", "c"];
|
||||
for eq in &equations {
|
||||
let image = images::generate_simple_equation(eq);
|
||||
image.save(&format!("/tmp/ratio_{}.png", eq)).unwrap();
|
||||
}
|
||||
|
||||
// Process each image twice
|
||||
for eq in &equations {
|
||||
let path = format!("/tmp/ratio_{}.png", eq);
|
||||
|
||||
// First time (miss)
|
||||
test_server
|
||||
.process_image(&path, OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Second time (hit)
|
||||
test_server
|
||||
.process_image(&path, OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
}
|
||||
|
||||
// Get stats
|
||||
let _stats = test_server
|
||||
.cache_stats()
|
||||
.await
|
||||
.expect("Failed to get cache stats");
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_ttl_expiration() {
|
||||
// Start server with 1-second TTL
|
||||
let test_server = TestServer::with_cache_ttl(1)
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
// Process image
|
||||
let image = images::generate_simple_equation("ttl");
|
||||
image.save("/tmp/ttl_test.png").unwrap();
|
||||
|
||||
let result1 = test_server
|
||||
.process_image("/tmp/ttl_test.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Immediately reprocess - should hit cache
|
||||
let result2 = test_server
|
||||
.process_image("/tmp/ttl_test.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
assert_eq!(result1.latex, result2.latex);
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cache_concurrent_access() {
|
||||
let test_server = TestServer::with_cache()
|
||||
.await
|
||||
.expect("Failed to start test server");
|
||||
|
||||
let image = images::generate_simple_equation("concurrent");
|
||||
image.save("/tmp/concurrent_cache.png").unwrap();
|
||||
|
||||
// First request to populate cache
|
||||
test_server
|
||||
.process_image("/tmp/concurrent_cache.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
.expect("Processing failed");
|
||||
|
||||
// Spawn multiple concurrent requests
|
||||
let mut handles = vec![];
|
||||
for _ in 0..10 {
|
||||
let server = test_server.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
server
|
||||
.process_image("/tmp/concurrent_cache.png", OutputFormat::LaTeX)
|
||||
.await
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// Wait for all to complete
|
||||
let results = futures::future::join_all(handles).await;
|
||||
|
||||
// All should succeed and return same result
|
||||
assert!(
|
||||
results.iter().all(|r| r.is_ok()),
|
||||
"All requests should succeed"
|
||||
);
|
||||
|
||||
let first_latex = &results[0].as_ref().unwrap().as_ref().unwrap().latex;
|
||||
assert!(
|
||||
results
|
||||
.iter()
|
||||
.all(|r| { &r.as_ref().unwrap().as_ref().unwrap().latex == first_latex }),
|
||||
"All results should match"
|
||||
);
|
||||
|
||||
test_server.shutdown().await;
|
||||
}
|
||||
|
||||
// Re-export CacheStats for backward compatibility
|
||||
pub use crate::common::CacheStats as CacheStatsCompat;
|
||||
Reference in New Issue
Block a user