"""Unit tests for devx.tools.tofu_ops.""" from pathlib import Path from unittest.mock import MagicMock, patch import pytest from click.testing import CliRunner from devx.tools.tofu_ops import ( _run_tofu, cli, tofu_init, tofu_validate, ) class TestRunTofu: @patch("devx.tools.tofu_ops.subprocess.run") def test_success(self, mock_run: MagicMock, tmp_path: Path) -> None: mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="") _run_tofu(["tofu", "init"], tmp_path) mock_run.assert_called_once() @patch("devx.tools.tofu_ops.subprocess.run") def test_failure_raises(self, mock_run: MagicMock, tmp_path: Path) -> None: mock_run.return_value = MagicMock(returncode=1, stdout="", stderr="error") with pytest.raises(Exception, match="error"): _run_tofu(["tofu", "validate"], tmp_path) class TestTofuInit: @patch("devx.tools.tofu_ops._run_tofu") def test_init_existing_dirs(self, mock_run: MagicMock, tmp_path: Path) -> None: (tmp_path / "tofu/environments/staging").mkdir(parents=True) (tmp_path / "tofu/environments/dns").mkdir(parents=True) tofu_init("staging", root=str(tmp_path)) assert mock_run.call_count == 2 @patch("devx.tools.tofu_ops._run_tofu") def test_init_skips_missing_dirs(self, mock_run: MagicMock, tmp_path: Path) -> None: (tmp_path / "tofu/environments/staging").mkdir(parents=True) # dns dir doesn't exist tofu_init("staging", root=str(tmp_path)) assert mock_run.call_count == 1 @patch("devx.tools.tofu_ops._run_tofu") def test_init_no_dirs_exist(self, mock_run: MagicMock, tmp_path: Path) -> None: tofu_init("staging", root=str(tmp_path)) mock_run.assert_not_called() @patch("devx.tools.tofu_ops._run_tofu") def test_init_custom_dirs(self, mock_run: MagicMock, tmp_path: Path) -> None: (tmp_path / "custom/dir").mkdir(parents=True) tofu_init("staging", root=str(tmp_path), extra_dirs=["custom/dir"]) assert mock_run.call_count == 1 class TestTofuValidate: @patch("devx.tools.tofu_ops._run_tofu") def test_validate_all_dirs(self, mock_run: MagicMock, tmp_path: Path) -> None: for d in [ "tofu/modules/hetzner-vm", "tofu/modules/hetzner-network", "tofu/environments/staging", "tofu/environments/production", "tofu/environments/dns", ]: (tmp_path / d).mkdir(parents=True) tofu_validate(root=str(tmp_path)) assert mock_run.call_count == 5 @patch("devx.tools.tofu_ops._run_tofu") def test_validate_skips_missing(self, mock_run: MagicMock, tmp_path: Path) -> None: (tmp_path / "tofu/environments/staging").mkdir(parents=True) tofu_validate(root=str(tmp_path)) assert mock_run.call_count == 1 @patch("devx.tools.tofu_ops._run_tofu") def test_validate_ci_mode(self, mock_run: MagicMock, tmp_path: Path) -> None: (tmp_path / "tofu/environments/staging").mkdir(parents=True) tofu_validate(root=str(tmp_path), ci=True) # CI mode runs init + validate = 2 calls per dir assert mock_run.call_count == 2 first_call = mock_run.call_args_list[0][0][0] assert "init" in first_call assert "-backend=false" in first_call @patch("devx.tools.tofu_ops._run_tofu") def test_validate_custom_dirs(self, mock_run: MagicMock, tmp_path: Path) -> None: (tmp_path / "custom").mkdir() tofu_validate(root=str(tmp_path), dirs=["custom"]) assert mock_run.call_count == 1 class TestCli: @patch("devx.tools.tofu_ops.tofu_init") def test_init_command(self, mock_init: MagicMock) -> None: runner = CliRunner() result = runner.invoke(cli, ["init", "--env", "staging"]) assert result.exit_code == 0 mock_init.assert_called_once_with("staging", ".") @patch("devx.tools.tofu_ops.tofu_validate") def test_validate_command(self, mock_validate: MagicMock) -> None: runner = CliRunner() result = runner.invoke(cli, ["validate"]) assert result.exit_code == 0 mock_validate.assert_called_once_with(".", ci=False) @patch("devx.tools.tofu_ops.tofu_validate") def test_validate_ci_command(self, mock_validate: MagicMock) -> None: runner = CliRunner() result = runner.invoke(cli, ["validate", "--ci"]) assert result.exit_code == 0 mock_validate.assert_called_once_with(".", ci=True)