"""Unit tests for devx.ci.check_workflow_tofu_init.""" from __future__ import annotations import textwrap from pathlib import Path from click.testing import CliRunner import devx.ci.check_workflow_tofu_init as mod from devx.ci.check_workflow_tofu_init import _check_workflow, main def _write_workflow(tmp_path: Path, content: str) -> Path: filepath = tmp_path / "test.yml" filepath.write_text(textwrap.dedent(content), encoding="utf-8") return filepath class TestCheckWorkflow: def test_passes_when_tofu_init_present(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: deploy: runs-on: docker steps: - run: python3 scripts/create_production_deployment.py --phase tofu-init - run: python3 scripts/preflight_deploy.py --env production """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] def test_fails_when_tofu_init_missing(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: preflight: runs-on: docker steps: - run: python3 scripts/preflight_deploy.py --env production """, ) errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "preflight" in errors[0] assert "tofu-init" in errors[0] def test_passes_when_direct_tofu_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: deploy: runs-on: docker steps: - run: tofu init - run: tofu output -json """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] def test_fails_when_direct_tofu_output_without_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: check: runs-on: docker steps: - run: tofu output -json """, ) errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "check" in errors[0] def test_passes_when_no_tofu_usage(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: lint: runs-on: docker steps: - run: make lint """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] def test_passes_with_staging_deployment_tofu_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: deploy: runs-on: docker steps: - run: python3 scripts/create_staging_deployment.py --phase tofu-init - run: python3 scripts/create_staging_deployment.py --phase deploy """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] def test_fails_with_tofu_plan_without_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: plan: runs-on: docker steps: - run: tofu plan """, ) errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "plan" in errors[0] def test_fails_with_tofu_apply_without_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: apply: runs-on: docker steps: - run: tofu apply -auto-approve """, ) errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "apply" in errors[0] def test_multiple_jobs_one_missing(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: good: runs-on: docker steps: - run: python3 scripts/create_production_deployment.py --phase tofu-init - run: python3 scripts/preflight_deploy.py --env production bad: runs-on: docker steps: - run: python3 scripts/preflight_deploy.py --env production """, ) errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "bad" in errors[0] def test_no_steps_passes(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: empty: runs-on: docker """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] def test_destroy_orphans_does_not_require_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: cleanup: runs-on: docker steps: - run: python3 scripts/destroy_orphans.py """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] def test_invalid_yaml_returns_error(self, tmp_path: Path) -> None: filepath = tmp_path / "bad.yml" filepath.write_text("jobs: [invalid yaml: {", encoding="utf-8") errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "cannot parse YAML" in errors[0] def test_tofu_show_requires_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: show: runs-on: docker steps: - run: tofu show -json """, ) errors = _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) assert len(errors) == 1 assert "show" in errors[0] def test_custom_state_scripts(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: custom: runs-on: docker steps: - run: python3 scripts/my_custom_script.py """, ) errors = _check_workflow(filepath, {"my_custom_script.py"}) assert len(errors) == 1 assert "custom" in errors[0] def test_step_with_no_run_skipped(self, tmp_path: Path) -> None: """A step with no 'run' key should be skipped (line 80 continue).""" filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: deploy: runs-on: docker steps: - name: Checkout uses: actions/checkout@v4 - run: tofu init - run: tofu output """, ) assert _check_workflow(filepath, mod.DEFAULT_TOFU_STATE_SCRIPTS) == [] class TestCli: def test_passes_with_specific_workflow(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: deploy: runs-on: docker steps: - run: tofu init - run: tofu output """, ) runner = CliRunner() result = runner.invoke(main, ["--workflow", str(filepath)]) assert result.exit_code == 0 assert "OK" in result.output def test_fails_with_missing_tofu_init(self, tmp_path: Path) -> None: filepath = _write_workflow( tmp_path, """ name: Test on: push jobs: preflight: runs-on: docker steps: - run: python3 scripts/preflight_deploy.py --env production """, ) runner = CliRunner() result = runner.invoke(main, ["--workflow", str(filepath)]) assert result.exit_code == 1 assert "FAIL" in result.output assert "preflight" in result.output def test_checks_all_workflows_by_default(self, tmp_path: Path) -> None: workflows_dir = tmp_path / "workflows" workflows_dir.mkdir() (workflows_dir / "good.yml").write_text( textwrap.dedent(""" name: Good on: push jobs: deploy: runs-on: docker steps: - run: tofu init - run: tofu output """), encoding="utf-8", ) (workflows_dir / "bad.yml").write_text( textwrap.dedent(""" name: Bad on: push jobs: check: runs-on: docker steps: - run: tofu output """), encoding="utf-8", ) runner = CliRunner() result = runner.invoke(main, ["--workflows-dir", str(workflows_dir)]) assert result.exit_code == 1 assert "bad.yml" in result.output assert "check" in result.output def test_all_workflows_pass(self, tmp_path: Path) -> None: workflows_dir = tmp_path / "workflows" workflows_dir.mkdir() (workflows_dir / "ok.yml").write_text( textwrap.dedent(""" name: OK on: push jobs: deploy: runs-on: docker steps: - run: tofu init - run: tofu plan """), encoding="utf-8", ) runner = CliRunner() result = runner.invoke(main, ["--workflows-dir", str(workflows_dir)]) assert result.exit_code == 0 assert "OK" in result.output