feat(deploy): schedule fundamentals shadow imports

This commit is contained in:
2026-07-23 12:41:27 +02:00
parent 71d21a45b6
commit 88fc90cc6f
10 changed files with 493 additions and 19 deletions
+22
View File
@@ -80,6 +80,28 @@ class TestTradingDayCrons:
)
assert fire.strftime("%a") == "Mon"
@pytest.mark.parametrize(
("key", "hour", "minute"),
(
("schedule_dolt_earnings_cron", 2, 30),
("schedule_sec_fundamentals_cron", 4, 0),
),
)
def test_shadow_imports_run_daily_at_expected_et_time(
self, key: str, hour: int, minute: int
):
from datetime import datetime
from apscheduler.triggers.cron import CronTrigger
trigger = CronTrigger.from_crontab(
SCHEDULE_DEFAULTS[key], timezone=SCHEDULE_DEFAULTS["schedule_timezone"]
)
fire = trigger.get_next_fire_time(
None, datetime(2026, 7, 19, tzinfo=trigger.timezone)
)
assert (fire.hour, fire.minute) == (hour, minute)
class TestScheduleConfig:
async def test_defaults_when_unset(self, session: AsyncSession):
+96
View File
@@ -1,5 +1,7 @@
"""Unit tests for app.scheduler module."""
from types import SimpleNamespace
import pytest
from app.scheduler import (
@@ -8,7 +10,9 @@ from app.scheduler import (
_parse_frequency,
_resume_tickers,
_last_successful,
_run_shadow_import,
configure_scheduler,
get_job_runtime_snapshot,
queue_backtest_options,
queue_backtest_target_model,
scheduler,
@@ -106,6 +110,8 @@ class TestConfigureScheduler:
"benchmark_collector",
"sentiment_collector",
"fundamental_collector",
"dolt_earnings_import",
"sec_fundamentals_import",
"rr_scanner",
"shadow_book",
"ticker_universe_sync",
@@ -137,6 +143,8 @@ class TestConfigureScheduler:
"data_collector",
"data_backfill",
"fundamental_collector",
"dolt_earnings_import",
"sec_fundamentals_import",
"market_regime",
"near_close_pipeline",
"regime_monitor",
@@ -147,3 +155,91 @@ class TestConfigureScheduler:
"shadow_book",
"ticker_universe_sync",
])
class _SessionContext:
async def __aenter__(self):
return object()
async def __aexit__(self, *exc):
return None
class TestShadowImportJobs:
@staticmethod
def _session_factory():
return _SessionContext()
async def test_promoted_run_surfaces_completion(self, monkeypatch):
async def enabled(db, job_name):
return True
async def imported(importer):
return SimpleNamespace(
status="promoted", revision="abcdef1234567890", error_details=None
)
monkeypatch.setattr("app.scheduler.async_session_factory", self._session_factory)
monkeypatch.setattr("app.scheduler._is_job_enabled", enabled)
monkeypatch.setattr("app.scheduler.run_import", imported)
await _run_shadow_import("dolt_earnings_import", object())
runtime = get_job_runtime_snapshot("dolt_earnings_import")
assert runtime["status"] == "completed"
assert runtime["processed"] == 1
assert runtime["message"] == "promoted · abcdef123456"
async def test_failed_run_surfaces_error(self, monkeypatch):
async def enabled(db, job_name):
return True
async def imported(importer):
return SimpleNamespace(
status="failed", revision=None, error_details="validation failed"
)
monkeypatch.setattr("app.scheduler.async_session_factory", self._session_factory)
monkeypatch.setattr("app.scheduler._is_job_enabled", enabled)
monkeypatch.setattr("app.scheduler.run_import", imported)
await _run_shadow_import("sec_fundamentals_import", object())
runtime = get_job_runtime_snapshot("sec_fundamentals_import")
assert runtime["status"] == "error"
assert runtime["processed"] == 0
assert runtime["message"] == "validation failed"
async def test_source_lock_surfaces_skipped(self, monkeypatch):
async def enabled(db, job_name):
return True
async def imported(importer):
return None
monkeypatch.setattr("app.scheduler.async_session_factory", self._session_factory)
monkeypatch.setattr("app.scheduler._is_job_enabled", enabled)
monkeypatch.setattr("app.scheduler.run_import", imported)
await _run_shadow_import("dolt_earnings_import", object())
runtime = get_job_runtime_snapshot("dolt_earnings_import")
assert runtime["status"] == "skipped"
assert "already running" in runtime["message"]
async def test_disabled_job_never_runs_importer(self, monkeypatch):
async def disabled(db, job_name):
return False
async def should_not_run(importer):
raise AssertionError("disabled job ran importer")
monkeypatch.setattr("app.scheduler.async_session_factory", self._session_factory)
monkeypatch.setattr("app.scheduler._is_job_enabled", disabled)
monkeypatch.setattr("app.scheduler.run_import", should_not_run)
await _run_shadow_import("sec_fundamentals_import", object())
runtime = get_job_runtime_snapshot("sec_fundamentals_import")
assert runtime["status"] == "skipped"
assert runtime["message"] == "Disabled"