From fefb516bec9d9b9d1ed7d36512cf983b71f9b1dc Mon Sep 17 00:00:00 2001 From: core-devops Date: Sun, 21 Jun 2026 22:51:39 -0700 Subject: [PATCH] =?UTF-8?q?fix(test):=20de-flake=20schedules=20cron=20UPDA?= =?UTF-8?q?TE=20assertion=20=E2=80=94=20daily=20next-run=20wrap=20is=20wal?= =?UTF-8?q?l-clock-dependent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TestIntegration_Schedules_CRUDRunHistoryHealth_RoundTrip (Handlers Postgres Integration, BP-required) failed on main HEAD adc2e9ff at 04:46Z with: UPDATE cron: next_run_at should have moved (orig=2026-06-23 03:00:00 UTC new=2026-06-22 05:00:00 UTC) Root cause (named, not flaky-dismiss): the assertion `newNextRun.After(origNextRun)` assumed the recomputed 5am next-run is always strictly later than the 3am next-run. But "0 3 * * *" and "0 5 * * *" are daily crons whose next occurrence wraps every 24h. Whenever the test runs in the 03:00-05:00 UTC window the 3am schedule has already rolled to *tomorrow* while the 5am schedule is still *today*, so the 5am next-run is EARLIER than the 3am next-run and the ordering inverts. Verified deterministically against scheduler.ComputeNextRun: the old assertion fails at exactly UTC hours 3 and 4 (the 04:46Z run window), and passes the other 22 hours. This is a time-of-day race, not infra/testcontainers and not migration-048. Fix: assert the wall-clock-independent invariants that always hold after a cron UPDATE — next_run_at (a) actually changed and (b) lands at exactly 05:00:00 UTC, proving the new cron was applied. Preserves the test's intent (UPDATE recomputes next_run_at from the new cron) without the daily-wrap ordering bug. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../handlers/schedules_integration_test.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/workspace-server/internal/handlers/schedules_integration_test.go b/workspace-server/internal/handlers/schedules_integration_test.go index aec94631..fa50dca8 100644 --- a/workspace-server/internal/handlers/schedules_integration_test.go +++ b/workspace-server/internal/handlers/schedules_integration_test.go @@ -207,8 +207,21 @@ func TestIntegration_Schedules_CRUDRunHistoryHealth_RoundTrip(t *testing.T) { `SELECT next_run_at FROM workspace_schedules WHERE id = $1`, created.ID).Scan(&newNextRun); err != nil { t.Fatalf("read new next_run_at: %v", err) } - if !newNextRun.After(origNextRun) { - t.Errorf("UPDATE cron: next_run_at should have moved (orig=%v new=%v)", origNextRun, newNextRun) + // The UPDATE must have RECOMPUTED next_run_at from the new cron. We must NOT + // assert newNextRun.After(origNextRun): "0 3" and "0 5" are daily crons whose + // next-occurrence wraps every 24h, so whenever this test runs in the + // 03:00–05:00 UTC window the 3am schedule has already rolled to *tomorrow* + // while the 5am schedule is still *today*, making the 5am next-run EARLIER + // than the 3am next-run and inverting any "strictly after" ordering. Instead + // assert the deterministic invariants that hold at every wall-clock time: + // (a) next_run_at actually changed, and + // (b) it now lands at exactly 05:00 UTC, proving the new cron was applied. + if newNextRun.Equal(origNextRun) { + t.Errorf("UPDATE cron: next_run_at should have been recomputed (orig=%v new=%v)", origNextRun, newNextRun) + } + nrUTC := newNextRun.UTC() + if nrUTC.Hour() != 5 || nrUTC.Minute() != 0 || nrUTC.Second() != 0 { + t.Errorf("UPDATE cron: next_run_at want 05:00:00 UTC (new cron \"0 5 * * *\"), got %v", nrUTC) } // --- Case 4: UPDATE with NEW timezone also recomputes next_run_at --- -- 2.52.0