Squashed 'vendor/ruvector/' content from commit b64c2172

git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
commit d803bfe2b1
7854 changed files with 3522914 additions and 0 deletions

View 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;