From 0983f67143b14b75e894c59e7d75c467c697b915 Mon Sep 17 00:00:00 2001 From: CodeWhale Bot Date: Mon, 7 Sep 2026 01:26:56 -0700 Subject: [PATCH] fix(test): derive the prune cut from recorded timestamps, not a fixed 6s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `prune_older_than_keeps_the_newest_and_drops_only_the_old_tail` fails intermittently on windows-latest with assertion `left == right` failed: only the old tail should be removed left: 3 right: 2 The fixture builds two old snapshots, sleeps 8s, then two new ones 1.1s apart, and cuts at a hardcoded 6s. That assumes `repo.snapshot()` is fast: `new:0` is only ~1.2s plus one git subprocess older than prune time, so on a loaded Windows runner that subprocess alone carries it past the 6s line and it is pruned with the old pair. The existing fixture guard could not catch it — it asserts on `before[0]` and `before[2]`, and `before[1]` is the entry that drifts. The cut is now computed from the timestamps the repo actually recorded: aim at the midpoint of the gap between the oldest survivor and the newest victim, which leaves ~4s of slack in both directions instead of depending on wall-clock luck. The gap itself is asserted first, so a fixture that collapsed says so plainly rather than failing later as a count mismatch. Behaviour under test is unchanged: two removed, `new:1` and `new:0` survive. No production code is touched. cargo clippy -p codewhale-tui --lib -> 0 errors cargo test -p codewhale-tui --lib -- prune_older_than -> test result: ok. 3 passed; 0 failed Found when it failed the windows leg of #5987, a PR containing zero Rust files (TypeScript, CI config and .gitignore only), so it cannot have been caused there. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01D4rk4NXwyy6wmvii9Lp84P Signed-off-by: CodeWhale Bot --- crates/tui/src/snapshot/repo.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/tui/src/snapshot/repo.rs b/crates/tui/src/snapshot/repo.rs index 70bb14c014..287a333f74 100644 --- a/crates/tui/src/snapshot/repo.rs +++ b/crates/tui/src/snapshot/repo.rs @@ -1411,22 +1411,32 @@ mod tests { } let before = repo.list(usize::MAX).unwrap(); assert_eq!(before.len(), 4); - // Guard the fixture itself: if load skewed the timestamps so the cut - // would not fall between the pairs, say so instead of failing later - // with a confusing count mismatch. + // Derive the cut from the timestamps actually recorded rather than a + // fixed 6s. A fixed cut assumes `repo.snapshot()` is fast: `new:0` is + // only ~1.2s plus one git subprocess older than prune time, so on a + // loaded Windows runner that subprocess alone pushed it past 6s and + // three snapshots were pruned instead of two. (The old fixture guard + // could not catch it either — it checked `before[0]` and `before[2]`, + // and `before[1]` is the entry that drifts.) let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs() as i64; + // Newest-first: [new:1, new:0, old:1, old:0]. The cut must land + // strictly between the pairs, so aim at the midpoint of the 8s gap — + // that leaves ~4s of slack against clock drift and a slow runner in + // both directions. + let survivor = before[1].timestamp; + let victim = before[2].timestamp; assert!( - now - before[0].timestamp < 6 && now - before[2].timestamp > 6, - "fixture ages unusable for a 6s cut (newest {}s, oldest-surviving-pair {}s)", - now - before[0].timestamp, - now - before[2].timestamp + survivor - victim >= 2, + "fixture needs a real gap between the pairs (survivor {survivor}, victim {victim})" ); + let midpoint = victim + (survivor - victim) / 2; + let max_age = Duration::from_secs((now - midpoint).max(0) as u64); - // Cut 6s back: the two old snapshots drop, the two new ones survive. - let removed = repo.prune_older_than(Duration::from_secs(6)).unwrap(); + // The two old snapshots drop, the two new ones survive. + let removed = repo.prune_older_than(max_age).unwrap(); assert_eq!(removed, 2, "only the old tail should be removed"); let remaining = repo.list(usize::MAX).unwrap();