"""REQ-106 + REQ-290: consumer guide documents both promotion shapes. Shape A (Step 8): edit the environment field → platform destroys the prior env before building the new env (no orphan path). Shape B (Per-environment deployment): per-env caller workflows, no field editing, promotion = running the matching job. """ from pathlib import Path ROOT = Path(__file__).resolve().parent.parent GUIDE = ROOT / "docs" / "consumer-guide.md" def test_consumer_guide_has_per_env_section(): text = GUIDE.read_text() assert "Per-environment deployment" in text assert "promotion-without-editing" in text.lower() or "promotion = running the matching job" in text.lower() def test_consumer_guide_has_four_caller_examples(): text = GUIDE.read_text() assert "deploy-dev" in text assert "deploy-qa" in text assert "deploy-prod" in text assert "deploy-dr" in text def test_consumer_guide_documents_environment_input(): text = GUIDE.read_text() assert "environment" in text assert "workflow input" in text.lower() or "workflow_call" in text.lower() or "environment:" in text def test_consumer_guide_documents_hitl_gates(): text = GUIDE.read_text() assert "approve_qa" in text assert "approve_prod" in text assert "approve_dr" in text assert "separation-of-duties" in text.lower() or "separation of duties" in text.lower() def test_consumer_guide_has_interpolation_reference(): text = GUIDE.read_text() assert "${env.environment}" in text assert "${env.account_id}" in text # P57: contract.module was dropped in favor of contract.id (short acronym) assert "${contract.id}" in text # The old token must not survive the P57 contract redesign assert "${contract.module}" not in text def test_consumer_guide_documents_both_promotion_shapes(): """REQ-290: the guide documents both Shape A (edit + destroy) and Shape B (per-env caller workflows). Replaces the old test_consumer_guide_states_no_field_editing which asserted only Shape B.""" text = GUIDE.read_text() # Shape B: per-env caller workflows, no field editing assert "Per-environment deployment" in text assert "promotion-without-editing" in text.lower() or "promotion = running the matching job" in text.lower() # Shape A: edit environment field (Step 8 documents this as a valid path) assert "Shape A" in text or "Shape B" in text assert "edit the environment field" in text.lower() or "change `environment`" in text.lower() or "change \"environment\"" in text.lower() def test_consumer_guide_documents_destroy_on_env_change(): """REQ-290: the guide states the platform destroys the prior env's resources when the environment field is changed, and that there is no orphan path.""" text = GUIDE.read_text() text_lower = text.lower() # The guide must state the platform destroys the prior environment assert "destroy" in text_lower and ("prior environment" in text_lower or "prior env" in text_lower) # The guide must state there is no orphan path / fail closed assert "no orphan path" in text_lower or "orphan" in text_lower assert "fail closed" in text_lower or "fails closed" in text_lower