diff --git a/tests/cron/test_cron_timezone_migration_catchup.py b/tests/cron/test_cron_timezone_migration_catchup.py index ef4cf4a108..180b473fe0 100644 --- a/tests/cron/test_cron_timezone_migration_catchup.py +++ b/tests/cron/test_cron_timezone_migration_catchup.py @@ -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" diff --git a/tests/cron/test_jobs.py b/tests/cron/test_jobs.py index 4bfdde1e99..cd81b8a61b 100644 --- a/tests/cron/test_jobs.py +++ b/tests/cron/test_jobs.py @@ -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: