102 lines
4.7 KiB
Python
102 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Focused BAT-002 live-battle specification assertions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
PAGE = ROOT / "docs/design-authority/pages/BAT-002-live-battle"
|
|
|
|
|
|
def load(path: Path):
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
class Bat002SpecificationTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.contract = load(PAGE / "page-contract.json")
|
|
self.matrix = load(PAGE / "state-matrix.json")
|
|
|
|
def test_index_preserves_spec_package_through_runtime_states(self) -> None:
|
|
index = load(ROOT / "docs/design-authority/design-index.json")
|
|
page = index["pages"]["BAT-002"]
|
|
self.assertIn(page["status"], {
|
|
"IMPLEMENTATION_IN_PROGRESS",
|
|
"IMPLEMENTED_AWAITING_OWNER_RUNTIME_REVIEW",
|
|
"VERIFIED_AWAITING_OWNER_ACCEPTANCE",
|
|
"COMPLETE",
|
|
})
|
|
self.assertEqual(page["contract"], "pages/BAT-002-live-battle/page-contract.json")
|
|
self.assertEqual(page["state_matrix"], "pages/BAT-002-live-battle/state-matrix.json")
|
|
|
|
def test_contract_and_matrix_cover_the_same_states(self) -> None:
|
|
self.assertEqual(self.contract["page_id"], "BAT-002")
|
|
self.assertIn(self.contract["state"], {
|
|
"IMPLEMENTATION_IN_PROGRESS",
|
|
"IMPLEMENTED_AWAITING_OWNER_RUNTIME_REVIEW",
|
|
"VERIFIED_AWAITING_OWNER_ACCEPTANCE",
|
|
"COMPLETE",
|
|
})
|
|
self.assertEqual(set(self.contract["states"]), set(self.matrix["states"]))
|
|
self.assertEqual(set(self.contract["states"]), set(self.contract["transition_graph"]))
|
|
declared_states = set(self.contract["states"])
|
|
for destinations in self.contract["transition_graph"].values():
|
|
self.assertTrue(set(destinations).issubset(declared_states))
|
|
self.assertEqual(self.contract["responsive_policy"]["text_scales"], [100, 130])
|
|
self.assertEqual(self.matrix["required_text_scales"], [100, 130])
|
|
self.assertEqual(set(self.matrix["required_locales"]), {"zh_CN", "en"})
|
|
self.assertEqual(
|
|
set(self.matrix["required_viewports"]),
|
|
{"760x360", "844x390", "1280x720"},
|
|
)
|
|
|
|
def test_waiting_error_and_interruption_states_are_explicit(self) -> None:
|
|
waiting = self.matrix["states"]["waiting_for_next_event"]
|
|
self.assertEqual(waiting["exact_english_copy"], "Waiting for next event")
|
|
invalid = self.matrix["states"]["invalid_event_data"]
|
|
self.assertEqual(invalid["timeline"], "paused")
|
|
self.assertEqual(invalid["primary_action"], "retry_snapshot")
|
|
self.assertIn("safe_exit", invalid["secondary_actions"])
|
|
interrupted = self.matrix["states"]["interrupted_background"]
|
|
self.assertEqual(interrupted["primary_action"], "open_bat006")
|
|
self.assertEqual(interrupted["timeline"], "frozen")
|
|
|
|
def test_live_battle_is_automatic_and_diagnostic_only(self) -> None:
|
|
corpus = " ".join(self.contract["constraints"] + self.matrix["required_boundaries"])
|
|
for forbidden in ("attack", "skill", "target-selection", "reorder", "equip", "Reserve-entry"):
|
|
self.assertIn(forbidden, corpus)
|
|
self.assertEqual(
|
|
set(self.contract["required_event_families"]),
|
|
set(self.matrix["required_event_families"]),
|
|
)
|
|
for required in ("melee_contact", "healing_source_and_recipient_change", "knockout_and_line_collapse"):
|
|
self.assertIn(required, self.contract["required_event_families"])
|
|
|
|
def test_page_ownership_and_stopping_boundary_are_truthful(self) -> None:
|
|
ownership = self.contract["control_ownership"]
|
|
for page_id in ("BAT-003", "BAT-004", "BAT-005", "BAT-006", "OUT-001"):
|
|
self.assertIn(page_id, " ".join(ownership.values()))
|
|
outcome = self.matrix["states"]["outcome_locked"]
|
|
self.assertEqual(outcome["primary_action"], "open_out001")
|
|
self.assertIn("no reward", outcome["guard"])
|
|
gate = self.contract["implementation_gate"]
|
|
self.assertIn("No image generation", gate)
|
|
self.assertIn("must not apply reward", gate)
|
|
|
|
def test_visual_authority_is_existing_hand_drawn_battle_direction(self) -> None:
|
|
provenance = " ".join(self.contract["provenance"])
|
|
constraints = " ".join(self.contract["constraints"])
|
|
self.assertIn("05_battle_ui_japanese_scale.png", provenance)
|
|
self.assertIn("phase6_battle_scale_04", provenance)
|
|
self.assertIn("functional provenance only", provenance)
|
|
self.assertIn("Pixel art", constraints)
|
|
self.assertIn("card-wall replacement", constraints)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|