"""Automated enforcement of module engineering standards (REQ-95, REQ-96).""" import json import os import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) ROOT = Path(__file__).resolve().parent.parent class TestModuleStandards: """REQ-95/96: automated standards enforcement for all modules.""" @pytest.fixture def registry(self): return json.load(open(ROOT / "modules" / "registry.json")) def test_all_l1_have_required_files(self, registry): for name, entry in registry.items(): iface_path = entry["1.0.0"]["interface"] if not iface_path.startswith("modules/l1/"): continue l1_dir = ROOT / "modules" / "l1" / name assert (l1_dir / "interface.json").is_file(), f"{name}: interface.json missing" assert (l1_dir / "instance.json").is_file(), f"{name}: instance.json missing" assert (l1_dir / "README.md").is_file(), f"{name}: README.md missing" assert (l1_dir / "examples" / "simple.yml").is_file(), f"{name}: examples/simple.yml missing" assert (l1_dir / "examples" / "complex.yml").is_file(), f"{name}: examples/complex.yml missing" def test_all_l2_have_required_files(self, registry): for name, entry in registry.items(): iface_path = entry["1.0.0"]["interface"] if not iface_path.startswith("modules/l2/"): continue l2_dir = ROOT / "modules" / "l2" / name assert (l2_dir / "composition.json").is_file(), f"{name}: composition.json missing" assert (l2_dir / "README.md").is_file(), f"{name}: README.md missing" assert (l2_dir / "examples" / "simple.yml").is_file(), f"{name}: examples/simple.yml missing" assert (l2_dir / "examples" / "complex.yml").is_file(), f"{name}: examples/complex.yml missing" def test_all_l1_have_deletion_protection_nfr(self, registry): for name, entry in registry.items(): iface_path = entry["1.0.0"]["interface"] if not iface_path.startswith("modules/l1/"): continue iface = json.load(open(ROOT / iface_path)) assert "deletion_protection" in iface.get("nfrs", {}), \ f"{name}: deletion_protection NFR missing" def test_all_l1_have_encryption_enabled_nfr(self, registry): for name, entry in registry.items(): iface_path = entry["1.0.0"]["interface"] if not iface_path.startswith("modules/l1/"): continue iface = json.load(open(ROOT / iface_path)) assert "encryption_enabled" in iface.get("nfrs", {}), \ f"{name}: encryption_enabled NFR missing" def test_all_l1_deletion_protection_defaults_true(self, registry): for name, entry in registry.items(): iface_path = entry["1.0.0"]["interface"] if not iface_path.startswith("modules/l1/"): continue iface = json.load(open(ROOT / iface_path)) dp = iface.get("nfrs", {}).get("deletion_protection", {}) assert dp.get("default") is True, \ f"{name}: deletion_protection default must be true" def test_all_l1_encryption_enabled_defaults_true(self, registry): for name, entry in registry.items(): iface_path = entry["1.0.0"]["interface"] if not iface_path.startswith("modules/l1/"): continue iface = json.load(open(ROOT / iface_path)) ee = iface.get("nfrs", {}).get("encryption_enabled", {}) assert ee.get("default") is True, \ f"{name}: encryption_enabled default must be true" def test_all_modules_registered(self, registry): l1_dirs = [d.name for d in (ROOT / "modules" / "l1").iterdir() if d.is_dir() and not d.name.startswith(".")] l2_dirs = [d.name for d in (ROOT / "modules" / "l2").iterdir() if d.is_dir() and not d.name.startswith(".")] for name in l1_dirs: assert name in registry, f"modules/l1/{name}/ not in registry.json" for name in l2_dirs: assert name in registry, f"modules/l2/{name}/ not in registry.json" def test_all_l1_readmes_have_nfrs_section(self, registry): """Check NFRs section for new v1.8 primitives (kms-key, uptime). Existing pre-v1.8 READMEs are grandfathered — the interface.json NFR check is the binding enforcement.""" new_primitives = ["kms-key", "uptime"] for name in new_primitives: if name not in registry: continue readme = open(ROOT / "modules" / "l1" / name / "README.md").read() assert "## NFRs" in readme or "## NFR" in readme, \ f"{name}: README.md must have a NFRs section" def test_standards_md_exists(self): assert (ROOT / "modules" / "STANDARDS.md").is_file(), "modules/STANDARDS.md must exist" def test_standards_md_has_required_sections(self): content = open(ROOT / "modules" / "STANDARDS.md").read() assert "L1 Primitive Standards" in content assert "L2 Module Standards" in content assert "Encryption by Default" in content assert "Deletion Protection by Default" in content assert "Code Review Checklist" in content def test_catalog_index_has_all_primitives(self, registry): content = open(ROOT / "modules" / "README.md").read() for name in registry: if registry[name]["1.0.0"]["interface"].startswith("modules/l1/"): assert name in content, f"modules/README.md catalog index missing primitive: {name}" def test_template_has_nfrs_section(self): content = open(ROOT / "modules" / "README-TEMPLATE.md").read() assert "## NFRs" in content or "## NFR" in content, "README-TEMPLATE.md must have NFRs section"