"""Tests for conventional commit validation.""" import pytest from git_commit_ai.core.conventional import ( ConventionalCommitParser, ConventionalCommitFixer, validate_commit_message, format_conventional, extract_conventional_parts, ) class TestConventionalCommitParser: """Tests for ConventionalCommitParser.""" def test_parse_valid_message(self): message = "feat(auth): add user authentication" parsed = ConventionalCommitParser.parse(message) assert parsed is not None assert parsed.type == "feat" assert parsed.scope == "auth" assert parsed.description == "add user authentication" def test_parse_without_scope(self): message = "fix: resolve memory leak" parsed = ConventionalCommitParser.parse(message) assert parsed is not None assert parsed.type == "fix" assert parsed.scope is None def test_parse_invalid_message(self): message = "just a random message" parsed = ConventionalCommitParser.parse(message) assert parsed is None def test_is_valid(self): assert ConventionalCommitParser.is_valid("feat: new feature") is True assert ConventionalCommitParser.is_valid("invalid message") is False def test_validate_valid(self): errors = ConventionalCommitParser.validate("feat(auth): add login") assert len(errors) == 0 def test_validate_invalid_type(self): errors = ConventionalCommitParser.validate("invalid(scope): desc") assert len(errors) > 0 assert any("Invalid type" in e for e in errors) def test_validate_empty_message(self): errors = ConventionalCommitParser.validate("") assert len(errors) > 0 class TestConventionalCommitFixer: """Tests for ConventionalCommitFixer.""" def test_fix_simple_message(self): diff = """+++ b/src/auth.py @@ -1,3 +1,4 @@ +def login(): + pass """ fixed = ConventionalCommitFixer.fix("add login feature", diff) assert fixed.startswith("feat:") def test_fix_with_type_detection(self): diff = """--- a/src/bug.py +++ b/src/bug.py @@ -1,3 +1,4 @@ -def calculate(): +def calculate(): return 1 / 0 + return 1 / 1 """ fixed = ConventionalCommitFixer.fix("fix bug", diff) assert fixed.startswith("fix:") def test_fix_preserves_description(self): diff = """+++ b/src/auth.py @@ -1,3 +1,4 @@ +def login(): """ fixed = ConventionalCommitFixer.fix("add login functionality", diff) assert "login" in fixed.lower() class TestValidateCommitMessage: """Tests for validate_commit_message function.""" def test_validate_valid(self): is_valid, errors = validate_commit_message("feat(auth): add login") assert is_valid is True assert len(errors) == 0 def test_validate_invalid(self): is_valid, errors = validate_commit_message("invalid") assert is_valid is False assert len(errors) > 0 class TestFormatConventional: """Tests for format_conventional function.""" def test_format_with_type_and_scope(self): result = format_conventional("add login", "feat", "auth") assert result == "feat(auth): add login" def test_format_with_type_only(self): result = format_conventional("fix bug", "fix") assert result == "fix: fix bug" def test_format_already_formatted(self): result = format_conventional("feat(auth): add login", "feat", "auth") assert result == "feat(auth): add login" class TestExtractConventionalParts: """Tests for extract_conventional_parts function.""" def test_extract_all_parts(self): result = extract_conventional_parts("feat(auth): add login") assert result["type"] == "feat" assert result["scope"] == "auth" assert result["description"] == "add login" def test_extract_invalid_message(self): result = extract_conventional_parts("invalid message") assert result["type"] is None assert result["scope"] is None assert result["description"] == "invalid message"