mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-08-07 01:33:52 +00:00
fix(cache): treat corrupted cache entries as cache misses
Issue #9: Previously, corrupted JSON in cache files would cause commands to fail with a ParseError. Now: - IO errors and parse errors are caught in read_entry() - A warning is printed to stderr with the data_type/symbol - The corrupted file is deleted - Ok(None) is returned (treated as cache miss) This applies to both get() and get_stale() since they share read_entry(). Added tests: - corrupted_cache_entry_returns_none_and_deletes_file - corrupted_cache_entry_get_stale_returns_none_and_deletes_file Both verify the corrupted file is cleaned up after the graceful failure.
This commit is contained in:
parent
df80a8201a
commit
299d315a16
1 changed files with 71 additions and 3 deletions
74
src/cache.rs
74
src/cache.rs
|
|
@ -163,9 +163,28 @@ impl Cache {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let raw = fs::read_to_string(&path).map_err(|e| IdxError::Io(e.to_string()))?;
|
let raw = match fs::read_to_string(&path) {
|
||||||
let entry: CacheEntry<T> =
|
Ok(s) => s,
|
||||||
serde_json::from_str(&raw).map_err(|e| IdxError::ParseError(e.to_string()))?;
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"warning: corrupted cache entry for {}/{}, treating as miss: {}",
|
||||||
|
data_type, symbol, e
|
||||||
|
);
|
||||||
|
let _ = fs::remove_file(&path);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let entry: CacheEntry<T> = match serde_json::from_str(&raw) {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"warning: corrupted cache entry for {}/{}, treating as miss: {}",
|
||||||
|
data_type, symbol, e
|
||||||
|
);
|
||||||
|
let _ = fs::remove_file(&path);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
if entry.schema_version != CURRENT_SCHEMA_VERSION {
|
if entry.schema_version != CURRENT_SCHEMA_VERSION {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"debug: cache schema mismatch for {} (got {}, expected {})",
|
"debug: cache schema mismatch for {} (got {}, expected {})",
|
||||||
|
|
@ -240,4 +259,53 @@ mod tests {
|
||||||
.expect("cache read stale");
|
.expect("cache read stale");
|
||||||
assert_eq!(stale, Some(T { v: 7 }));
|
assert_eq!(stale, Some(T { v: 7 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn corrupted_cache_entry_returns_none_and_deletes_file() {
|
||||||
|
let root = tmp();
|
||||||
|
let cache = Cache::with_root(root.clone());
|
||||||
|
|
||||||
|
// Write invalid JSON to a cache file
|
||||||
|
let path = root.join("quote/CORRUPT.JK.json");
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
fs::create_dir_all(parent).expect("create parent dir");
|
||||||
|
}
|
||||||
|
fs::write(&path, "this is not valid json {{{").expect("write corrupted cache");
|
||||||
|
|
||||||
|
assert!(path.exists(), "corrupted file should exist before get()");
|
||||||
|
|
||||||
|
// get() should return Ok(None), not an error
|
||||||
|
let result: Option<T> = cache.get("quote", "CORRUPT.JK").expect("get should not error");
|
||||||
|
assert_eq!(result, None, "corrupted entry should be treated as miss");
|
||||||
|
|
||||||
|
// The corrupted file should be deleted
|
||||||
|
assert!(!path.exists(), "corrupted file should be deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn corrupted_cache_entry_get_stale_returns_none_and_deletes_file() {
|
||||||
|
let root = tmp();
|
||||||
|
let cache = Cache::with_root(root.clone());
|
||||||
|
|
||||||
|
// Write invalid JSON to a cache file
|
||||||
|
let path = root.join("quote/STALE_CORRUPT.JK.json");
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
fs::create_dir_all(parent).expect("create parent dir");
|
||||||
|
}
|
||||||
|
fs::write(&path, "{ not valid json at all").expect("write corrupted cache");
|
||||||
|
|
||||||
|
assert!(path.exists(), "corrupted file should exist before get_stale()");
|
||||||
|
|
||||||
|
// get_stale() should return Ok(None), not an error
|
||||||
|
let result: Option<T> = cache
|
||||||
|
.get_stale("quote", "STALE_CORRUPT.JK")
|
||||||
|
.expect("get_stale should not error");
|
||||||
|
assert_eq!(
|
||||||
|
result, None,
|
||||||
|
"corrupted entry should be treated as miss in get_stale"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The corrupted file should be deleted
|
||||||
|
assert!(!path.exists(), "corrupted file should be deleted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue