test(sync): make downtime expiry checks deterministic

The sandboxed package build exposed a scheduler-sensitive rejected-index test: a 150 ms fixed sleep was expected to exceed the 100 ms hot expiry while staying below the 200 ms cold expiry. Under load it crossed both boundaries and failed after 794 passing tests.

Rewrite the persisted checkpoint timestamp to simulate downtime directly and widen the hot/cold expiry separation. Apply the same helper to the adjacent persistence test so neither test depends on wall-clock sleeping.

Only test setup changes; production expiry and persistence behavior are untouched. The simulated timestamp remains inside both tests temporary checkpoint files.

Validated with cargo fmt --check, git diff --check, and the three downtime-filtered unit tests running sequentially.
This commit is contained in:
DanConwayDev
2026-08-15 14:51:18 +00:00
parent bd371b17a4
commit 14170f202f
+21 -12
View File
@@ -1507,6 +1507,20 @@ mod tests {
keys.sign_event(unsigned).unwrap() keys.sign_event(unsigned).unwrap()
} }
fn simulate_checkpoint_downtime(path: &Path, downtime: Duration) {
let json = std::fs::read_to_string(path).expect("read rejected-event checkpoint");
let mut state: RejectedCacheState =
serde_json::from_str(&json).expect("parse rejected-event checkpoint");
state.saved_at = SystemTime::now()
.checked_sub(downtime)
.expect("simulated downtime must fit in SystemTime");
std::fs::write(
path,
serde_json::to_string_pretty(&state).expect("serialize rejected-event checkpoint"),
)
.expect("write rejected-event checkpoint");
}
#[tokio::test] #[tokio::test]
async fn test_hot_cache_stores_and_retrieves_events() { async fn test_hot_cache_stores_and_retrieves_events() {
let cache = HotCache::new(Duration::from_secs(120)); let cache = HotCache::new(Duration::from_secs(120));
@@ -2282,8 +2296,7 @@ mod tests {
// Save to disk // Save to disk
index.save_to_disk(&state_path).unwrap(); index.save_to_disk(&state_path).unwrap();
// Simulate downtime by sleeping simulate_checkpoint_downtime(&state_path, Duration::from_secs(1));
std::thread::sleep(Duration::from_millis(100));
// Restore // Restore
let index2 = let index2 =
@@ -2301,11 +2314,9 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap(); let temp_dir = tempfile::tempdir().unwrap();
let state_path = temp_dir.path().join("rejected_cache.json"); let state_path = temp_dir.path().join("rejected_cache.json");
// Create index with very short expiry // Keep a wide boundary around the simulated downtime so scheduler
let index = RejectedEventsIndex::new( // delays cannot also expire the cold entry under test.
Duration::from_millis(100), // Hot cache: 100ms let index = RejectedEventsIndex::new(Duration::from_secs(1), Duration::from_secs(60));
Duration::from_millis(200), // Cold index: 200ms
);
let event = create_test_event().await; let event = create_test_event().await;
index.add_announcement( index.add_announcement(
@@ -2318,16 +2329,14 @@ mod tests {
// Save to disk // Save to disk
index.save_to_disk(&state_path).unwrap(); index.save_to_disk(&state_path).unwrap();
// Simulate downtime longer than hot cache expiry simulate_checkpoint_downtime(&state_path, Duration::from_secs(10));
std::thread::sleep(Duration::from_millis(150));
// Restore // Restore
let index2 = let index2 = RejectedEventsIndex::new(Duration::from_secs(1), Duration::from_secs(60));
RejectedEventsIndex::new(Duration::from_millis(100), Duration::from_millis(200));
index2.restore_from_disk(&state_path).unwrap(); index2.restore_from_disk(&state_path).unwrap();
// Hot cache entry should have expired during downtime // Hot cache entry should have expired during downtime
// Cold index should still have it (200ms expiry) // Cold index should still have it (60s expiry)
assert_eq!(index2.hot_cache_len(), 1); assert_eq!(index2.hot_cache_len(), 1);
assert_eq!(index2.cold_index_len(), 1); assert_eq!(index2.cold_index_len(), 1);