diff --git a/TODO.md b/TODO.md index 11b5a2d..ccf423f 100644 --- a/TODO.md +++ b/TODO.md @@ -31,6 +31,14 @@ P2: security: referer blacklist # Completed Steps +- 2026-10-04 `TestPeriodicReconciliationAdoptsFileThatAppearsAfterStartup` + only passes through a periodic pass (closes #189): it slept for three + eviction intervals before writing its file, and a startup pass still running + then could adopt the file itself. It now holds the test database's only + connection until the startup pass waits for it after walking the empty + variant directory, writes the file and lets the connection go, as + `TestEvictionRunsOnPeriodicSchedule` does, so only a periodic reconciliation + pass can adopt the file. Test only. - 2026-10-04 `TODO.md` merges with git's union merge (closes #190): a root `.gitattributes`, copied from `sneak/prompts`, marks it `merge=union`, so two branches that each add an entry at the top of Completed Steps merge without a diff --git a/internal/imgcache/eviction_internal_test.go b/internal/imgcache/eviction_internal_test.go index 4b0bbf7..af91ac5 100644 --- a/internal/imgcache/eviction_internal_test.go +++ b/internal/imgcache/eviction_internal_test.go @@ -847,15 +847,28 @@ func TestPeriodicReconciliationAdoptsFileThatAppearsAfterStartup(t *testing.T) { cache, _ := newEvictionTestCache(t, 1<<30) - const interval = 100 * time.Millisecond + // Hold the test database's only connection, so the startup pass + // waits for it after walking the still empty variant directory: the + // file written while it waits is first seen by a periodic pass. + conn, err := cache.db.Conn(t.Context()) + if err != nil { + t.Fatalf("failed to take the database connection: %v", err) + } - cache.StartEviction(interval) + defer func() { _ = conn.Close() }() + + cache.StartEviction(100 * time.Millisecond) defer func() { _ = cache.StopEviction(t.Context()) }() - // Let startup reconciliation run and settle on an empty cache - // before introducing the untracked file, so the adoption we assert - // below can only be the work of a later, periodic pass. - time.Sleep(3 * interval) + deadline := time.Now().Add(5 * time.Second) + + for cache.db.Stats().WaitCount == 0 { + if time.Now().After(deadline) { + t.Fatal("the startup pass never waited for the database") + } + + time.Sleep(10 * time.Millisecond) + } // Simulate a variant whose accounting insert failed after the // process was already running and serving requests: the content @@ -864,14 +877,16 @@ func TestPeriodicReconciliationAdoptsFileThatAppearsAfterStartup(t *testing.T) { // insert had failed and only the file write had succeeded. untracked := bytes.Repeat([]byte{0x41}, 900) - _, err := cache.variants.Store( + _, err = cache.variants.Store( "aabbccdd0099", bytes.NewReader(untracked), "image/webp", ) if err != nil { t.Fatalf("failed to store untracked variant file: %v", err) } - deadline := time.Now().Add(5 * time.Second) + _ = conn.Close() + + deadline = time.Now().Add(5 * time.Second) var usage int64