"""REQ-330 tests: core.env.synthesize_local_env — local env synthesizer. Verifies the synthesizer: - reads a contract YAML and produces a local env dict - the dict mirrors the shape of core/environments/*.json (validates against schemas/environment.schema.json) - region is "local" + account_id is the placeholder (no real AWS) - the environment override wins over the contract's environment field - mirrors core/onboarding.py:generate_env_file() shape (same required keys) """ from __future__ import annotations import json import sys from pathlib import Path import jsonschema import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from core.env import synthesize_local_env REPO_ROOT = Path(__file__).resolve().parent.parent ENV_SCHEMA_PATH = REPO_ROOT / "schemas" / "environment.schema.json" @pytest.fixture def env_schema(): return json.loads(ENV_SCHEMA_PATH.read_text()) @pytest.fixture def sample_contract(tmp_path): """A minimal contract YAML for the synthesizer to read.""" contract = """ id: msvc name: microservice environment: dev infrastructure: microservice: version: "1.0.0" inputs: image: nginx:latest """ p = tmp_path / "contract.yml" p.write_text(contract) return p class TestSynthesizeLocalEnv: def test_returns_dict_with_required_keys(self, sample_contract, env_schema): env = synthesize_local_env(str(sample_contract)) assert isinstance(env, dict) # The schema-required keys. for key in ( "name", "account_id", "region", "state_backend", "network", "runner_role_arn", "autonomy", "confidence_threshold", ): assert key in env, f"missing required key: {key}" def test_validates_against_environment_schema(self, sample_contract, env_schema): env = synthesize_local_env(str(sample_contract)) jsonschema.validate(env, env_schema) # raises on invalid def test_region_is_local(self, sample_contract): env = synthesize_local_env(str(sample_contract)) assert env["region"] == "local", "region must be the local sentinel" def test_account_id_is_placeholder(self, sample_contract): env = synthesize_local_env(str(sample_contract)) assert env["account_id"] == "000000000000", ( "account_id must be the placeholder (no real AWS account)" ) def test_state_backend_is_local(self, sample_contract): env = synthesize_local_env(str(sample_contract)) sb = env["state_backend"] assert sb["bucket"] == "local-tfstate" assert sb["lock_table"] == "local-locks" def test_uses_contract_environment_by_default(self, sample_contract): env = synthesize_local_env(str(sample_contract)) assert env["name"] == "dev" # the contract's environment field def test_environment_override_wins(self, sample_contract): env = synthesize_local_env(str(sample_contract), environment="qa") assert env["name"] == "qa" # qa threshold is 0.75 (per-env default) assert env["confidence_threshold"] == 0.75 def test_confidence_threshold_per_env(self, sample_contract): for env_name, expected in (("dev", 0.50), ("qa", 0.75), ("prod", 0.90), ("dr", 0.95)): env = synthesize_local_env(str(sample_contract), environment=env_name) assert env["confidence_threshold"] == expected, env_name def test_autonomy_is_full(self, sample_contract): env = synthesize_local_env(str(sample_contract)) assert env["autonomy"] == "full" # local tier is autonomous def test_no_real_aws_resources(self, sample_contract): """The synthesizer must NOT reference real AWS resources — region is 'local', the ARN uses the placeholder account, the bucket is local.""" env = synthesize_local_env(str(sample_contract)) assert "us-east-1" not in env["region"] assert "000000000000" in env["runner_role_arn"] assert "local" in env["state_backend"]["bucket"] def test_mirrors_onboarding_env_file_shape(self, sample_contract, env_schema): """The synthesized env has the same core shape as core/onboarding.py:generate_env_file() output — both carry the schema-required environment-binding keys. (generate_env_file adds ownerId/billingTag for the onboarding request path; the synthesizer is the local-tier counterpart and omits those — no consumer binding.)""" from core.onboarding import generate_env_file request = { "consumerRepo": "acdl/consumer-a", "requestedEnvironment": "dev", "ownerId": "team-a", "billingTag": "cc-a", } onboarded = generate_env_file(request) # The synthesizer output validates against the env schema. synth = synthesize_local_env(str(sample_contract)) jsonschema.validate(synth, env_schema) # Both carry the schema-required environment-binding keys. required = { "name", "account_id", "region", "state_backend", "network", "runner_role_arn", "autonomy", "confidence_threshold", } assert required <= set(onboarded.keys()), "onboarding output missing required keys" assert required <= set(synth.keys()), "synthesizer output missing required keys" # The synthesizer omits the onboarding-request-only keys. assert "ownerId" not in synth assert "billingTag" not in synth def test_missing_contract_file_defaults_to_dev(self, tmp_path): """A non-existent contract path defaults to the dev env (no crash).""" env = synthesize_local_env(str(tmp_path / "nonexistent.yml")) assert env["name"] == "dev" assert env["region"] == "local" def test_description_mentions_contract_id(self, sample_contract): env = synthesize_local_env(str(sample_contract)) assert "msvc" in env["description"], ( "description should reference the contract id for traceability" )