fix(sector-radar): 收紧可复现发布契约
This commit is contained in:
@@ -7,6 +7,7 @@ from zhixing_server.modules.sector_radar.application.build import (
|
||||
BuildSectorRadarCommand,
|
||||
)
|
||||
from zhixing_server.modules.sector_radar.domain.models import (
|
||||
MembershipStatus,
|
||||
PublicationStatus,
|
||||
RadarPublication,
|
||||
SectorType,
|
||||
@@ -19,6 +20,7 @@ from zhixing_server.modules.sector_radar.domain.source import (
|
||||
SectorIndexRow,
|
||||
SectorMemberRow,
|
||||
SourceResult,
|
||||
SourceSnapshot,
|
||||
StockBasicRow,
|
||||
SuspendRow,
|
||||
TradeCalendarRow,
|
||||
@@ -37,9 +39,11 @@ class FakeRadarSource:
|
||||
self,
|
||||
*,
|
||||
missing_moneyflow: bool = False,
|
||||
missing_membership: bool = False,
|
||||
net_scale: Decimal = Decimal(1),
|
||||
) -> None:
|
||||
self.missing_moneyflow = missing_moneyflow
|
||||
self.missing_membership = missing_membership
|
||||
self.net_scale = net_scale
|
||||
self.fail_daily = False
|
||||
self.calls: list[str] = []
|
||||
@@ -153,6 +157,38 @@ class FakeRadarSource:
|
||||
self, trade_date: date, sector_codes: Sequence[str]
|
||||
) -> SourceResult[SectorMemberRow]:
|
||||
self.calls.append("members")
|
||||
if self.missing_membership:
|
||||
snapshots: list[SourceSnapshot] = []
|
||||
member_rows: list[SectorMemberRow] = []
|
||||
for sector_code in sector_codes:
|
||||
sector_rows = (
|
||||
()
|
||||
if sector_code == sector_codes[-1]
|
||||
else tuple(
|
||||
SectorMemberRow(
|
||||
trade_date,
|
||||
sector_code,
|
||||
f"00000{index}.SZ",
|
||||
f"股票{index}",
|
||||
)
|
||||
for index in range(1, 6)
|
||||
)
|
||||
)
|
||||
snapshots.append(
|
||||
build_source_snapshot(
|
||||
api_name="dc_member",
|
||||
params={
|
||||
"trade_date": trade_date.isoformat(),
|
||||
"ts_code": sector_code,
|
||||
},
|
||||
rows=tuple(self._raw_row(row) for row in sector_rows),
|
||||
target_trade_date=trade_date,
|
||||
partition_key=sector_code,
|
||||
observed_at=NOW,
|
||||
)
|
||||
)
|
||||
member_rows.extend(sector_rows)
|
||||
return SourceResult(tuple(snapshots), tuple(member_rows))
|
||||
rows = tuple(
|
||||
SectorMemberRow(trade_date, sector_code, f"00000{index}.SZ", f"股票{index}")
|
||||
for sector_code in sector_codes
|
||||
@@ -288,6 +324,33 @@ def test_partial_coverage_and_lock_have_distinct_exit_codes() -> None:
|
||||
assert locked.outcomes[0].status == "locked"
|
||||
|
||||
|
||||
def test_unknown_membership_is_persisted_as_partial_and_retried_independently() -> None:
|
||||
repository = InMemorySectorRadarRepository()
|
||||
source = FakeRadarSource(missing_membership=True)
|
||||
use_case = BuildSectorRadar(source, repository, today=TARGET_DATE, now_fn=lambda: NOW)
|
||||
|
||||
partial = use_case.execute(BuildSectorRadarCommand(trade_date=TARGET_DATE))
|
||||
partial_id = partial.outcomes[0].publication_id
|
||||
assert partial_id is not None
|
||||
publication = repository.get_publication(partial_id)
|
||||
assert partial.status == "partial"
|
||||
assert publication is not None
|
||||
assert publication.error_summary == "membership_unknown"
|
||||
assert any(item.status is MembershipStatus.UNKNOWN for item in repository.memberships.values())
|
||||
assert {
|
||||
record.source_group
|
||||
for record in repository.load_publication_sources(partial_id)
|
||||
if record.refresh_on_retry
|
||||
} == {PublicationSourceGroup.MEMBERS}
|
||||
|
||||
source.missing_membership = False
|
||||
source.calls.clear()
|
||||
retried = use_case.execute(BuildSectorRadarCommand(retry_publication_id=partial_id))
|
||||
|
||||
assert retried.status == "success"
|
||||
assert source.calls == ["members"]
|
||||
|
||||
|
||||
def test_range_builds_dates_in_order_and_retry_uses_old_target() -> None:
|
||||
repository = InMemorySectorRadarRepository()
|
||||
source = FakeRadarSource(missing_moneyflow=True)
|
||||
|
||||
Reference in New Issue
Block a user