"""P1-1: adapter ECS/ALB/VPC defaults are parameterized via L1 interface.json inputs (REQ-102, D-085). The adapter is a thin translator — defaults live in the interface, not the adapter. """ import json import os import sys from pathlib import Path import yaml ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) from adapters.terraform.adapter import adapt from core.contract_resolver import resolve def _load_ir(path): with open(path) as f: return json.load(f) def _tf_for_contract(contract_dict, tmp_path): """Resolve a contract dict to a stack, emit TF, return the main.tf text.""" contract_path = tmp_path / "contract.yaml" contract_path.write_text(yaml.safe_dump(contract_dict)) stack = resolve(str(contract_path)) out_dir = tmp_path / "tf" adapt(stack, str(out_dir)) return (out_dir / "main.tf").read_text() def test_desired_count_override_emits_overridden_value(tmp_path): """An L1 with desired_count: 3 in contract inputs emits desired_count = 3.""" contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, "desired_count": 3, }, } tf = _tf_for_contract(contract, tmp_path) assert "desired_count = 3" in tf assert "desired_count = 1" not in tf def test_desired_count_default_emits_one_via_interface(tmp_path): """Absent desired_count emits desired_count = 1 via interface default.""" contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, }, } tf = _tf_for_contract(contract, tmp_path) assert "desired_count = 1" in tf def test_launch_type_override_emits_overridden_value(tmp_path): contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, "launch_type": "EC2", }, } tf = _tf_for_contract(contract, tmp_path) assert 'launch_type = "EC2"' in tf assert 'launch_type = "FARGATE"' not in tf def test_target_type_override_emits_overridden_value(tmp_path): contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, "target_type": "instance", }, } tf = _tf_for_contract(contract, tmp_path) assert 'target_type = "instance"' in tf assert 'target_type = "ip"' not in tf def test_load_balancer_type_override_emits_overridden_value(tmp_path): contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, "load_balancer_type": "network", }, } tf = _tf_for_contract(contract, tmp_path) assert 'load_balancer_type = "network"' in tf assert 'load_balancer_type = "application"' not in tf def test_family_override_emits_overridden_value(tmp_path): contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, "family": "myservice", }, } tf = _tf_for_contract(contract, tmp_path) assert 'family = "myservice"' in tf def test_family_default_emits_app(tmp_path): contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, }, } tf = _tf_for_contract(contract, tmp_path) assert 'family = "app"' in tf def test_v1_1_s3_regression_still_passes(tmp_path): """The v1.1 S3 regression: the static-assets L1 (s3-only) must still produce valid Terraform with no ECS/ALB/VPC defaults leaking in.""" contract_path = ROOT / "contracts" / "static-assets.yaml" stack = resolve(str(contract_path)) out_dir = tmp_path / "tf" adapt(stack, str(out_dir)) tf = (out_dir / "main.tf").read_text() assert "aws_s3_bucket" in tf assert "desired_count" not in tf assert "launch_type" not in tf assert "target_type" not in tf def test_no_hardcoded_microservice_name_in_route_table(tmp_path): """The hardcoded 'acdl-microservice-rt' / 'acdl-microservice-igw' Name tags are removed (D-085); the name derives from the VPC name input.""" contract = { "uses": "acdl/pipelines/deploy.yaml@v1.9", "module": "microservice", "environment": "dev", "inputs": { "bucket_name": "acdl-test", "region": "us-east-1", "image": "public.ecr.aws/docker/library/nginx:latest", "port": 80, }, } tf = _tf_for_contract(contract, tmp_path) assert "acdl-microservice-rt" not in tf assert "acdl-microservice-igw" not in tf def test_ecs_service_interface_has_parameterized_inputs(): """The L1 interface declares the inputs (the adapter reads them).""" iface = _load_ir(ROOT / "modules" / "l1" / "ecs-service" / "interface.json") inputs = iface["inputs"] assert "desired_count" in inputs assert inputs["desired_count"]["default"] == 1 assert "launch_type" in inputs assert inputs["launch_type"]["default"] == "FARGATE" assert "family" in inputs assert inputs["family"]["default"] == "app" def test_alb_interface_has_parameterized_inputs(): iface = _load_ir(ROOT / "modules" / "l1" / "alb" / "interface.json") inputs = iface["inputs"] assert "load_balancer_type" in inputs assert inputs["load_balancer_type"]["default"] == "application" assert "target_type" in inputs assert inputs["target_type"]["default"] == "ip"