188 lines
7.5 KiB
Python
188 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
|
"""check_snapshot.py 的无网络离线测试。"""
|
|
|
|
import pathlib
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
|
from check_snapshot import ( # noqa: E402
|
|
STATUS_BLOCKED_AUTHORIZATION,
|
|
STATUS_INVALID_ARM_DIFF,
|
|
STATUS_READY,
|
|
STATUS_SCHEMA_INVALID,
|
|
STATUS_TARGET_SOURCE_FORBIDDEN,
|
|
check_arm_manifests,
|
|
check_authorization,
|
|
check_candidate_output,
|
|
check_replay,
|
|
check_target_sources,
|
|
)
|
|
|
|
|
|
AUTH = {
|
|
"sourceStatus": "active",
|
|
"copyrightStatus": "licensed",
|
|
"sourceVersion": "work-v1",
|
|
"allowedPurpose": ["offline_evaluation"],
|
|
"authorizationSnapshot": {
|
|
"id": "auth-1",
|
|
"version": "v1",
|
|
"immutable": True,
|
|
"sourceVersion": "work-v1",
|
|
"sourceStatus": "active",
|
|
"allowedPurpose": ["offline_evaluation"],
|
|
"checkedAt": "2026-07-19T00:00:00Z",
|
|
"revalidationAt": "2026-07-20T00:00:00Z",
|
|
},
|
|
}
|
|
|
|
|
|
def arm(common, cards):
|
|
return {**common, "cardInjection": cards}
|
|
|
|
|
|
class CheckSnapshotTest(unittest.TestCase):
|
|
def test_authorization_is_fail_closed(self):
|
|
self.assertEqual(check_authorization(None)["status"], STATUS_BLOCKED_AUTHORIZATION)
|
|
self.assertTrue(check_authorization(AUTH)["ok"])
|
|
self.assertEqual(check_authorization({"sourceStatus": "active"})["status"], STATUS_BLOCKED_AUTHORIZATION)
|
|
denied = {**AUTH, "allowedPurpose": ["read"]}
|
|
self.assertEqual(check_authorization(denied)["status"], STATUS_BLOCKED_AUTHORIZATION)
|
|
unknown_status = {**AUTH, "sourceStatus": "temporary"}
|
|
self.assertEqual(check_authorization(unknown_status)["status"], STATUS_BLOCKED_AUTHORIZATION)
|
|
unlicensed = {**AUTH, "copyrightStatus": "unlicensed"}
|
|
self.assertEqual(check_authorization(unlicensed)["status"], STATUS_BLOCKED_AUTHORIZATION)
|
|
|
|
def test_target_source_is_blocked(self):
|
|
allowed = check_target_sources(
|
|
489,
|
|
[
|
|
{"sourceId": "chapter-488", "sourceVersion": "v1", "chapter": 488},
|
|
{"sourceId": "outline-450-482", "sourceVersion": "v1", "from_order": 450, "to_order": 482},
|
|
],
|
|
)
|
|
self.assertEqual(allowed["status"], STATUS_READY)
|
|
blocked = check_target_sources(489, [{"sourceId": "chapter-489", "sourceVersion": "v1", "chapter": 489}])
|
|
self.assertEqual(blocked["status"], STATUS_TARGET_SOURCE_FORBIDDEN)
|
|
|
|
def test_arm_common_input_must_match(self):
|
|
common = {"snapshotVersion": "v0", "asOfChapter": 488, "l0": {"target": 489}}
|
|
manifests = {
|
|
"outline_only": arm(common, []),
|
|
"outline_plus_cards": arm(common, [{"id": 1}]),
|
|
"outline_plus_placebo_cards": arm(common, [{"id": 2}]),
|
|
}
|
|
self.assertTrue(check_arm_manifests(manifests)["ok"])
|
|
extra = {**manifests, "unregistered_arm": arm(common, [])}
|
|
self.assertEqual(check_arm_manifests(extra)["status"], STATUS_INVALID_ARM_DIFF)
|
|
changed = dict(manifests)
|
|
changed["outline_plus_cards"] = arm({**common, "l0": {"target": 490}}, [{"id": 1}])
|
|
self.assertEqual(check_arm_manifests(changed)["status"], STATUS_INVALID_ARM_DIFF)
|
|
|
|
def test_two_arm_smoke_can_be_explicit(self):
|
|
common = {"snapshotVersion": "v0", "asOfChapter": 488}
|
|
manifests = {"outline_only": arm(common, []), "outline_plus_cards": arm(common, [{"id": 1}])}
|
|
self.assertTrue(check_arm_manifests(manifests, ["outline_only", "outline_plus_cards"], smoke=True)["ok"])
|
|
|
|
def test_candidate_contract_is_structural(self):
|
|
candidate = {
|
|
"targetChapter": 489,
|
|
"chapterGoal": "突破",
|
|
"keyEvents": [],
|
|
"entities": [],
|
|
"foreshadowing": [],
|
|
"stateChanges": [],
|
|
"hook": "悬念",
|
|
"unknowns": [],
|
|
"assumptions": [],
|
|
}
|
|
self.assertTrue(check_candidate_output(candidate, 489)["ok"])
|
|
bad = {**candidate, "正文全文": "原文"}
|
|
self.assertEqual(check_candidate_output(bad, 489)["status"], STATUS_SCHEMA_INVALID)
|
|
bad_type = {**candidate, "keyEvents": "事件"}
|
|
self.assertEqual(check_candidate_output(bad_type, 489)["status"], STATUS_SCHEMA_INVALID)
|
|
duplicate = {**candidate, "keyEvents": [{"id": "event-1"}, {"id": "event-1"}]}
|
|
self.assertEqual(check_candidate_output(duplicate, 489)["status"], STATUS_SCHEMA_INVALID)
|
|
missing_id = {**candidate, "keyEvents": [{"event": "没有 id"}]}
|
|
self.assertEqual(check_candidate_output(missing_id, 489)["status"], STATUS_SCHEMA_INVALID)
|
|
unknown_field = {**candidate, "futureSources": ["target-scaffold"]}
|
|
self.assertEqual(check_candidate_output(unknown_field, 489)["status"], STATUS_SCHEMA_INVALID)
|
|
future_ref = {**candidate, "sourceRefs": ["target-scaffold"]}
|
|
self.assertEqual(
|
|
check_candidate_output(
|
|
future_ref,
|
|
489,
|
|
[{"sourceId": "target-scaffold", "chapter": 489}],
|
|
)["status"],
|
|
STATUS_TARGET_SOURCE_FORBIDDEN,
|
|
)
|
|
|
|
def test_replay_fails_closed_before_model(self):
|
|
common = {"snapshotVersion": "v0", "asOfChapter": 488}
|
|
manifests = {
|
|
"outline_only": arm(common, []),
|
|
"outline_plus_cards": arm(common, [{"id": 1}]),
|
|
"outline_plus_placebo_cards": arm(common, [{"id": 2}]),
|
|
}
|
|
result = check_replay(
|
|
authorization=AUTH,
|
|
as_of_chapter=488,
|
|
target_chapter=489,
|
|
planner_sources=[{"chapter": 489}],
|
|
arm_manifests=manifests,
|
|
)
|
|
self.assertFalse(result["ok"])
|
|
self.assertEqual(result["status"], STATUS_TARGET_SOURCE_FORBIDDEN)
|
|
|
|
def test_production_arm_semantics_and_chapter_binding(self):
|
|
common = {"snapshotVersion": "v0", "asOfChapter": 488, "targetChapter": 489}
|
|
manifests = {
|
|
"outline_only": {
|
|
**common,
|
|
"cardInjectionCount": 0,
|
|
"cardSourceIds": [],
|
|
"cardStrategy": "none",
|
|
},
|
|
"outline_plus_cards": {
|
|
**common,
|
|
"cardInjectionCount": 1,
|
|
"cardSourceIds": ["correct-1"],
|
|
"cardStrategy": "correct",
|
|
},
|
|
"outline_plus_placebo_cards": {
|
|
**common,
|
|
"cardInjectionCount": 1,
|
|
"cardSourceIds": ["placebo-1"],
|
|
"cardStrategy": "placebo",
|
|
},
|
|
}
|
|
self.assertTrue(
|
|
check_replay(
|
|
authorization=AUTH,
|
|
as_of_chapter=488,
|
|
target_chapter=489,
|
|
planner_sources=[{"sourceId": "history-488", "sourceVersion": "v1", "chapter": 488}],
|
|
arm_manifests=manifests,
|
|
)["ok"]
|
|
)
|
|
wrong = {**manifests, "outline_only": {**manifests["outline_only"], "cardInjectionCount": 1}}
|
|
self.assertEqual(
|
|
check_replay(
|
|
authorization=AUTH,
|
|
as_of_chapter=488,
|
|
target_chapter=489,
|
|
planner_sources=[{"sourceId": "history-488", "sourceVersion": "v1", "chapter": 488}],
|
|
arm_manifests=wrong,
|
|
)["status"],
|
|
STATUS_INVALID_ARM_DIFF,
|
|
)
|
|
|
|
def test_missing_source_chapter_is_blocked(self):
|
|
result = check_target_sources(489, [{"sourceId": "unknown", "sourceVersion": "v1", "payload": "future"}])
|
|
self.assertEqual(result["status"], STATUS_TARGET_SOURCE_FORBIDDEN)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|