feat(worker): ручной пересчёт метрик через очередь, диагностика шагов и heartbeat
POST /metrics/refresh ставит задачу в sync_job (source=METRICS_JOB) и отвечает 202, пересчёт делает worker; GET /metrics/status отдаёт refreshing и consistent. metric_refresh_log хранит failed_step и step_timings. Источники с needs="tinvest_token" не попадают в расписание без токена, tinvest/moex добавлены в default_schedule. Воркер трогает heartbeat-файл для healthcheck.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user