mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
fix: reject malformed market snapshot containers
This commit is contained in:
@@ -29,7 +29,7 @@
|
|||||||
- red/yellow/green 状态变化输出 `market.<region>.status`。
|
- red/yellow/green 状态变化输出 `market.<region>.status`。
|
||||||
- change item 的 quality 取 current/previous 两份快照中较差的一侧;任一侧 partial 会降低整个 what_changed 块,任一侧 unavailable 不生成可靠变化项。
|
- change item 的 quality 取 current/previous 两份快照中较差的一侧;任一侧 partial 会降低整个 what_changed 块,任一侧 unavailable 不生成可靠变化项。
|
||||||
- 没有第二份有效快照时,返回 `previous_completed_snapshot_unavailable`;部分 region 缺基线时整个块标记为 `partial`,并追加 `previous_completed_snapshot_unavailable:<region>`,避免把“缺少基线”误解为“没有变化”。不会临时拉行情或生成一份“当前”快照冒充基线。
|
- 没有第二份有效快照时,返回 `previous_completed_snapshot_unavailable`;部分 region 缺基线时整个块标记为 `partial`,并追加 `previous_completed_snapshot_unavailable:<region>`,避免把“缺少基线”误解为“没有变化”。不会临时拉行情或生成一份“当前”快照冒充基线。
|
||||||
- 历史详情读取失败,或投影出的 `context_snapshot` 不是 JSON object(例如损坏的 JSON 字符串)时,不再把其余更旧记录提升为 current/latest;对应快照和变化对比保持不可用并返回 limitation。
|
- 历史详情读取失败、投影出的 `context_snapshot` 不是 JSON object,或其中已存在的 `market_light_snapshots` 不是 object(例如损坏的 JSON 字符串/数组)时,不再把其余更旧记录提升为 current/latest;对应快照和变化对比保持不可用并返回 limitation。
|
||||||
- 快照校验失败时仍保留其 `trade_date` 的目标位置:最新日期无有效快照时不回退到更旧 current,最近更早日期无有效快照时不越过它继续寻找更旧 previous。同一目标日期存在其他有效重跑快照时,仍可使用该日期的有效版本。
|
- 快照校验失败时仍保留其 `trade_date` 的目标位置:最新日期无有效快照时不回退到更旧 current,最近更早日期无有效快照时不越过它继续寻找更旧 previous。同一目标日期存在其他有效重跑快照时,仍可使用该日期的有效版本。
|
||||||
- 外层市场键必须与快照内部 `region` 一致;例如 `cn` 键下的 `region=us` 快照会按无效快照处理,不能进入 A 股的 current/previous 或变化项。
|
- 外层市场键必须与快照内部 `region` 一致;例如 `cn` 键下的 `region=us` 快照会按无效快照处理,不能进入 A 股的 current/previous 或变化项。
|
||||||
- 历史扫描被截断等 market limitations 会同步进入 what_changed;剩余快照不能在来源历史不完整时被标记为 fresh。
|
- 历史扫描被截断等 market limitations 会同步进入 what_changed;剩余快照不能在来源历史不完整时被标记为 fresh。
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ class DashboardOverviewService:
|
|||||||
if not isinstance(context_snapshot, dict):
|
if not isinstance(context_snapshot, dict):
|
||||||
detail_failure_count += 1
|
detail_failure_count += 1
|
||||||
continue
|
continue
|
||||||
|
snapshot_container = context_snapshot.get("market_light_snapshots")
|
||||||
|
if snapshot_container is not None and not isinstance(snapshot_container, dict):
|
||||||
|
detail_failure_count += 1
|
||||||
|
continue
|
||||||
raw_snapshots = self._extract_snapshots(context_snapshot)
|
raw_snapshots = self._extract_snapshots(context_snapshot)
|
||||||
for region, raw_snapshot in raw_snapshots.items():
|
for region, raw_snapshot in raw_snapshots.items():
|
||||||
raw_trade_date = str(raw_snapshot.get("trade_date") or "").strip()
|
raw_trade_date = str(raw_snapshot.get("trade_date") or "").strip()
|
||||||
|
|||||||
@@ -296,6 +296,26 @@ def test_malformed_projected_context_does_not_promote_older_snapshots() -> None:
|
|||||||
assert "latest_completed_snapshot_unavailable" in payload["market"]["meta"]["limitations"]
|
assert "latest_completed_snapshot_unavailable" in payload["market"]["meta"]["limitations"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_malformed_nested_snapshot_container_does_not_promote_older_snapshots() -> None:
|
||||||
|
dependencies = _dependencies()
|
||||||
|
original_detail = dependencies["history_service"].get_history_detail_by_id.side_effect
|
||||||
|
|
||||||
|
def detail_with_malformed_container(record_id: int) -> dict:
|
||||||
|
if record_id == 1:
|
||||||
|
return {"context_snapshot": {"market_light_snapshots": "{invalid-json"}}
|
||||||
|
return original_detail(record_id)
|
||||||
|
|
||||||
|
dependencies["history_service"].get_history_detail_by_id.side_effect = detail_with_malformed_container
|
||||||
|
|
||||||
|
payload = DashboardOverviewService(**dependencies).get_overview()
|
||||||
|
|
||||||
|
assert payload["market"]["data"]["latest_snapshots"] == {}
|
||||||
|
assert payload["what_changed"]["data"]["current_trade_dates"] == {}
|
||||||
|
assert payload["what_changed"]["data"]["items"] == []
|
||||||
|
assert "market_review_detail_partial" in payload["market"]["meta"]["limitations"]
|
||||||
|
assert "latest_completed_snapshot_unavailable" in payload["market"]["meta"]["limitations"]
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_latest_snapshot_does_not_promote_an_older_trade_date() -> None:
|
def test_invalid_latest_snapshot_does_not_promote_an_older_trade_date() -> None:
|
||||||
dependencies = _dependencies()
|
dependencies = _dependencies()
|
||||||
reviews = [
|
reviews = [
|
||||||
|
|||||||
Reference in New Issue
Block a user