From 6d8c098205690fef1c731494dcfdeed1e9d8710f Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 29 Jul 2026 13:44:52 +0000 Subject: [PATCH] =?UTF-8?q?test(P70):=20add=20CAP-013=20regression=20tests?= =?UTF-8?q?=20=E2=80=94=20terraform=20validate=20+=20dedup=20unit=20assert?= =?UTF-8?q?ions=20(P1=20remediation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 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. --- tests/test_adapter.py | 93 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/tests/test_adapter.py b/tests/test_adapter.py index ffd0bde..a1ee5f4 100644 --- a/tests/test_adapter.py +++ b/tests/test_adapter.py @@ -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}" \ No newline at end of file + 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" \ No newline at end of file