Files
acdl/tests/test_module_standards.py
T
Jon Chery 031887ec56 refactor(P57): contract surface redesign + rename + .yml repo-wide
Contract surface redesign:
- New top-level fields: id (3-6 char acronym → stack.name), name (full → stack.title),
  infrastructure (map keyed by module name, replaces module:)
- Drop uses: field (dead reference; version pin lives in CI workflow uses: line)
- Drop top-level module/inputs (now nested under infrastructure map)
- Per-module optional version (defaults to latest published from registry)
- Multi-module contracts: one file deploys N modules in one pipeline run,
  resource IDs namespaced with module name to avoid collisions
- stack.schema.json: add optional title field for display name

Rename:
- pipelines/deploy.yaml → pipelines/contract.yml (declarative spec, not a pipeline)
- pipelines/ci.yaml → pipelines/ci.yml
- All 44 .yaml files → .yml repo-wide (contracts, module examples, kyverno policies)
- .acdl/contract.yaml → .acdl/contract.yml

Resolver (core/contract_resolver.py):
- Rewrite resolve() to loop infrastructure map, default version to latest,
  merge module fragments into one stack with namespaced resource IDs
- _latest_version() picks highest non-deprecated from registry
- _namespace_resources() prefixes IDs + rewrites ref: expressions for multi-module
- Single-module path: unprefixed IDs (backward compatible)

Verification:
- 494 tests pass (0 contract-shape failures)
- Local E2E passes (contract → resolver → adapter → local ECS HTTP 200 → outbox)

---ci---
project: acdl
phase: 57
milestone: v1.10.2
status: execute
---/ci---
2026-07-27 21:37:40 +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.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"