"""The timetable only names sources that exist, and leaves off the ones that cannot run.""" from __future__ import annotations from fintracker.config import Settings from fintracker.sources import registry from fintracker.worker.jobs import default_schedule, scheduled def test_every_scheduled_source_is_registered_once(): names = [s.source for s in default_schedule()] assert len(names) == len(set(names)) assert set(names) <= set(registry.names()) def test_every_live_source_is_on_the_timetable(): assert {s.source for s in default_schedule()} == set(registry.names()) def test_sources_needing_a_token_are_left_off_without_one(): ready, skipped = scheduled(Settings.model_construct(tinvest_token=None)) assert {s.source for s in skipped} == {"tinvest", "tinvest_events"} assert not {s.source for s in ready} & {"tinvest", "tinvest_events"} ready, skipped = scheduled(Settings.model_construct(tinvest_token="t.x")) assert skipped == [] assert {"tinvest", "moex", "tinvest_events", "moex_payouts"} <= {s.source for s in ready} def test_needs_names_a_real_setting(): for spec in default_schedule(): if spec.needs is not None: assert spec.needs in Settings.model_fields def test_worker_timetable_has_the_heartbeat_and_the_queue_poller(): from fintracker.worker.scheduler import build_scheduler ids = {j.id for j in build_scheduler().get_jobs()} assert {"heartbeat", "poll-sync-jobs", "sync:zenmoney", "sync:moex", "sync:moex_payouts"} <= ids async def test_heartbeat_touches_its_file(tmp_path, monkeypatch): from fintracker.worker import scheduler beat = tmp_path / "beat" monkeypatch.setattr(scheduler, "HEARTBEAT_FILE", beat) await scheduler._heartbeat() assert beat.exists()