Seed the boot-recovery test's work before the failing boot
TestTransientControlFailureAtBootRecovers inserted its queue row straight into the database after the app had booted. When the retry loop's first attempt ran before that INSERT it found nothing due, succeeded, and nothing woke it again (a raw INSERT skips the request middleware that does), so the timer stayed unarmed and the test failed "timer was not armed" about one run in ten, on main as well. It turned this branch's CI red. The row is now written through a first boot of the same database, which is then closed; the second boot meets the control failure with the work already there, as a restarting instance does. 40 runs pass, and dropping sync's retry request on a boot failure still fails it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+15
−5
internal/calendartest/schedule_recovery_test.go+15 −5
CI passed — run details
CI log
=== vet === go vet ./... go vet -tags browser ./internal/calendartest/ === fmt === === test === go test ./... ? amadan.net/oficina/calendar/cmd/calendar [no test files] ok amadan.net/oficina/calendar/internal/calendar (cached) ? amadan.net/oficina/calendar/internal/calendar/icons [no test files] ? amadan.net/oficina/calendar/internal/calendar/seal [no test files] ok amadan.net/oficina/calendar/internal/calendartest 2.494s ok amadan.net/oficina/calendar/internal/guardstest 0.044s ? amadan.net/oficina/calendar/scripts/writeestilo [no test files] === migration-check === go tool rastrillo migration check rastrillo migration check: models and migrations agree
diff --git a/internal/calendartest/schedule_recovery_test.go b/internal/calendartest/schedule_recovery_test.go| index 4c2699d..1e1f420 100644 |
| --- a/internal/calendartest/schedule_recovery_test.go |
| +++ b/internal/calendartest/schedule_recovery_test.go |
| @@ -9,16 +9,26 @@ import ( |
| calendar "amadan.net/oficina/calendar/internal/calendar" |
| ) |
| +// The work is in the database before the failing boot, as it is when an |
| +// instance restarts. It used to be inserted straight into the database |
| +// after boot, which raced the retry loop: when the first retry ran before |
| +// the INSERT it found nothing due, succeeded, and nothing woke it again |
| +// (a raw INSERT bypasses the request middleware that wakes it), so about |
| +// one run in ten failed "timer was not armed". |
| func TestTransientControlFailureAtBootRecovers(t *testing.T) { |
| control := controlForTest(t) |
| - control.mu.Lock() |
| - control.fail = true |
| - control.mu.Unlock() |
| - app := calendar.NewForTest(t, t.TempDir()) |
| + dir := t.TempDir() |
| + seed := calendar.NewForTest(t, dir) |
| due := time.Now().Add(time.Hour).Unix() |
| - if _, err := app.DBForTest().Exec(`INSERT INTO reminder_queues (reminder_id,occurrence_key,due_at,attempts,late_by_sec) VALUES (1,'boot-retry',?,0,0)`, due); err != nil { |
| + if _, err := seed.DBForTest().Exec(`INSERT INTO reminder_queues (reminder_id,occurrence_key,due_at,attempts,late_by_sec) VALUES (1,'boot-retry',?,0,0)`, due); err != nil { |
| t.Fatal(err) |
| } |
| + seed.Close() |
| + control.mu.Lock() |
| + control.at = time.Time{} |
| + control.fail = true |
| + control.mu.Unlock() |
| + calendar.NewForTest(t, dir) |
| control.mu.Lock() |
| control.fail = false |
| control.mu.Unlock() |