Repository navigation
Expand file tree
/
Copy pathgitlint_rules_test.py
More file actions
123 lines (88 loc) · 3.18 KB
/
Copy pathgitlint_rules_test.py
File metadata and controls
123 lines (88 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Tests for the ForbiddenTypeScope gitlint rule and commit-type config sync."""
import configparser
import re
from pathlib import Path
import pytest
import yaml
from gitlint.rules import RuleViolation
from gitlint_rules.forbidden_type_scope import ForbiddenTypeScope
class FakeCommit:
"""Minimal stand-in for a gitlint commit object."""
def __init__(self, title):
self.message = type("msg", (), {"title": title})()
def run_rule(title):
"""Run ForbiddenTypeScope against a commit title and return violations."""
rule = ForbiddenTypeScope()
commit = FakeCommit(title)
return rule.validate(commit)
# --- should be rejected ---
@pytest.mark.parametrize(
"title",
[
"fix(ci): update workflow",
"feat(ci): add new job",
"fix(e2e): repair test",
"feat(e2e): add new test",
],
)
def test_rejects_forbidden_combinations(title):
violations = run_rule(title)
assert violations, f"Expected violation for {title!r}"
assert len(violations) == 1
assert isinstance(violations[0], RuleViolation)
def test_fix_ci_suggests_ci_subsystem():
violations = run_rule("fix(ci): update workflow")
assert "ci(<subsystem>)" in violations[0].message.lower()
def test_feat_e2e_suggests_ci_e2e():
violations = run_rule("feat(e2e): add new test")
assert "ci(e2e)" in violations[0].message.lower()
# --- should be allowed ---
@pytest.mark.parametrize(
"title",
[
"ci(lint): update linter config",
"ci(e2e): fix flaky test",
"fix(mint): correct token refresh",
"feat(review-agent): add outcome labels",
"chore(ci): bump action version",
"test(e2e): add new scenario",
"refactor(ci): simplify matrix",
"docs: update readme",
"fix(#123): handle nil pointer",
],
)
def test_allows_valid_combinations(title):
violations = run_rule(title)
assert not violations, f"Unexpected violation for {title!r}: {violations}"
# --- should not crash on non-conventional titles ---
@pytest.mark.parametrize(
"title",
[
"just a plain message",
"WIP",
"",
],
)
def test_ignores_non_conventional(title):
violations = run_rule(title)
assert not violations
# --- .gitlint, COMMITS.md and .goreleaser.yml must agree on commit types ---
ROOT = Path(__file__).parent
def gitlint_types():
cfg = configparser.ConfigParser()
cfg.read(ROOT / ".gitlint")
return set(cfg["contrib-title-conventional-commits"]["types"].split(","))
def commits_md_rows():
"""Map type -> 'Appears in release notes?' cell from the COMMITS.md types table."""
rows = re.findall(
r"^\| `(\w+)` \| [^|]+ \| ([^|]+) \|$", (ROOT / "COMMITS.md").read_text(), re.M
)
return {t: notes.strip() for t, notes in rows}
def test_gitlint_types_match_commits_md():
assert gitlint_types() == set(commits_md_rows())
@pytest.mark.parametrize(
"ctype", sorted(t for t, notes in commits_md_rows().items() if notes == "No")
)
def test_hidden_types_excluded_from_release_notes(ctype):
goreleaser = yaml.safe_load((ROOT / ".goreleaser.yml").read_text())
assert f"^{ctype}(\\(.*\\))?:" in goreleaser["changelog"]["filters"]["exclude"]