"""Tests for devx.tools.generate_cliff_config.""" from __future__ import annotations import tomllib from pathlib import Path from unittest.mock import patch import pytest from click.testing import CliRunner from devx.tools.generate_cliff_config import main class TestGenerateCliffConfig: """Tests for the generate_cliff_config tool.""" @pytest.fixture def runner(self) -> CliRunner: return CliRunner() def test_generate_to_new_file(self, runner: CliRunner, tmp_path: Path) -> None: """Generate cliff.toml to a new file.""" output = tmp_path / "cliff.toml" result = runner.invoke(main, ["--prefix", "GRM", "--output", str(output)]) assert result.exit_code == 0 assert output.exists() content = output.read_text() assert "git-cliff configuration for GRM" in content assert 'pattern = "^GRM-\\\\d+:\\\\s+"' in content def test_generate_with_default_prefix(self, runner: CliRunner, tmp_path: Path) -> None: """Generate with default prefix (DEVX_TASK_PREFIX or 'DEVX').""" output = tmp_path / "cliff.toml" with patch("devx.tools.generate_cliff_config.TASK_PREFIX", "DEVX"): result = runner.invoke(main, ["--output", str(output)]) assert result.exit_code == 0 content = output.read_text() assert "git-cliff configuration for DEVX" in content def test_existing_file_without_force(self, runner: CliRunner, tmp_path: Path) -> None: """Refuse to overwrite existing file without --force.""" output = tmp_path / "cliff.toml" output.write_text("# existing") result = runner.invoke(main, ["--prefix", "GRM", "--output", str(output)]) assert result.exit_code != 0 assert "already exists" in result.output assert output.read_text() == "# existing" def test_existing_file_with_force(self, runner: CliRunner, tmp_path: Path) -> None: """Overwrite existing file with --force.""" output = tmp_path / "cliff.toml" output.write_text("# existing") result = runner.invoke(main, ["--prefix", "GRM", "--output", str(output), "--force"]) assert result.exit_code == 0 content = output.read_text() assert "git-cliff configuration for GRM" in content assert "# existing" not in content def test_generated_config_is_valid_toml(self, runner: CliRunner, tmp_path: Path) -> None: """Generated config must be valid TOML.""" output = tmp_path / "cliff.toml" result = runner.invoke(main, ["--prefix", "GRM", "--output", str(output)]) assert result.exit_code == 0 with open(output, "rb") as f: data = tomllib.load(f) assert "changelog" in data assert "git" in data assert "bump" in data assert data["bump"]["initial_tag"] == "0.1.0" assert data["bump"]["features_always_bump_minor"] is True def test_generated_config_has_correct_preprocessor(self, runner: CliRunner, tmp_path: Path) -> None: """Preprocessor pattern must match the given prefix.""" output = tmp_path / "cliff.toml" result = runner.invoke(main, ["--prefix", "INFRA", "--output", str(output)]) assert result.exit_code == 0 with open(output, "rb") as f: data = tomllib.load(f) preprocessors = data["git"]["commit_preprocessors"] assert len(preprocessors) == 1 pattern = preprocessors[0]["pattern"] assert "INFRA" in pattern def test_generated_config_has_commit_parsers(self, runner: CliRunner, tmp_path: Path) -> None: """Generated config must have all standard commit parsers.""" output = tmp_path / "cliff.toml" result = runner.invoke(main, ["--prefix", "GRM", "--output", str(output)]) assert result.exit_code == 0 with open(output, "rb") as f: data = tomllib.load(f) parsers = data["git"]["commit_parsers"] # Should have feat, fix, perf, refactor, doc, test, style, chore, ci, release, security, revert, catch-all messages = [p["message"] for p in parsers if "message" in p] assert "^feat" in messages assert "^fix" in messages assert "^perf" in messages assert "^refactor" in messages assert "^release:" in messages assert "^revert" in messages assert ".*" in messages # catch-all def test_default_output_path(self, runner: CliRunner, tmp_path: Path) -> None: """Default output path is cliff.toml in current directory.""" output = tmp_path / "cliff.toml" # Change to tmp_path so default cliff.toml is created there import os old_cwd = os.getcwd() os.chdir(tmp_path) try: result = runner.invoke(main, ["--prefix", "GRM"]) assert result.exit_code == 0 assert output.exists() finally: os.chdir(old_cwd) def test_success_message(self, runner: CliRunner, tmp_path: Path) -> None: """Success message includes file and prefix.""" output = tmp_path / "cliff.toml" result = runner.invoke(main, ["--prefix", "GRM", "--output", str(output)]) assert result.exit_code == 0 assert "Generated" in result.output assert "GRM" in result.output