fix: surface the fundamentals cache result on every SEC job outcome
The runtime message only appended the cache summary when the import itself completed. On a deferred, failed or source-locked run Admin → Jobs showed just the import outcome, so an operator had no signal that `fundamental_data` had advanced — contradicting the claim now made in the docstring, the schedule hint and the deployment doc. The import status still varies and stays the headline; the cache summary is appended to all of them. Adds a source-locked regression test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+10
-11
@@ -905,21 +905,20 @@ async def run_sec_fundamentals_import() -> None:
|
||||
f"cache {summary['refreshed']} · "
|
||||
f"{summary['score_inputs_changed']} score inputs changed"
|
||||
)
|
||||
# Every outcome carries the cache summary — including deferred, failed and
|
||||
# source-locked ones. The import status is what varies; the refresh always
|
||||
# happened, and Admin → Jobs is the only place an operator sees that.
|
||||
runtime = get_job_runtime_snapshot(job_name)
|
||||
if not import_ran:
|
||||
_runtime_finish(
|
||||
job_name,
|
||||
"completed",
|
||||
processed=1,
|
||||
total=1,
|
||||
message=f"Import disabled · {cache_message}",
|
||||
)
|
||||
elif runtime.get("status") == "completed":
|
||||
if import_ran:
|
||||
status = str(runtime.get("status") or "completed")
|
||||
import_message = runtime.get("message") or "import completed"
|
||||
processed = 1 if status == "completed" else 0
|
||||
else:
|
||||
status, import_message, processed = "completed", "Import disabled", 1
|
||||
_runtime_finish(
|
||||
job_name,
|
||||
"completed",
|
||||
processed=1,
|
||||
status,
|
||||
processed=processed,
|
||||
total=1,
|
||||
message=f"{import_message} · {cache_message}",
|
||||
)
|
||||
|
||||
@@ -276,7 +276,7 @@ class TestShadowImportJobs:
|
||||
assert runtime["status"] == "skipped"
|
||||
assert runtime["message"] == "Disabled"
|
||||
|
||||
async def test_sec_failure_still_runs_activated_local_refresh(self, monkeypatch):
|
||||
async def test_sec_failure_still_runs_local_cache_refresh(self, monkeypatch):
|
||||
calls = []
|
||||
|
||||
async def enabled(db, job_name):
|
||||
@@ -307,7 +307,10 @@ class TestShadowImportJobs:
|
||||
assert len(calls) == 1
|
||||
runtime = get_job_runtime_snapshot("sec_fundamentals_import")
|
||||
assert runtime["status"] == "error"
|
||||
assert runtime["message"] == "SEC unavailable"
|
||||
# the failure stays the headline, but the cache result is still visible
|
||||
assert runtime["message"] == (
|
||||
"SEC unavailable · cache 511 · 2 score inputs changed"
|
||||
)
|
||||
|
||||
async def test_sec_success_surfaces_cache_refresh_summary(self, monkeypatch):
|
||||
async def enabled(db, job_name):
|
||||
@@ -342,6 +345,42 @@ class TestShadowImportJobs:
|
||||
"no_op · abcdef123456 · cache 511 · 2 score inputs changed"
|
||||
)
|
||||
|
||||
async def test_source_locked_sec_run_still_reports_the_cache_refresh(
|
||||
self, monkeypatch
|
||||
):
|
||||
"""A skipped import keeps its skip status but shows the cache advanced."""
|
||||
|
||||
async def enabled(db, job_name):
|
||||
return True
|
||||
|
||||
async def locked(importer):
|
||||
return None # another import owns the source lock
|
||||
|
||||
async def refreshed(db):
|
||||
return {
|
||||
"refreshed": 511,
|
||||
"score_inputs_changed": 0,
|
||||
"dimension_scores_staled": 0,
|
||||
"composite_scores_staled": 0,
|
||||
}
|
||||
|
||||
monkeypatch.setattr("app.scheduler.async_session_factory", self._session_factory)
|
||||
monkeypatch.setattr("app.scheduler._is_job_enabled", enabled)
|
||||
monkeypatch.setattr("app.scheduler.run_import", locked)
|
||||
monkeypatch.setattr(
|
||||
"app.scheduler.fundamental_data_refresh_service.refresh",
|
||||
refreshed,
|
||||
)
|
||||
|
||||
await run_sec_fundamentals_import()
|
||||
|
||||
runtime = get_job_runtime_snapshot("sec_fundamentals_import")
|
||||
assert runtime["status"] == "skipped"
|
||||
assert runtime["message"] == (
|
||||
"Another import for this source is already running · "
|
||||
"cache 511 · 0 score inputs changed"
|
||||
)
|
||||
|
||||
async def test_disabled_sec_job_still_refreshes_local_cache(self, monkeypatch):
|
||||
"""Disabling the job stops the SEC fetch, not the local cache.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user