Files
acdl/tests/test_module_standards.py
T
Jon Chery cb02c69e0c docs(P35): module engineering standards + catalog fix + template update (REQ-95, REQ-96)
---ci---
project: acdl
phase: 35
milestone: v1.8
status: execute
---/ci---

- modules/STANDARDS.md: comprehensive L1+L2 authoring + code review
  standards (9 sections: L1 standards, L2 standards, encryption by default,
  deletion protection by default, registry, README standards, adapter
  extension pattern, code review checklist).
- modules/README.md: catalog index fixed — rds, kms-key, uptime added
  to the Primitives table.
- modules/README-TEMPLATE.md: NFRs section added between Outputs and Usage.
- tests/test_module_standards.py: automated enforcement test (12 tests)
  validating required files, NFRs, registration, README sections.

Tests: +12 (332 -> 344). All pass.
2026-07-22 22:20:50 +00:00

122 lines
5.8 KiB
Python

"""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.yaml").is_file(), f"{name}: examples/simple.yaml missing"
assert (l1_dir / "examples" / "complex.yaml").is_file(), f"{name}: examples/complex.yaml 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.yaml").is_file(), f"{name}: examples/simple.yaml missing"
assert (l2_dir / "examples" / "complex.yaml").is_file(), f"{name}: examples/complex.yaml 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"