use cached_context::{CacheConfig, CacheStore, Error};
use rusqlite::params;
async fn create_test_store(temp_dir: &tempfile::TempDir) -> CacheStore {
let config = CacheConfig {
db_path: temp_dir.path().join("test_cache.db"),
session_id: "test-session".to_string(),
workdir: temp_dir.path().to_path_buf(),
};
let store = CacheStore::new(config).expect("Failed to create cache store");
store.init().await.expect("Failed to init cache store");
store
}
#[tokio::test]
async fn test_read_nonexistent_file() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let store = create_test_store(&temp_dir).await;
let result = store.read_file("nonexistent.txt", None, None, false).await;
assert!(result.is_err());
match result {
Err(Error::FileNotFound(path)) => {
assert!(path.contains("nonexistent.txt"));
}
Err(e) => panic!("Expected FileNotFound error, got: {:?}", e),
Ok(_) => panic!("Expected error, got success"),
}
}
#[tokio::test]
async fn test_read_file_with_invalid_path() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let store = create_test_store(&temp_dir).await;
let result = store.read_file("/invalid/\0path", None, None, false).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_mcp_read_nonexistent_file() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let store = create_test_store(&temp_dir).await;
let service = cached_context::mcp::CachebroMcpService::new(store);
let result = service
.read_file("/nonexistent/file.txt".to_string(), None, None, false)
.await
.expect("read_file should not panic");
let text = result
.content
.first()
.and_then(|c| c.as_text())
.map(|t| t.text.clone())
.unwrap_or_default();
assert!(
text.to_lowercase().contains("error") || text.to_lowercase().contains("not found"),
"Error response should mention error or not found, got: {}",
text
);
}
#[tokio::test]
async fn test_cache_operations_after_clear() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "test content").expect("Failed to write test file");
let store = create_test_store(&temp_dir).await;
let result1 = store.read_file("test.txt", None, None, false).await.expect("First read should work");
assert!(!result1.cached);
store.clear().await.expect("Clear should work");
let result2 = store.read_file("test.txt", None, None, false).await.expect("Read after clear should work");
assert!(!result2.cached, "After clear, file should not be cached");
}
#[tokio::test]
async fn test_multiple_sessions_isolated() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "test content").expect("Failed to write test file");
let config1 = CacheConfig {
db_path: temp_dir.path().join("test_cache.db"),
session_id: "session-1".to_string(),
workdir: temp_dir.path().to_path_buf(),
};
let store1 = CacheStore::new(config1).expect("Failed to create store1");
store1.init().await.expect("Failed to init store1");
let config2 = CacheConfig {
db_path: temp_dir.path().join("test_cache.db"),
session_id: "session-2".to_string(),
workdir: temp_dir.path().to_path_buf(),
};
let store2 = CacheStore::new(config2).expect("Failed to create store2");
store2.init().await.expect("Failed to init store2");
let result1 = store1.read_file("test.txt", None, None, false).await.expect("Session 1 read should work");
assert!(!result1.cached);
let _result2 = store2.read_file("test.txt", None, None, false).await.expect("Session 2 read should work");
}
#[tokio::test]
async fn test_token_estimation_savings() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "test content for token test").expect("Failed to write test file");
let store = create_test_store(&temp_dir).await;
let _result1 = store.read_file("test.txt", None, None, false).await.expect("First read should work");
let stats1 = store.get_stats().await.expect("Get stats should work");
assert!(
stats1.files_tracked > 0,
"Should track the file after first read, got: {}",
stats1.files_tracked
);
}
#[tokio::test]
async fn test_partial_read_offset_limit() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let test_file = temp_dir.path().join("test.txt");
std::fs::write(
&test_file,
"line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n",
)
.expect("Failed to write test file");
let store = create_test_store(&temp_dir).await;
let result = store
.read_file("test.txt", Some(3), Some(2), false)
.await
.expect("Read with offset/limit should work");
assert_eq!(result.content, "line3\nline4");
assert_eq!(result.total_lines, 10);
}
#[tokio::test]
async fn test_force_read_bypasses_cache() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "original content").expect("Failed to write test file");
let store = create_test_store(&temp_dir).await;
let result1 = store.read_file("test.txt", None, None, false).await.expect("First read should work");
assert!(!result1.cached);
let result2 = store
.read_file("test.txt", None, None, true)
.await
.expect("Force read should work");
assert!(result2.content.contains("original content"));
}
#[tokio::test]
async fn test_changed_file_old_version_missing() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "original content\nline 2\n").expect("Failed to write test file");
let db_path = temp_dir.path().join("test_cache.db");
let config = CacheConfig {
db_path: db_path.clone(),
session_id: "test-session".to_string(),
workdir: temp_dir.path().to_path_buf(),
};
let store = CacheStore::new(config).expect("Failed to create cache store");
store.init().await.expect("Failed to init cache store");
let result1 = store
.read_file("test.txt", None, None, false)
.await
.expect("First read should work");
assert!(!result1.cached);
let original_hash = result1.hash.clone();
let conn = rusqlite::Connection::open(&db_path).expect("Failed to open DB");
conn.execute(
"DELETE FROM file_versions WHERE path = ? AND hash = ?",
params!["test.txt", original_hash],
)
.expect("Failed to delete old version");
drop(conn);
std::fs::write(&test_file, "modified content\nline 2\nline 3\n")
.expect("Failed to modify test file");
let result2 = store
.read_file("test.txt", None, None, false)
.await
.expect("Read with missing old version should work");
assert!(
!result2.cached,
"Should NOT be cached when old version is missing"
);
assert!(
result2.content.contains("modified content"),
"Should return the new full content, got: {}",
result2.content
);
assert!(
result2.diff.is_none(),
"Should NOT have a diff when old version is missing"
);
}