Files
devx/tests/unit/test_cli.py
T
emilandemo bd4530094e
Post-merge / detect-and-configure (push) Successful in 18s
Post-merge / release-and-maintain (push) Successful in 1m13s
DEVX-152: fix: remove dead translation keys and add missing one
Co-authored-by: emil User <emil.simeonov@tutanota.com>
2026-08-09 00:59:55 +00:00

332 lines
13 KiB
Python

"""Unit tests for devx.cli module."""
from unittest.mock import MagicMock, patch
import click
import pytest
from click.testing import CliRunner
from devx.cli import cli
class TestCliGroups:
def test_cli_help(self) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "devx" in result.output
assert "ci" in result.output
assert "tools" in result.output
assert "molecule" in result.output
def test_ci_group_help(self) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "--help"])
assert result.exit_code == 0
assert "auto-merge" in result.output
assert "release" in result.output
assert "publish" in result.output
def test_tools_group_help(self) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "--help"])
assert result.exit_code == 0
assert "setup" in result.output
assert "install-tools" in result.output
assert "configure-repo" in result.output
def test_molecule_group_help(self) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["molecule", "--help"])
assert result.exit_code == 0
assert "distribute" in result.output
assert "all" in result.output
class TestCiCommands:
@patch("devx.cli._run_module")
def test_ci_auto_merge(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "auto-merge", "branch", "title", "repo", "1"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.auto_merge", ["branch", "title", "repo", "1"])
@patch("devx.cli._run_module")
def test_ci_check_translations(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "check-translations"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.check_translations", [])
@patch("devx.cli._run_module")
def test_ci_classify_changes(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "classify-changes", "positional"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.classify_changes", ["positional"])
@patch("devx.cli._run_module")
def test_ci_detect_release_commit(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "detect-release-commit"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.detect_release_commit", [])
@patch("devx.cli._run_module")
def test_ci_discover_runners(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "discover-runners", "positional"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.discover_runners", ["positional"])
@patch("devx.cli._run_module")
def test_ci_doc_coverage(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "doc-coverage"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.doc_coverage", [])
@patch("devx.cli._run_module")
def test_ci_lint_docs(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "lint-docs", "--", "--root", "."])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.lint_docs", ["--root", "."])
@patch("devx.cli._run_module")
def test_ci_notify_failure(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "notify-failure", "job1"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.notify_failure", ["job1"])
@patch("devx.cli._run_module")
def test_ci_post_merge(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "post-merge", "DEVX-1"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.post_merge", ["DEVX-1"])
@patch("devx.cli._run_module")
def test_ci_pr_review(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "pr-review", "42"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.pr_review", ["42"])
@patch("devx.cli._run_module")
def test_ci_publish(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "publish", "v1.0.0", "owner/repo"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.publish", ["v1.0.0", "owner/repo"])
@patch("devx.cli._run_module")
def test_ci_push_badges(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "push-badges"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.push_badges", [])
@patch("devx.cli._run_module")
def test_ci_release(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "release", "positional"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.release", ["positional"])
@patch("devx.cli._run_module")
def test_ci_sync_wiki(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "sync-wiki"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.sync_wiki", [])
@patch("devx.cli._run_module")
def test_ci_validate_commit_msg(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "validate-commit-msg", "msg"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.validate_commit_msg", ["msg"])
class TestToolsCommands:
@patch("devx.cli._run_module")
def test_tools_check_test_speed(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "check-test-speed"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.check_test_speed", [])
@patch("devx.cli._run_module")
def test_tools_configure_repo(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "configure-repo", "myrepo"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.configure_repo", ["myrepo"])
@patch("devx.cli._run_module")
def test_tools_generate_badges(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "generate-badges"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.generate_badges", [])
@patch("devx.cli._run_module")
def test_tools_generate_cliff_config(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "generate-cliff-config"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.generate_cliff_config", [])
@patch("devx.cli._run_module")
def test_tools_install_checkmake(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "install-checkmake"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.install_checkmake", [])
@patch("devx.cli._run_module")
def test_tools_install_tools(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "install-tools"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.install_tools", [])
@patch("devx.cli._run_module")
def test_tools_setup(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "setup"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.setup", [])
@patch("devx.cli._run_module")
def test_tools_rebase(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "rebase", "--", "--no-push"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.rebase", ["--no-push"])
@patch("devx.cli._run_module")
def test_tools_pr_rebase(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["tools", "pr-rebase", "--", "--pr", "42"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.tools.pr_rebase", ["--pr", "42"])
class TestMoleculeCommands:
@patch("devx.cli._run_module")
def test_molecule_distribute(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["molecule", "distribute", "positional"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.molecule.distribute_molecule", ["positional"])
@patch("devx.cli._run_module")
def test_molecule_discover_runners(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["molecule", "discover-runners"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.molecule.discover_runners", [])
@patch("devx.cli._run_module")
def test_molecule_all(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["molecule", "all"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.molecule.molecule_all", [])
class TestNewCiCommands:
@patch("devx.cli._run_module")
def test_ci_distribute_files(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "distribute-files", "--", "--pattern", "*.py"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.distribute_files", ["--pattern", "*.py"])
@patch("devx.cli._run_module")
def test_ci_integration_guard(self, mock_run: MagicMock) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["ci", "integration-guard", "--", "-v"])
assert result.exit_code == 0
mock_run.assert_called_once_with("devx.ci.integration_guard", ["-v"])
class TestRunModule:
@patch("importlib.import_module")
def test_run_module_success(self, mock_import: MagicMock) -> None:
from devx.cli import _run_module
mock_module = MagicMock()
mock_main = MagicMock()
mock_module.main = mock_main
mock_import.return_value = mock_module
_run_module("devx.ci.release", ["--dry-run"])
mock_import.assert_called_once_with("devx.ci.release")
mock_main.assert_called_once()
@patch("importlib.import_module")
def test_run_module_no_main_raises(self, mock_import: MagicMock) -> None:
from devx.cli import _run_module
mock_module = MagicMock()
mock_module.main = None
mock_import.return_value = mock_module
with pytest.raises(SystemExit) as exc_info:
_run_module("devx.ci.nonexistent", [])
assert exc_info.value.code == 1
@patch("importlib.import_module")
def test_run_module_click_exception(self, mock_import: MagicMock) -> None:
from devx.cli import _run_module
mock_module = MagicMock()
mock_main = MagicMock()
mock_main.side_effect = click.ClickException("test error")
mock_module.main = mock_main
mock_import.return_value = mock_module
with pytest.raises(SystemExit):
_run_module("devx.ci.release", [])
@patch("importlib.import_module")
def test_run_module_abort(self, mock_import: MagicMock) -> None:
from devx.cli import _run_module
mock_module = MagicMock()
mock_main = MagicMock()
mock_main.side_effect = click.exceptions.Abort()
mock_module.main = mock_main
mock_import.return_value = mock_module
with pytest.raises(SystemExit) as exc_info:
_run_module("devx.ci.release", [])
assert exc_info.value.code == 1
@patch("importlib.import_module")
def test_run_module_click_command_object(self, mock_import: MagicMock) -> None:
"""When main is not callable (a click Command), uses .main() method."""
from devx.cli import _run_module
mock_module = MagicMock()
# Create a non-callable object with a .main method (like click.Command)
# callable() returns False only if __call__ is not defined
class NonCallableMain:
def __init__(self) -> None:
self.main = MagicMock()
non_callable = NonCallableMain()
assert not callable(non_callable)
mock_module.main = non_callable
mock_import.return_value = mock_module
_run_module("devx.ci.release", ["arg1"])
non_callable.main.assert_called_once()
class TestVersionOption:
def test_version_flag(self) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["--version"])
assert result.exit_code == 0