Files
ZNJJ-api-server/test/test_phase0_artifacts.py
2026-07-27 17:21:29 +08:00

120 lines
4.1 KiB
Python

import json
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DOMAIN = ROOT / "docs" / "domain"
GOLDEN = ROOT / "test" / "fixtures" / "golden" / "accident-scenarios.json"
def load_json(path):
return json.loads(path.read_text(encoding="utf-8"))
def test_stage_registry_and_transition_matrix_are_closed():
stage_registry = load_json(DOMAIN / "stage-codes.json")
transitions = load_json(DOMAIN / "stage-transitions.json")
codes = {entry["code"] for entry in stage_registry["codes"]}
assert len(codes) == len(stage_registry["codes"])
assert {"0000", "0004", "0005", "1001", "1002", "3001", "3002"} <= codes
assert set(transitions["allowed"]) == codes
for source, targets in transitions["allowed"].items():
assert set(targets) <= codes, source
for terminal in {"0000", "0001", "0002", "0003", "0004", "0005"}:
assert transitions["allowed"][terminal] == []
def test_photo_sequences_cannot_skip_steps():
transitions = load_json(DOMAIN / "stage-transitions.json")
for sequence in transitions["photo_sequences"].values():
for current, following in zip(sequence, sequence[1:]):
assert following in transitions["allowed"][current]
later_steps = set(sequence[sequence.index(following) + 1 :])
assert later_steps.isdisjoint(transitions["allowed"][current])
def test_field_registry_groups_are_complete_and_unique():
registry = load_json(DOMAIN / "field-registry.json")
fields = registry["fields"]
keys = [field["key"] for field in fields]
assert len(keys) == len(set(keys))
assert set(keys) == {
key for group_keys in registry["groups"].values() for key in group_keys
}
assert {"sfzmhm1", "sjhm1", "hphm1", "sfzmhm2", "sjhm2", "hphm2"} <= {
field["key"] for field in fields if field["sensitive"]
}
assert {"sfzmwh1", "sjwh1", "sfzmwh2", "sjwh2"} <= {
field["key"] for field in fields if not field["external_write"]
}
def test_golden_scenarios_are_unique_and_use_known_stages():
stages = {
entry["code"]
for entry in load_json(DOMAIN / "stage-codes.json")["codes"]
}
scenarios = load_json(GOLDEN)["scenarios"]
case_ids = [case["case_id"] for case in scenarios]
assert len(scenarios) >= 12
assert len(case_ids) == len(set(case_ids))
for case in scenarios:
assert case["initial_stage"] in stages
if "expected_stage" in case:
assert case["expected_stage"] in stages
for turn in case.get("turns", []):
if "expected_stage" in turn:
assert turn["expected_stage"] in stages
def test_golden_turn_sequences_follow_the_transition_matrix():
allowed = load_json(DOMAIN / "stage-transitions.json")["allowed"]
scenarios = load_json(GOLDEN)["scenarios"]
for case in scenarios:
current = case["initial_stage"]
for turn in case.get("turns", []):
expected = turn.get("expected_stage")
if expected is None:
continue
if "expected_error" not in turn:
assert expected in allowed[current], (
case["case_id"],
current,
expected,
)
current = expected
def test_golden_fixture_contains_no_realistic_phone_or_national_id():
raw = GOLDEN.read_text(encoding="utf-8")
assert not re.search(r"(?<!\d)1[3-9]\d{9}(?!\d)", raw)
assert not re.search(r"(?<!\d)\d{17}[\dXx](?!\d)", raw)
def test_repository_sources_and_examples_do_not_embed_fastgpt_tokens():
token_pattern = re.compile(r"fastgpt-[A-Za-z0-9]{20,}")
candidates = [ROOT / ".env.example"]
for directory in ("src", "test", "docs"):
candidates.extend(
path
for path in (ROOT / directory).rglob("*")
if path.is_file() and "__pycache__" not in path.parts
)
for path in candidates:
try:
text = path.read_text(encoding="utf-8")
except UnicodeDecodeError:
continue
assert not token_pattern.search(text), path