Public Access
357 lines
11 KiB
Python
357 lines
11 KiB
Python
"""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
|