From 1940cb548f7d7cfea85eec652346e5b67edde92f Mon Sep 17 00:00:00 2001 From: Logan Date: Mon, 17 Aug 2026 19:55:22 -0400 Subject: [PATCH] fix(test): stop asserting a cache hit against a one-second deadline TestCacheTTLExpiry set a 1s TTL and immediately asserted a hit, so it depended on an upper bound of elapsed wall-clock time between Set and Get. Nothing can promise that: on the capacity-1 runner, with the rest of the suite running in parallel, the goroutine can be descheduled for longer than the TTL and the entry is then correctly gone. It failed that way on this PR while passing five times out of five locally, and it touches no code this branch changed. Two entries now: one with an hour to live carries the presence assertions, one with a second carries the expiry. Sleeping past a TTL is always safe, so only the direction that cannot flake is timed. --- backend/explore/cache_test.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/backend/explore/cache_test.go b/backend/explore/cache_test.go index 8feb4d2..a1bc0a4 100644 --- a/backend/explore/cache_test.go +++ b/backend/explore/cache_test.go @@ -46,23 +46,42 @@ func TestCacheMiss(t *testing.T) { } } +// TestCacheTTLExpiry checks both halves of the TTL contract, and uses two +// entries to do it. +// +// **No assertion here may depend on an upper bound of elapsed wall-clock +// time**, which is what the single-entry version of this test did: it set +// a 1s TTL and immediately asserted a *hit*, so on a loaded runner — one +// goroutine descheduled for over a second while the rest of the suite +// runs — the entry was correctly gone and the test failed with "expected +// cache hit immediately after set". It did exactly that in CI while +// passing five times out of five locally. +// +// Sleeping *past* a TTL is always safe, so the expiry half keeps a short +// one; the presence half gets a TTL nothing can outrun. func TestCacheTTLExpiry(t *testing.T) { c := newTestCache(t) data := []byte(`{"ephemeral":true}`) - c.Set("ttl-test-key", data, 1*time.Second, "", "") + c.Set("ttl-live-key", data, time.Hour, "", "") + c.Set("ttl-expiring-key", data, 1*time.Second, "", "") - // Verify it's there immediately. - if _, ok := c.Get("ttl-test-key"); !ok { - t.Fatal("expected cache hit immediately after set") + if _, ok := c.Get("ttl-live-key"); !ok { + t.Fatal("expected a cache hit on an entry with an hour to live") } - // Wait for expiry. + // Wait for the short one to expire. time.Sleep(2 * time.Second) - if _, ok := c.Get("ttl-test-key"); ok { + if _, ok := c.Get("ttl-expiring-key"); ok { t.Error("expected cache miss after TTL expiry, got hit") } + + // And the long-lived entry is still there, which is what says the + // sweep above expired an entry rather than the cache. + if _, ok := c.Get("ttl-live-key"); !ok { + t.Error("the hour-long entry expired too") + } } func TestCacheMBID(t *testing.T) {