"""REQ-289: run_platform.sh Step 0b environment-transition check. Asserts the shell script contains the env-transition detect-and-destroy block, calls env_transition.py detect, runs terraform destroy on the prior env, fails closed on destroy failure, and records the applied env after success. Pattern: tests/test_pipeline.py:79-95 (read script text + assert substrings). """ from pathlib import Path ROOT = Path(__file__).resolve().parent.parent SCRIPT = ROOT / "scripts" / "run_platform.sh" DEPLOY = ROOT / ".github" / "workflows" / "deploy.yml" def _read(path): return Path(path).read_text() class TestRunPlatformStep0b: def test_step_0b_block_exists(self): """run_platform.sh has a Step 0b: environment-transition check.""" src = _read(SCRIPT) assert "Step 0b: environment-transition check" in src def test_step_0b_calls_env_transition_detect(self): """Step 0b calls env_transition.py detect.""" src = _read(SCRIPT) assert "env_transition.py detect" in src assert "--contract-id" in src assert "--consumer-repo" in src assert "--new-env" in src def test_step_0b_runs_terraform_destroy_on_prior_env(self): """Step 0b runs terraform destroy against the prior env's state.""" src = _read(SCRIPT) assert "terraform destroy" in src assert "prior" in src.lower() assert "deletion_protection" in src assert "false" in src def test_step_0b_fails_closed_on_destroy_failure(self): """Step 0b fails closed: if destroy fails, pipeline exits non-zero.""" src = _read(SCRIPT) assert "NO ORPHAN PATH" in src or "no orphan path" in src.lower() assert "fail" in src.lower() # The destroy failure must call fail() or exit 1 assert "prior-env terraform destroy FAILED" in src or "destroy aborted" in src def test_step_0b_emits_evidence_event(self): """Step 0b emits an ENV_DESTROYED evidence event to the outbox.""" src = _read(SCRIPT) assert "ENV_DESTROYED" in src assert "outbox_writer.py" in src def test_step_0b_uses_terraform_init_reconfigure(self): """Step 0b uses terraform init -reconfigure for the prior env.""" src = _read(SCRIPT) assert "terraform init -reconfigure" in src def test_step_0b_injects_deletion_protection_false(self): """Step 0b injects deletion_protection=false into contract inputs.""" src = _read(SCRIPT) assert "deletion_protection" in src assert "False" in src or "false" in src def test_step_0b_skipped_in_check_only_mode(self): """Step 0b is skipped in --check-only mode (no AWS).""" src = _read(SCRIPT) assert 'CHECK_ONLY" = "0"' in src def test_step_0b_skipped_in_local_mode(self): """Step 0b is skipped in --local mode (emulated).""" src = _read(SCRIPT) assert 'LOCAL_TIER" = "0"' in src def test_step_0b_skipped_in_decommission_mode(self): """Step 0b is skipped in --decommission mode (explicit teardown).""" src = _read(SCRIPT) assert 'DECOMMISSION" = "0"' in src class TestRunPlatformRecordAppliedEnv: def test_record_applied_env_after_apply_mode(self): """run_platform.sh records applied env after --apply success.""" src = _read(SCRIPT) assert "env_transition.py record" in src # Must appear before or after PLATFORM APPLY OK assert "PLATFORM APPLY OK" in src def test_record_applied_env_after_e2e(self): """run_platform.sh records applied env after e2e success.""" src = _read(SCRIPT) assert "env_transition.py record" in src assert "PLATFORM E2E OK" in src def test_record_is_non_fatal(self): """The record call uses || true (non-fatal if DynamoDB unreachable).""" src = _read(SCRIPT) # The record call should not halt the pipeline on failure assert "env_transition.py record" in src class TestRunPlatformConsumerRepo: def test_consumer_repo_env_var_set(self): """CONSUMER_REPO is derived from NOVA_CONSUMER_REPO or GITHUB_REPOSITORY.""" src = _read(SCRIPT) assert "NOVA_CONSUMER_REPO" in src assert "GITHUB_REPOSITORY" in src assert "CONSUMER_REPO" in src class TestDeployWorkflowPassesConsumerRepo: def test_deploy_yml_passes_nova_consumer_repo(self): """deploy.yml passes NOVA_CONSUMER_REPO to run_platform.sh (REQ-286).""" src = _read(DEPLOY) assert "NOVA_CONSUMER_REPO" in src assert "github.repository" in src