From 3f8125fdaf809ab118f16017bd4141bc82548a48 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sat, 12 Sep 2026 13:47:01 +0000 Subject: [PATCH] test(sync): simulate downtime in checkpoint integration test The persistence integration test sleeps 75 ms between 50 ms hot-cache and 100 ms cold-index expiry deadlines. Snapshot writes and host scheduling can consume the remaining margin, legitimately expiring both tiers. A sandboxed nixpkgs build exposed this after the streaming tests passed. Set the persisted saved_at timestamp back two minutes and use minute/week retention periods. This follows the synthetic-checkpoint approach already used by the rejected-index unit tests in 14170f202f815fc53424f2e0f2d8de1bd4ecf042. Retain all assertions so restoration must expire hot data while preserving cold metadata. Production persistence and expiry behavior are unchanged. Validation: independent review, rustfmt, git diff --check and patch application to the released 3.0.2 source pass. Full test execution remains pending in upstream CI and the host nixpkgs build; no heavy build ran here. Assisted-by: Codex (GPT-6) --- tests/purgatory_persistence.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/purgatory_persistence.rs b/tests/purgatory_persistence.rs index 8a9abcd..ebace42 100644 --- a/tests/purgatory_persistence.rs +++ b/tests/purgatory_persistence.rs @@ -766,11 +766,11 @@ async fn test_rejected_cache_entries_expired_during_downtime() { let temp_dir = tempfile::tempdir().unwrap(); let state_path = temp_dir.path().join("rejected_cache.json"); - // Create index with very short expiry - let index = RejectedEventsIndex::new( - Duration::from_millis(50), // Hot cache: 50ms - Duration::from_millis(100), // Cold index: 100ms - ); + // Use production-scale retention periods so host scheduling cannot + // accidentally expire the cold entry during this test. + let hot_expiry = Duration::from_secs(60); + let cold_expiry = Duration::from_secs(7 * 24 * 60 * 60); + let index = RejectedEventsIndex::new(hot_expiry, cold_expiry); let keys = Keys::generate(); let event = create_test_event(&keys, "test").await; @@ -785,11 +785,20 @@ async fn test_rejected_cache_entries_expired_during_downtime() { // Save to disk index.save_to_disk(&state_path).unwrap(); - // Simulate downtime longer than hot cache expiry - tokio::time::sleep(Duration::from_millis(75)).await; + // Simulate two minutes of downtime in the persisted snapshot instead + // of relying on a narrow wall-clock interval between two expirations. + let mut snapshot: serde_json::Value = + serde_json::from_slice(&std::fs::read(&state_path).unwrap()).unwrap(); + snapshot["saved_at"] = serde_json::to_value( + std::time::SystemTime::now() + .checked_sub(Duration::from_secs(120)) + .expect("simulated downtime must fit in SystemTime"), + ) + .unwrap(); + std::fs::write(&state_path, serde_json::to_vec(&snapshot).unwrap()).unwrap(); // Restore - let index2 = RejectedEventsIndex::new(Duration::from_millis(50), Duration::from_millis(100)); + let index2 = RejectedEventsIndex::new(hot_expiry, cold_expiry); index2.restore_from_disk(&state_path).unwrap(); // Both should be restored initially