test(sec): cover the ceiling's promote/queue/alert path end to end

The ceiling tests asserted validate()'s verdict but nothing proved the claim
the design rests on: that a forced promotion actually queues the filings it
released and says that it did. Drive it through run_import with a filing that
stays inside the per-filing window, so only the aggregate ceiling can release
it, and assert the SecFilingGap row and the promotion_ceiling_forced event.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 16:58:46 +02:00
co-authored by Claude Opus 5
parent 77570557db
commit 486fb500d1
@@ -1204,3 +1204,82 @@ async def test_ceiling_never_fires_before_a_first_promotion(engine):
assert result.ok is False
assert result.summary["promotion_ceiling_tripped"] is None
async def test_ceiling_promotes_queues_and_alerts_end_to_end(engine, monkeypatch):
"""The self-heal claim, end to end: a filing that would block forever gets
promoted through, queued for retry, and announced."""
from app.models.data_import_run import DataImportRun
from app.services.sec_facts_parser import ParseResult
from sqlalchemy import update as sa_update
factory = _factory(engine)
await _seed(factory, ["AAPL"])
backfill = FakeSecClient(
tickers={"AAPL": 320193},
companyfacts={320193: _companyfacts([CF_K, CF_Q1], [SH_K, SH_Q1])},
submissions={320193: _submissions(SUB_FILINGS)},
latest_index=date(2026, 1, 31),
)
assert (await run_import(_importer(backfill), engine=engine)).status == STATUS_PROMOTED
# Age the only promotion past the ceiling: this is the wedge the ceiling exists
# for — the filing below stays young, so nothing else would ever release it.
async with factory() as db:
await db.execute(
sa_update(DataImportRun)
.where(DataImportRun.source == "sec_facts")
.values(started_at=datetime(2026, 4, 20, tzinfo=timezone.utc))
)
await db.commit()
# Present in Company Facts but unparseable — one missing_xbrl entry, not the
# two an absent-from-facts accession would also raise.
stuck_fact = _rev("2025-09-28", "2026-03-28", 254940, 2026, "Q2", "STUCK")
stuck_share = _shares("2026-04-17", 14687, "STUCK", 2026, "Q2")
client = FakeSecClient(
tickers={"AAPL": 320193},
companyfacts={
320193: _companyfacts(
[CF_K, CF_Q1, stuck_fact], [SH_K, SH_Q1, stuck_share]
)
},
submissions={320193: _submissions(SUB_FILINGS + [
_filing("STUCK", "10-Q", "2026-03-28", "2026-05-01",
"2026-05-01T10:01:00.000Z"),
])},
latest_index=date(2026, 5, 2),
daily={date(2026, 5, 1): [
{"form": "10-Q", "cik": 320193, "accession": "STUCK"},
]},
)
monkeypatch.setattr(
"app.services.sec_facts_parser.parse_snapshots",
lambda *a, **k: ParseResult(
skipped_filings=[{"accession": "STUCK", "reason": "unparseable"}]
),
)
# index_date 2026-05-01 vs today 2026-05-03 => 2 days old, still inside the
# per-filing window, so only the aggregate ceiling can let this through.
run = await run_import(_importer(client, today=date(2026, 5, 3)), engine=engine)
assert run.status == STATUS_PROMOTED
summary = json.loads(run.validation_json)
assert summary["promotion_ceiling_tripped"] == {"forced": 1, "unresolved": 1}
async with factory() as db:
gap = (await db.execute(select(SecFilingGap))).scalar_one()
events = (
await db.execute(
select(SystemEvent).where(
SystemEvent.code == "promotion_ceiling_forced"
)
)
).scalars().all()
# Queued, so later runs retry it without it ever blocking again...
assert gap.accession == "STUCK"
# ...and the safety valve firing is visible, not silent.
assert len(events) == 1
assert events[0].severity == "warning"
assert "7 days" in events[0].message