test(cron): crash-safety tests claim the fire before asserting not-due

advance_next_run alone no longer commits an occurrence — a restart before the
fire claim restores it (#107485). The at-most-once assertions model the real
tick sequence (advance → claim) so they keep guarding the mid-run crash case.
This commit is contained in:
Teknium
2026-09-12 01:32:21 -07:00
parent 6cd4fbd640
commit 36b1a6e62c
2 changed files with 9 additions and 4 deletions

View File

@@ -85,13 +85,15 @@ def test_legacy_utc_offset_next_run_still_fires(temp_home, monkeypatch):
def test_legacy_offset_catchup_fires_at_most_once(temp_home, monkeypatch):
"""The catch-up run is a single fire: once the scheduler advances the
job, the legacy instant is gone and a second scan finds nothing due."""
from cron.jobs import advance_next_run, get_due_jobs, get_job
from cron.jobs import advance_next_run, claim_job_for_fire, get_due_jobs, get_job
monkeypatch.setattr("cron.jobs._hermes_now", lambda: _BRUSSELS_NOW)
jid = _write_cron_job(_DAILY_0400, _LEGACY_UTC_NEXT_RUN)
assert jid in [j["id"] for j in get_due_jobs()]
assert advance_next_run(jid) is True
# The fire claim is what commits the occurrence; without it a restart restores the slot.
assert claim_job_for_fire(jid)
# Re-anchored to tomorrow's occurrence, expressed in the configured zone.
assert get_job(jid)["next_run_at"] == "2026-09-03T04:00:00+02:00"

View File

@@ -860,12 +860,15 @@ class TestAdvanceNextRun:
due_before = get_due_jobs()
assert len(due_before) == 1
# Advance (simulating what tick() does before run_job)
# Advance + claim (what tick() does before run_job); the claim is the point after which
# side effects may exist, so a restart after it must not re-fire (#3396). A restart
# BEFORE the claim restores the occurrence instead (#107485, test_missed_window_catchup).
advance_next_run(job["id"])
assert claim_job_for_fire(job["id"])
# Now the job should NOT be due (simulates restart after crash)
# Now the job should NOT be due (simulates restart after a mid-run crash)
due_after = get_due_jobs()
assert len(due_after) == 0, "Job should not be due after advance_next_run"
assert len(due_after) == 0, "Job should not be due after advance + claim"
class TestGetDueJobs: