Public Access
193 lines
6.1 KiB
Python
193 lines
6.1 KiB
Python
"""Unit tests for devx.molecule.molecule_changed.
|
|
|
|
Verifies that the script correctly detects changed roles and maps
|
|
them to make targets.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
from click.testing import CliRunner
|
|
|
|
from devx.molecule.molecule_changed import (
|
|
detect_changed_roles,
|
|
get_changed_files,
|
|
main,
|
|
roles_to_targets,
|
|
)
|
|
|
|
|
|
def test_detect_role_change():
|
|
"""A file in ansible/roles/<role>/ maps to that role."""
|
|
files = ["ansible/roles/docker_base/tasks/main.yml"]
|
|
roles = detect_changed_roles(files)
|
|
assert "docker_base" in roles
|
|
|
|
|
|
def test_detect_playbook_change():
|
|
"""A playbook change maps to its included roles."""
|
|
files = ["ansible/playbooks/deploy-observability.yml"]
|
|
roles = detect_changed_roles(files)
|
|
assert "observability" in roles
|
|
assert "docker_base" in roles
|
|
assert "zitadel" in roles
|
|
|
|
|
|
def test_detect_shared_infra_triggers_all():
|
|
"""ansible.cfg change triggers all roles."""
|
|
files = ["ansible/ansible.cfg"]
|
|
roles = detect_changed_roles(files)
|
|
assert len(roles) == 10 # all roles
|
|
|
|
|
|
def test_detect_no_ansible_changes():
|
|
"""Non-Ansible files don't trigger any roles."""
|
|
files = ["scripts/molecule_changed.py", "Makefile"]
|
|
roles = detect_changed_roles(files)
|
|
assert len(roles) == 0
|
|
|
|
|
|
def test_roles_to_targets():
|
|
"""Role names map to make targets."""
|
|
targets = roles_to_targets({"docker_base", "zitadel"})
|
|
assert "molecule-docker-base" in targets
|
|
assert "molecule-zitadel" in targets
|
|
|
|
|
|
def test_roles_to_targets_unknown_role():
|
|
"""Unknown roles are silently skipped."""
|
|
targets = roles_to_targets({"docker_base", "unknown_role"})
|
|
assert targets == ["molecule-docker-base"]
|
|
|
|
|
|
def test_main_no_changes():
|
|
"""When no files changed, outputs message to stderr."""
|
|
with patch("devx.molecule.molecule_changed.get_changed_files", return_value=[]):
|
|
runner = CliRunner()
|
|
result = runner.invoke(main, ["--print-targets"])
|
|
assert result.exit_code == 0
|
|
assert "No changed files" in result.output
|
|
|
|
|
|
def test_main_print_targets():
|
|
"""--print-targets outputs make targets."""
|
|
with patch(
|
|
"devx.molecule.molecule_changed.get_changed_files",
|
|
return_value=["ansible/roles/docker_base/tasks/main.yml"],
|
|
):
|
|
runner = CliRunner()
|
|
result = runner.invoke(main, ["--print-targets"])
|
|
assert result.exit_code == 0
|
|
assert "molecule-docker-base" in result.output
|
|
|
|
|
|
def test_main_print_roles():
|
|
"""--print-roles outputs role names."""
|
|
with patch(
|
|
"devx.molecule.molecule_changed.get_changed_files",
|
|
return_value=["ansible/roles/zitadel/tasks/main.yml"],
|
|
):
|
|
runner = CliRunner()
|
|
result = runner.invoke(main, ["--print-roles"])
|
|
assert result.exit_code == 0
|
|
assert "zitadel" in result.output
|
|
|
|
|
|
def test_main_no_ansible_changes():
|
|
"""When only non-Ansible files changed, outputs no scenarios message."""
|
|
with patch(
|
|
"devx.molecule.molecule_changed.get_changed_files",
|
|
return_value=["scripts/molecule_changed.py"],
|
|
):
|
|
runner = CliRunner()
|
|
result = runner.invoke(main, ["--print-targets"])
|
|
assert result.exit_code == 0
|
|
assert "No molecule scenarios" in result.output
|
|
|
|
|
|
def test_get_changed_files_with_mock():
|
|
"""get_changed_files returns files from git diff."""
|
|
with patch("devx.molecule.molecule_changed._run_git", return_value="file1\nfile2\n"):
|
|
files = get_changed_files("origin/master")
|
|
assert files == ["file1", "file2"]
|
|
|
|
|
|
def test_get_changed_files_falls_back_to_master():
|
|
"""When base ref has no diff, falls back to master."""
|
|
calls: list[list[str]] = []
|
|
|
|
def mock_git(args):
|
|
calls.append(args)
|
|
# First call (origin/master) returns empty, second (master) returns files
|
|
if "origin/master...HEAD" in args[2]:
|
|
return ""
|
|
return "ansible/roles/docker_base/tasks/main.yml\n"
|
|
|
|
with patch("devx.molecule.molecule_changed._run_git", side_effect=mock_git):
|
|
files = get_changed_files("origin/master")
|
|
assert files == ["ansible/roles/docker_base/tasks/main.yml"]
|
|
assert len(calls) == 2
|
|
|
|
|
|
def test_get_changed_files_empty():
|
|
"""When no changes in either ref, returns empty list."""
|
|
with patch("devx.molecule.molecule_changed._run_git", return_value=""):
|
|
files = get_changed_files("origin/master")
|
|
assert files == []
|
|
|
|
|
|
def test_detect_molecule_shared_path():
|
|
"""ansible/molecule/ change triggers all roles."""
|
|
files = ["ansible/molecule/Dockerfile"]
|
|
roles = detect_changed_roles(files)
|
|
assert len(roles) == 10
|
|
|
|
|
|
def test_detect_requirements_yml_triggers_all():
|
|
"""ansible/requirements.yml change triggers all roles."""
|
|
files = ["ansible/requirements.yml"]
|
|
roles = detect_changed_roles(files)
|
|
assert len(roles) == 10
|
|
|
|
|
|
def test_detect_configure_oidc_playbook():
|
|
"""configure-oidc.yml maps to sso_config and app_container."""
|
|
files = ["ansible/playbooks/configure-oidc.yml"]
|
|
roles = detect_changed_roles(files)
|
|
assert "sso_config" in roles
|
|
assert "app_container" in roles
|
|
|
|
|
|
def test_detect_prepare_vms_playbook():
|
|
"""prepare-vms.yml maps to all base roles."""
|
|
files = ["ansible/playbooks/prepare-vms.yml"]
|
|
roles = detect_changed_roles(files)
|
|
assert "docker_base" in roles
|
|
assert "app_hardening" in roles
|
|
assert "storage" in roles
|
|
assert "disk_cleanup" in roles
|
|
assert "crowdsec" in roles
|
|
|
|
|
|
def test_detect_deploy_customer_playbook():
|
|
"""deploy-customer.yml maps to its roles."""
|
|
files = ["ansible/playbooks/deploy-customer.yml"]
|
|
roles = detect_changed_roles(files)
|
|
assert "app_container" in roles
|
|
assert "docker_base" in roles
|
|
assert "app_hardening" in roles
|
|
assert "sso_config" in roles
|
|
|
|
|
|
def test_main_default_base():
|
|
"""main() with no --base uses origin/master."""
|
|
with patch(
|
|
"devx.molecule.molecule_changed.get_changed_files",
|
|
return_value=["ansible/roles/zitadel/tasks/main.yml"],
|
|
) as mock:
|
|
runner = CliRunner()
|
|
result = runner.invoke(main, ["--print-roles"])
|
|
assert result.exit_code == 0
|
|
mock.assert_called_once_with("origin/master")
|