test(P70): add CAP-013 regression tests — terraform validate + dedup unit assertions (P1 remediation)

---
ci---
project: acdl
phase: 70
milestone: v1.12
status: verify
---
/ci---

Code review (P70) flagged 2 P1 testing gaps:
1. No end-to-end terraform validate test for the microservice (the real
   CAP-013 surface). A future refactor could re-break the dedup and the
   suite would stay green.
2. No unit test for the _child_id helper / id_remap / dedup merge.

Added 6 tests (38 adapter tests total, 522 suite total):
- test_microservice_dedup_names_modules_by_child_id: asserts module 'alb'
  + 'service' appear, expanded sub-ids do NOT.
- test_microservice_dedup_rewrites_stack_outputs: service_arn -> module.service,
  lb_arn -> module.alb.
- test_microservice_dedup_rewrites_cross_module_refs: lb_target_group_arn ->
  module.alb.target_group_arn (not module.alb-targetgroup).
- test_microservice_emits_valid_terraform: end-to-end terraform init +
  validate on the microservice main.tf (locks in CAP-013).
- test_single_resource_returns_id_verbatim / test_multi_resource_returns_common_prefix:
  unit tests for _child_id.

P2 nits (noted, not fixed): the ci-vpc-apply/destroy 'if' uses != 'plan'
rather than == 'full' (stricter but not exploitable); _child_id docstring
could note commonprefix is character-wise. Both are post-hoc.
This commit is contained in:
Jon Chery
2026-07-29 13:44:52 +00:00
parent e33d6c890f
commit 6d8c098205
+91 -2
View File
@@ -9,7 +9,7 @@ import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from adapters.terraform.adapter import adapt, _tf_value, _ref_expr, _module_name
from adapters.terraform.adapter import adapt, _tf_value, _ref_expr, _module_name, _child_id
ROOT = Path(__file__).resolve().parent.parent
@@ -241,4 +241,93 @@ class TestAdapterEmitsValidTerraform:
["terraform", "validate"],
cwd=str(tmp_path), capture_output=True, text=True
)
assert result.returncode == 0, f"terraform validate failed: {result.stderr}"
assert result.returncode == 0, f"terraform validate failed: {result.stderr}"
class TestAdapterDedupMultiResourceL1:
"""CAP-013 (v1.12, REQ-129): multi-resource L1s (ecs-service, alb) with
stack outputs + cross-module refs must dedup to ONE module block named by
the composition child id, with all expanded sub-ids rewritten. This is the
regression test that locks in the fix — a future refactor that re-breaks
the dedup must turn this red."""
def test_microservice_dedup_names_modules_by_child_id(self, tmp_path):
import sys
sys.path.insert(0, str(ROOT))
from core.contract_resolver import resolve
stack = resolve(str(ROOT / "contracts/microservice.yml"))
adapt(stack, str(tmp_path))
main_tf = (tmp_path / "main.tf").read_text()
# The merged modules are named by the child id (alb, service), not the
# expanded sub-ids (alb-loadbalancer, service-service, ...).
assert 'module "alb" {' in main_tf
assert 'module "service" {' in main_tf
# Expanded sub-ids must NOT appear as module names.
assert 'module "alb-loadbalancer"' not in main_tf
assert 'module "alb-targetgroup"' not in main_tf
assert 'module "alb-listener"' not in main_tf
assert 'module "service-task-definition"' not in main_tf
assert 'module "service-service"' not in main_tf
def test_microservice_dedup_rewrites_stack_outputs(self, tmp_path):
"""Stack outputs reference the expanded sub-ids; after dedup they
must resolve to the child-id module name."""
import sys
sys.path.insert(0, str(ROOT))
from core.contract_resolver import resolve
stack = resolve(str(ROOT / "contracts/microservice.yml"))
adapt(stack, str(tmp_path))
main_tf = (tmp_path / "main.tf").read_text()
# service_arn output references service-service -> module.service
assert 'output "service_arn" {\n value = module.service.service_arn' in main_tf
# lb_arn output references alb-loadbalancer -> module.alb
assert 'output "lb_arn" {\n value = module.alb.lb_arn' in main_tf
def test_microservice_dedup_rewrites_cross_module_refs(self, tmp_path):
"""A ref: input that targets an expanded sub-id (e.g.
ref:alb-targetgroup.target_group_arn) must be rewritten to the
child-id module (module.alb.target_group_arn)."""
import sys
sys.path.insert(0, str(ROOT))
from core.contract_resolver import resolve
stack = resolve(str(ROOT / "contracts/microservice.yml"))
adapt(stack, str(tmp_path))
main_tf = (tmp_path / "main.tf").read_text()
assert "lb_target_group_arn = module.alb.target_group_arn" in main_tf
# The un-rewritten expanded form must NOT appear.
assert "module.alb-targetgroup" not in main_tf
assert "module.service-service" not in main_tf
def test_microservice_emits_valid_terraform(self, tmp_path):
"""CAP-013 end-to-end: the microservice (multi-resource L1s) main.tf
passes terraform init + validate. This is the real regression — the
v1.11 dedup produced 'No module call name' here."""
import sys
sys.path.insert(0, str(ROOT))
from core.contract_resolver import resolve
stack = resolve(str(ROOT / "contracts/microservice.yml"))
adapt(stack, str(tmp_path))
result = subprocess.run(
["terraform", "init", "-backend=false", "-input=false"],
cwd=str(tmp_path), capture_output=True, text=True
)
assert result.returncode == 0, f"terraform init failed: {result.stderr}"
result = subprocess.run(
["terraform", "validate"],
cwd=str(tmp_path), capture_output=True, text=True
)
assert result.returncode == 0, f"terraform validate failed (CAP-013 regression): {result.stderr}"
class TestChildIdHelper:
"""Unit tests for _child_id (the common-prefix heuristic)."""
def test_single_resource_returns_id_verbatim(self):
assert _child_id(["cluster"]) == "cluster"
assert _child_id(["kms-key"]) == "kms-key"
def test_multi_resource_returns_common_prefix(self):
# ecs-service expands to service-task-definition + service-service
assert _child_id(["service-task-definition", "service-service"]) == "service"
# alb expands to alb-loadbalancer + alb-targetgroup + alb-listener
assert _child_id(["alb-loadbalancer", "alb-targetgroup", "alb-listener"]) == "alb"