diff --git a/tests/test_module_spec.py b/tests/test_module_spec.py new file mode 100644 index 0000000..d82cec2 --- /dev/null +++ b/tests/test_module_spec.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +import json +import sys +import unittest +from pathlib import Path, PurePosixPath + + +ROOT = Path(__file__).resolve().parents[1] +SPEC_PATH = ROOT / "MODULE_SPEC.yaml" +sys.path.insert(0, str(ROOT / "scripts")) + +from gen_clash_verge_script import load_payload, to_rule # noqa: E402 + + +class ModuleSpecTests(unittest.TestCase): + def test_module_spec_declares_rule_authority_and_safe_context(self) -> None: + spec = json.loads(SPEC_PATH.read_text(encoding="utf-8")) + + self.assertEqual( + set(spec), + { + "schema_version", + "module_id", + "authority", + "repository", + "bounded_context", + "capabilities", + "data", + "contracts", + "dependencies", + "agent_context", + "verification", + }, + ) + self.assertEqual(spec["schema_version"], 1) + self.assertEqual(spec["module_id"], "vpn-rules") + self.assertEqual( + spec["authority"], + { + "scope": "module_metadata", + "subject": "vpn-rules", + "owner": "vpn-rules-owner", + "source": "MODULE_SPEC.yaml", + "revision": 1, + "effective_from": "2026-08-20T00:00:00+08:00", + }, + ) + self.assertEqual( + spec["repository"], + { + "name": "vpn-rules", + "workspace_id": None, + "type": "infrastructure", + "maturity": "operational", + }, + ) + + boundary = spec["bounded_context"] + self.assertEqual(boundary["domain"], "network-routing-policy") + self.assertIn("single source of truth", boundary["responsibility"].lower()) + prohibited = " ".join(boundary["prohibited_responsibilities"]).lower() + self.assertIn("production", prohibited) + self.assertIn("credential", prohibited) + self.assertIn("proxy subscription", prohibited) + + self.assertEqual( + {capability["id"] for capability in spec["capabilities"]}, + { + "direct-routing-rule-authoring", + "clash-verge-script-generation", + "router-rule-projection", + }, + ) + self.assertEqual(spec["contracts"]["consumes"], []) + self.assertEqual(spec["dependencies"], []) + provided = { + contract["contract_id"]: contract + for contract in spec["contracts"]["provides"] + } + self.assertEqual( + set(provided), + { + "mihomo-direct-rule-provider", + "router-direct-rules", + "router-direct-plain-list", + }, + ) + self.assertEqual( + {contract["version"] for contract in provided.values()}, {"1.0.0"} + ) + for contract in provided.values(): + self.assertTrue((ROOT / contract["path"]).is_file()) + + context = spec["agent_context"] + self.assertLessEqual(context["max_default_tokens"], 6000) + self.assertEqual( + {entry["path"] for entry in context["default_entrypoints"]}, + {"README.md", "direct.yaml"}, + ) + for entry in context["default_entrypoints"]: + self.assertTrue((ROOT / entry["path"]).is_file()) + self.assertIn("router", context["excluded_paths"]) + for value in context["excluded_paths"]: + path = PurePosixPath(value) + self.assertFalse(path.is_absolute()) + self.assertNotIn("..", path.parts) + + self.assertEqual( + spec["verification"], + { + "commands": [ + { + "id": "rule-contract-tests", + "argv": [ + "python3", + "-m", + "unittest", + "discover", + "-s", + "tests", + "-v", + ], + "cwd": ".", + "network": False, + "required": True, + } + ] + }, + ) + + def test_checked_in_router_projections_match_the_rule_source(self) -> None: + payload = load_payload(ROOT / "direct.yaml") + plain = (ROOT / "router/direct-plain.txt").read_text(encoding="utf-8") + router = (ROOT / "router/direct-rules.yaml").read_text(encoding="utf-8") + + self.assertEqual(plain, "\n".join(payload) + "\n") + self.assertEqual( + router.split("rules:\n", 1)[1], + "\n".join(f" - {to_rule(entry)}" for entry in payload) + "\n", + ) + self.assertTrue(all(",DIRECT" not in entry for entry in payload)) + + +if __name__ == "__main__": + unittest.main()