fix: gate setups on SEC filing completeness
This commit is contained in:
@@ -15,6 +15,7 @@ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_asyn
|
||||
from app.database import Base
|
||||
import app.models # noqa: F401
|
||||
from app.models.fundamental_snapshot import FundamentalSnapshot
|
||||
from app.models.sec_filing_gap import SecFilingGap
|
||||
from app.models.system_event import SystemEvent
|
||||
from app.models.ticker import Ticker
|
||||
from app.services.data_import import (
|
||||
@@ -374,7 +375,7 @@ async def test_recovers_facts_misfiled_under_coregistrant(engine):
|
||||
# Stamped to the issuer that filed, NOT the co-registrant whose file it came from.
|
||||
assert q2.cik == "0000320193"
|
||||
assert q2.revenue == 254940 and q2.shares_outstanding == 14687
|
||||
assert "coregistrant_recovery" in codes
|
||||
assert "coregistrant_recovery" not in codes
|
||||
|
||||
|
||||
async def test_coregistrant_recovery_rejects_discontinuous_share_count(engine):
|
||||
@@ -425,11 +426,59 @@ async def test_unresolved_filing_stops_blocking_after_retry_window(engine):
|
||||
assert run.source_max_date == date(2026, 5, 2) # and the index advances
|
||||
summary = json.loads(run.validation_json or "{}")
|
||||
assert summary["missing_xbrl_count"] == 1 and summary["missing_xbrl_blocking"] == 0
|
||||
assert await _count(factory, SecFilingGap) == 1
|
||||
|
||||
async with factory() as s:
|
||||
events = (await s.execute(select(SystemEvent))).scalars().all()
|
||||
unresolved = [e for e in events if e.code == "unresolved_filing"]
|
||||
assert len(unresolved) == 1 and "GHOST" in unresolved[0].message
|
||||
assert "automatic SEC retry" in unresolved[0].message
|
||||
|
||||
# The next scheduled run retries even though the SEC daily-index revision
|
||||
# has not changed. Company Facts is a separate SEC product and may catch up
|
||||
# independently, so the generic revision no-op must not suppress this work.
|
||||
still_missing_run = await run_import(
|
||||
_importer(incr, today=date(2026, 5, 11)),
|
||||
engine=engine,
|
||||
)
|
||||
assert still_missing_run.status == STATUS_PROMOTED
|
||||
assert still_missing_run.revision is None
|
||||
assert await _count(factory, SecFilingGap) == 1
|
||||
|
||||
# A later normal scheduled import retries only the queued issuer. Once SEC
|
||||
# publishes the accession in Company Facts, it is inserted and unblocked
|
||||
# without a full-universe reparse or operator action.
|
||||
cf_ghost = _rev("2025-09-28", "2026-03-28", 254940, 2026, "Q2", "GHOST")
|
||||
sh_ghost = _shares("2026-04-17", 14687, "GHOST", 2026, "Q2")
|
||||
healed = FakeSecClient(
|
||||
tickers={"AAPL": 320193},
|
||||
companyfacts={
|
||||
320193: _companyfacts(
|
||||
[CF_K, CF_Q1, cf_ghost],
|
||||
[SH_K, SH_Q1, sh_ghost],
|
||||
)
|
||||
},
|
||||
submissions={
|
||||
320193: _submissions(SUB_FILINGS + [
|
||||
_filing(
|
||||
"GHOST",
|
||||
"10-Q",
|
||||
"2026-03-28",
|
||||
"2026-05-01",
|
||||
"2026-05-01T10:01:00.000Z",
|
||||
)
|
||||
])
|
||||
},
|
||||
latest_index=date(2026, 5, 2),
|
||||
)
|
||||
healed_run = await run_import(
|
||||
_importer(healed, today=date(2026, 5, 12)),
|
||||
engine=engine,
|
||||
)
|
||||
|
||||
assert healed_run.status == STATUS_PROMOTED
|
||||
assert await _count(factory, FundamentalSnapshot) == 3
|
||||
assert await _count(factory, SecFilingGap) == 0
|
||||
|
||||
|
||||
async def test_non_xbrl_amendment_skipped_not_failed(engine):
|
||||
|
||||
Reference in New Issue
Block a user