thermograph/backend/tests/notifications/test_scheduler.py
Emi Griffith a4be7066e5 Subtree-merge thermograph-backend (origin/main) into backend/
git-subtree-dir: backend
git-subtree-mainline: 6723fc0326
git-subtree-split: 83c2e05b96
2026-07-22 22:01:11 -07:00

79 lines
2.8 KiB
Python

"""Tests for the recurring worker-tier jobs (scheduler.py): job registration,
cadence from env, idempotent start/stop, and that each job calls the right
underlying function. No real hours-long wait — inspects APScheduler's own job
registry rather than letting anything actually fire."""
import datetime
import pytest
from notifications import scheduler
@pytest.fixture(autouse=True)
def _clean_scheduler():
"""Every test starts from "not running" and leaves nothing behind, even on
failure — scheduler._scheduler is module-global state shared across tests."""
scheduler.stop()
yield
scheduler.stop()
def test_start_registers_both_jobs_with_configured_intervals(monkeypatch):
monkeypatch.setattr(scheduler, "WARM_CITIES_INTERVAL_HOURS", 24)
monkeypatch.setattr(scheduler, "INDEXNOW_INTERVAL_HOURS", 6)
scheduler.start()
jobs = {j.id: j for j in scheduler._scheduler.get_jobs()}
assert set(jobs) == {"warm_cities", "indexnow"}
assert jobs["warm_cities"].trigger.interval == datetime.timedelta(hours=24)
assert jobs["indexnow"].trigger.interval == datetime.timedelta(hours=6)
assert jobs["warm_cities"].func is scheduler._warm_cities_job
assert jobs["indexnow"].func is scheduler._indexnow_job
def test_start_does_not_run_a_job_immediately():
"""warm_cities already runs at deploy time; an immediate duplicate run on
every worker boot/restart would just be wasted work — the first scheduled
run must be one interval away, not "now"."""
scheduler.start()
now = datetime.datetime.now(datetime.timezone.utc)
for job in scheduler._scheduler.get_jobs():
assert job.next_run_time > now
def test_start_is_idempotent(monkeypatch):
scheduler.start()
first = scheduler._scheduler
calls = []
monkeypatch.setattr(scheduler.BackgroundScheduler, "__init__",
lambda self, **kw: calls.append(1) or None)
scheduler.start()
assert scheduler._scheduler is first
assert calls == [] # a second BackgroundScheduler was never constructed
def test_stop_shuts_down_and_clears_state():
scheduler.start()
assert scheduler._scheduler is not None
scheduler.stop()
assert scheduler._scheduler is None
def test_stop_without_start_is_a_safe_no_op():
scheduler.stop() # must not raise
assert scheduler._scheduler is None
def test_warm_cities_job_calls_warm_cities_main(monkeypatch):
import warm_cities
calls = []
monkeypatch.setattr(warm_cities, "main", lambda: calls.append(1))
scheduler._warm_cities_job()
assert calls == [1]
def test_indexnow_job_calls_submit_if_changed(monkeypatch):
import indexnow
calls = []
monkeypatch.setattr(indexnow, "submit_if_changed", lambda: calls.append(1))
scheduler._indexnow_job()
assert calls == [1]