"""Unit tests for devx.ci.fast_molecule.""" from pathlib import Path from unittest.mock import MagicMock, patch from click.testing import CliRunner from devx.ci.fast_molecule import ( build_molecule_commands, cli, get_molecule_scenarios, ) class TestGetMoleculeScenarios: def test_finds_scenarios(self, tmp_path: Path) -> None: roles_dir = tmp_path / "ansible" / "roles" / "myrole" / "molecule" roles_dir.mkdir(parents=True) (roles_dir / "default").mkdir() (roles_dir / "default" / "molecule.yml").write_text("name: default") (roles_dir / "full").mkdir() (roles_dir / "full" / "molecule.yml").write_text("name: full") (roles_dir / "no_scenario").mkdir() # No molecule.yml scenarios = get_molecule_scenarios("myrole", str(tmp_path / "ansible" / "roles")) assert sorted(scenarios) == ["default", "full"] def test_returns_empty_when_no_molecule_dir(self, tmp_path: Path) -> None: scenarios = get_molecule_scenarios("nonexistent", str(tmp_path / "ansible" / "roles")) assert scenarios == [] class TestBuildMoleculeCommands: def test_builds_commands_for_roles(self, tmp_path: Path) -> None: roles_dir = tmp_path / "ansible" / "roles" for role in ["role_a", "role_b"]: mol_dir = roles_dir / role / "molecule" / "default" mol_dir.mkdir(parents=True) (mol_dir / "molecule.yml").write_text("name: default") commands = build_molecule_commands({"role_a", "role_b"}, str(roles_dir)) assert len(commands) == 2 assert all("molecule test -s default" in c for c in commands) assert all("--destroy=never" in c for c in commands) assert all("ubuntu-2604" in c for c in commands) def test_empty_when_no_scenarios(self, tmp_path: Path) -> None: commands = build_molecule_commands({"nonexistent"}, str(tmp_path / "ansible" / "roles")) assert commands == [] def test_empty_when_no_roles(self) -> None: assert build_molecule_commands(set()) == [] class TestCli: @patch("devx.ci.fast_molecule.get_changed_files") def test_no_changes(self, mock_get: MagicMock) -> None: mock_get.return_value = [] runner = CliRunner() result = runner.invoke(cli, ["--base", "origin/master", "--head", "HEAD"]) assert result.exit_code == 0 assert "No files changed" in result.output @patch("devx.ci.fast_molecule.detect_changed_roles") @patch("devx.ci.fast_molecule.get_changed_files") def test_no_ansible_changes(self, mock_get: MagicMock, mock_detect: MagicMock) -> None: mock_get.return_value = ["src/main.py", "README.md"] mock_detect.return_value = set() runner = CliRunner() result = runner.invoke(cli, ["--base", "origin/master", "--head", "HEAD"]) assert result.exit_code == 0 assert "No Ansible roles changed" in result.output @patch("devx.ci.fast_molecule.detect_changed_roles") @patch("devx.ci.fast_molecule.get_changed_files") def test_detects_changed_roles(self, mock_get: MagicMock, mock_detect: MagicMock) -> None: mock_get.return_value = ["ansible/roles/sso_bridge/tasks/main.yml"] mock_detect.return_value = {"sso_bridge"} runner = CliRunner() result = runner.invoke(cli, ["--base", "origin/master", "--head", "HEAD"]) assert result.exit_code == 0 assert "sso_bridge" in result.output