|
|
@@ -362,3 +362,87 @@ class TestAdapterDedupRejectsUnregisteredModule:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
adapt(stack, str(tmp_path))
|
|
|
|
adapt(stack, str(tmp_path))
|
|
|
|
assert (tmp_path / "main.tf").exists()
|
|
|
|
assert (tmp_path / "main.tf").exists()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestAdapterDedupMergesSameModule:
|
|
|
|
|
|
|
|
"""P2-2 (v1.14, REQ-139): two resources with the same module collapse
|
|
|
|
|
|
|
|
to one module block named by the child id, with merged inputs. This
|
|
|
|
|
|
|
|
locks in the dedup-merge behavior at the unit level."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_two_resources_same_module_collapse_to_one_block(self, tmp_path):
|
|
|
|
|
|
|
|
"""Two resources sharing the same terraform dir (e.g. cloudfront
|
|
|
|
|
|
|
|
distribution + OAC) must produce ONE module block, not two."""
|
|
|
|
|
|
|
|
stack = {
|
|
|
|
|
|
|
|
"resources": [
|
|
|
|
|
|
|
|
{"id": "cloudfront-distribution", "type": "aws:cloudfront:distribution", "module": "cloudfront@1.0.0", "inputs": {"price_class": "PriceClass_100"}},
|
|
|
|
|
|
|
|
{"id": "cloudfront-originaccesscontrol", "type": "aws:cloudfront:originaccesscontrol", "module": "cloudfront@1.0.0", "inputs": {"viewer_protocol_policy": "redirect-to-https"}},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"outputs": {},
|
|
|
|
|
|
|
|
"data_sources": [],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adapt(stack, str(tmp_path))
|
|
|
|
|
|
|
|
main_tf = (tmp_path / "main.tf").read_text()
|
|
|
|
|
|
|
|
# Exactly one module block for cloudfront (deduped to child id "cloudfront")
|
|
|
|
|
|
|
|
assert main_tf.count('module "cloudfront" {') == 1
|
|
|
|
|
|
|
|
# No separate module blocks for the expanded sub-ids
|
|
|
|
|
|
|
|
assert 'module "cloudfront-distribution"' not in main_tf
|
|
|
|
|
|
|
|
assert 'module "cloudfront-originaccesscontrol"' not in main_tf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_dedup_merges_inputs_from_both_resources(self, tmp_path):
|
|
|
|
|
|
|
|
"""When two resources share a module, their inputs are merged into
|
|
|
|
|
|
|
|
the single module block (first resource's inputs + second's, with
|
|
|
|
|
|
|
|
first-wins for overlapping keys)."""
|
|
|
|
|
|
|
|
stack = {
|
|
|
|
|
|
|
|
"resources": [
|
|
|
|
|
|
|
|
{"id": "cloudfront-distribution", "type": "aws:cloudfront:distribution", "module": "cloudfront@1.0.0", "inputs": {"price_class": "PriceClass_100", "region": "us-east-1"}},
|
|
|
|
|
|
|
|
{"id": "cloudfront-originaccesscontrol", "type": "aws:cloudfront:originaccesscontrol", "module": "cloudfront@1.0.0", "inputs": {"viewer_protocol_policy": "redirect-to-https"}},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"outputs": {},
|
|
|
|
|
|
|
|
"data_sources": [],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adapt(stack, str(tmp_path))
|
|
|
|
|
|
|
|
main_tf = (tmp_path / "main.tf").read_text()
|
|
|
|
|
|
|
|
# Both inputs present in the merged module block
|
|
|
|
|
|
|
|
assert "PriceClass_100" in main_tf
|
|
|
|
|
|
|
|
assert "redirect-to-https" in main_tf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestAdapterRemoteStateKeyOverride:
|
|
|
|
|
|
|
|
"""P2-2 (v1.14, REQ-139): ACDL_REMOTE_STATE_KEY env var overrides the
|
|
|
|
|
|
|
|
default 'platform/terraform.tfstate' key in the emitted
|
|
|
|
|
|
|
|
data terraform_remote_state block. This is the load-bearing correctness
|
|
|
|
|
|
|
|
mechanism for the microservice L2 lifecycle (remote state points at the
|
|
|
|
|
|
|
|
CI VPC, not the platform VPC)."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_default_remote_state_key(self, tmp_path, monkeypatch):
|
|
|
|
|
|
|
|
"""When ACDL_REMOTE_STATE_KEY is unset, the default key is used."""
|
|
|
|
|
|
|
|
monkeypatch.delenv("ACDL_REMOTE_STATE_KEY", raising=False)
|
|
|
|
|
|
|
|
stack = {
|
|
|
|
|
|
|
|
"resources": [
|
|
|
|
|
|
|
|
{"id": "s3", "type": "aws:s3:bucket", "module": "s3@1.0.0", "inputs": {"bucket_name": "test", "region": "us-east-1"}}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"outputs": {},
|
|
|
|
|
|
|
|
"data_sources": ["platform"],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adapt(stack, str(tmp_path))
|
|
|
|
|
|
|
|
terraform_tf = (tmp_path / "terraform.tf").read_text()
|
|
|
|
|
|
|
|
main_tf = (tmp_path / "main.tf").read_text()
|
|
|
|
|
|
|
|
# The remote state data block uses the default key
|
|
|
|
|
|
|
|
assert "platform/terraform.tfstate" in main_tf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_env_override_remote_state_key(self, tmp_path, monkeypatch):
|
|
|
|
|
|
|
|
"""When ACDL_REMOTE_STATE_KEY is set, the emitted data block uses
|
|
|
|
|
|
|
|
the overridden key (e.g. 'spike/ci-vpc/terraform.tfstate')."""
|
|
|
|
|
|
|
|
monkeypatch.setenv("ACDL_REMOTE_STATE_KEY", "spike/ci-vpc/terraform.tfstate")
|
|
|
|
|
|
|
|
stack = {
|
|
|
|
|
|
|
|
"resources": [
|
|
|
|
|
|
|
|
{"id": "s3", "type": "aws:s3:bucket", "module": "s3@1.0.0", "inputs": {"bucket_name": "test", "region": "us-east-1"}}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"outputs": {},
|
|
|
|
|
|
|
|
"data_sources": ["platform"],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adapt(stack, str(tmp_path))
|
|
|
|
|
|
|
|
main_tf = (tmp_path / "main.tf").read_text()
|
|
|
|
|
|
|
|
# The remote state data block uses the overridden key
|
|
|
|
|
|
|
|
assert "spike/ci-vpc/terraform.tfstate" in main_tf
|
|
|
|
|
|
|
|
assert "platform/terraform.tfstate" not in main_tf
|