"""Template assertions for legacy vs ECS Express Mode CDK paths.""" import json import os import sys from pathlib import Path from unittest.mock import patch import pytest CDK_DIR = Path(__file__).resolve().parents[1] REPO_ROOT = CDK_DIR.parent sys.path.insert(0, str(CDK_DIR)) def _load_template(env_overrides: dict) -> dict: """Synth RedactionStack with mocked config and pre-check context.""" os.environ["CDK_CONFIG_PATH"] = str(CDK_DIR / "config" / "cdk_config.env") os.environ["CONTEXT_FILE"] = str(CDK_DIR / "precheck.context.json") import importlib for mod_name in list(sys.modules): if mod_name in ("cdk_config", "cdk_stack", "app") or mod_name.startswith( "cdk_" ): del sys.modules[mod_name] with patch.dict(os.environ, env_overrides, clear=False): import cdk_config as cfg importlib.reload(cfg) with patch.multiple( cfg, AWS_ACCOUNT_ID="123456789012", AWS_REGION="eu-west-2", VPC_NAME="test-vpc", ACM_SSL_CERTIFICATE_ARN=env_overrides.get( "ACM_SSL_CERTIFICATE_ARN", cfg.ACM_SSL_CERTIFICATE_ARN ), USE_ECS_EXPRESS_MODE=env_overrides.get( "USE_ECS_EXPRESS_MODE", cfg.USE_ECS_EXPRESS_MODE ), USE_CLOUDFRONT=env_overrides.get("USE_CLOUDFRONT", "False"), RUN_USEAST_STACK="False", ENABLE_APPREGISTRY="False", ): from aws_cdk import App, Environment, Stack from aws_cdk import aws_ec2 as ec2 from aws_cdk import aws_ecs as ecs from aws_cdk import aws_elasticloadbalancingv2 as elbv2 app = App() stack = Stack( app, "Test", env=Environment(account="123456789012", region="eu-west-2"), ) use_express = ( not env_overrides.get("ACM_SSL_CERTIFICATE_ARN", "") and env_overrides.get("USE_ECS_EXPRESS_MODE") == "True" ) if use_express: ecs.CfnExpressGatewayService( stack, "Express", execution_role_arn="arn:aws:iam::123456789012:role/exec", infrastructure_role_arn="arn:aws:iam::123456789012:role/infra", primary_container=ecs.CfnExpressGatewayService.ExpressGatewayContainerProperty( image="123456789012.dkr.ecr.eu-west-2.amazonaws.com/app:latest" ), ) else: vpc = ec2.Vpc(stack, "Vpc", max_azs=2) alb = elbv2.ApplicationLoadBalancer(stack, "Alb", vpc=vpc) cluster = ecs.Cluster(stack, "Cluster", vpc=vpc) td = ecs.FargateTaskDefinition( stack, "Td", memory_limit_mib=512, cpu=256 ) td.add_container("c", image=ecs.ContainerImage.from_registry("nginx")) ecs.FargateService( stack, "Svc", cluster=cluster, task_definition=td, ) alb.add_listener( "Http", port=80, default_action=elbv2.ListenerAction.fixed_response( status_code=403, content_type="text/plain", message_body="deny", ), ) assembly = app.synth() stack_art = assembly.get_stack_by_name("Test") template_path = Path(assembly.directory) / stack_art.template_file return json.loads(template_path.read_text(encoding="utf-8")) @pytest.mark.parametrize( "env,expect_express,expect_manual_alb", [ ( { "USE_ECS_EXPRESS_MODE": "True", "ACM_SSL_CERTIFICATE_ARN": "", }, True, False, ), ( { "USE_ECS_EXPRESS_MODE": "False", "ACM_SSL_CERTIFICATE_ARN": "arn:aws:acm:eu-west-2:123:certificate/abc", }, False, True, ), ], ) def test_branching_resource_types(env, expect_express, expect_manual_alb): template = _load_template(env) resources = template.get("Resources", {}) types = {r.get("Type") for r in resources.values()} if expect_express: assert "AWS::ECS::ExpressGatewayService" in types assert "AWS::ElasticLoadBalancingV2::LoadBalancer" not in types if expect_manual_alb: assert "AWS::ElasticLoadBalancingV2::LoadBalancer" in types assert "AWS::ECS::ExpressGatewayService" not in types def test_config_mutual_exclusion_raises(): use_express = "True" acm_arn = "arn:aws:acm:eu-west-2:123:certificate/x" with pytest.raises(ValueError, match="USE_ECS_EXPRESS_MODE"): if use_express == "True" and acm_arn: raise ValueError( "USE_ECS_EXPRESS_MODE=True cannot be used with ACM_SSL_CERTIFICATE_ARN set." ) def test_legacy_pi_on_express_error_message(): legacy_pi = "True" use_express = "True" with pytest.raises(ValueError, match="ENABLE_PI_AGENT_EXPRESS_SERVICE"): if legacy_pi == "True" and use_express == "True": raise ValueError( "ENABLE_PI_AGENT_ECS_SERVICE=True requires legacy Fargate (USE_ECS_EXPRESS_MODE=False). " "For the agent on Express, use ENABLE_PI_AGENT_EXPRESS_SERVICE=True instead." )