-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_commit_msg.py
More file actions
138 lines (110 loc) · 4.83 KB
/
Copy pathtest_commit_msg.py
File metadata and controls
138 lines (110 loc) · 4.83 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import pytest
from click.testing import CliRunner
from afterpython.cli.commands.commit_msg import commit_msg
COMMIT_TYPES = {"feat": "A new feature", "fix": "A bug fix", "wip": "Work in progress"}
@pytest.fixture(autouse=True)
def fake_afterpython_toml(monkeypatch):
import tomlkit
doc = tomlkit.parse(tomlkit.dumps({"commit": {"types": COMMIT_TYPES}}))
monkeypatch.setattr("afterpython.tools._afterpython.read_afterpython", lambda: doc)
def run(tmp_path, message: str):
msg_file = tmp_path / "COMMIT_EDITMSG"
msg_file.write_text(message, encoding="utf-8")
return CliRunner().invoke(commit_msg, [str(msg_file)])
@pytest.mark.parametrize(
"message",
[
"feat: add X",
"fix(cli): handle Y",
"feat!: drop Z",
"feat(api)!: drop Z",
"wip: custom type from config",
"feat: subject\n\nA longer body\nover several lines",
# comments written by git's template/editor are ignored
"# Please enter the commit message\n\nfix: after comments",
# git-generated messages
"Merge branch 'dev' into main",
'Revert "feat: add X"',
"fixup! feat: add X",
"squash! feat: add X",
"amend! feat: add X",
# empty message: git rejects it itself
"",
"# only comments\n",
],
)
def test_accepted(tmp_path, message):
result = run(tmp_path, message)
assert result.exit_code == 0, result.output
@pytest.mark.parametrize(
"message, reason",
[
("chore: not in config", "unknown commit type 'chore'"),
("docs: not in config", "unknown commit type 'docs'"),
("add X", "doesn't follow the format"),
("feat:missing space", "doesn't follow the format"),
("feat: ", "doesn't follow the format"),
("Feat: uppercase type", "doesn't follow the format"),
# the diff shown by `git commit -v` is not part of the message
(
"bad message\n# ------------------------ >8 ------------------------\nfeat: x",
"doesn't follow the format",
),
],
)
def test_rejected(tmp_path, message, reason):
result = run(tmp_path, message)
assert result.exit_code == 1
assert reason in result.output
assert "SKIP=ap-commit git commit" in result.output
def test_error_lists_allowed_types(tmp_path):
result = run(tmp_path, "nope")
for t, desc in COMMIT_TYPES.items():
assert t in result.output and desc in result.output
@pytest.mark.parametrize("toml", ["", "[commit.types]\n"])
def test_no_commit_types_skips_check(tmp_path, monkeypatch, toml):
import tomlkit
monkeypatch.setattr(
"afterpython.tools._afterpython.read_afterpython",
lambda: tomlkit.parse(toml),
)
result = run(tmp_path, "anything goes")
assert result.exit_code == 0, result.output
assert result.output == ""
GIT_DEFAULT_MESSAGE = (
"\n# Please enter the commit message for your changes. Lines starting\n"
"# with '#' will be ignored, and an empty message aborts the commit.\n"
)
def run_template(tmp_path, message: str, source: str | None = None):
msg_file = tmp_path / "COMMIT_EDITMSG"
msg_file.write_text(message, encoding="utf-8")
env = {"PRE_COMMIT_COMMIT_MSG_SOURCE": source} if source else {}
result = CliRunner().invoke(commit_msg, [str(msg_file), "--template"], env=env)
return result, msg_file.read_text(encoding="utf-8")
def test_template_added_for_plain_commit(tmp_path):
result, text = run_template(tmp_path, GIT_DEFAULT_MESSAGE)
assert result.exit_code == 0, result.output
lines = text.splitlines()
# subject line stays empty, template comes before git's own comments
assert lines[0] == ""
assert all(line.startswith("#") for line in lines[1:])
for t, desc in COMMIT_TYPES.items():
assert any(t in line and desc in line for line in lines)
assert text.index("[commit.types]") < text.index("Please enter the commit message")
assert "SKIP=ap-commit" in text
def test_template_keeps_user_template_text(tmp_path):
# e.g. text coming from git's own commit.template
result, text = run_template(tmp_path, "feat: \n" + GIT_DEFAULT_MESSAGE, "template")
assert result.exit_code == 0, result.output
assert text.startswith("feat: \n\n# Format:")
@pytest.mark.parametrize("source", ["message", "merge", "squash", "commit"])
def test_template_skipped_when_message_given(tmp_path, source):
original = "feat: from -m\n"
result, text = run_template(tmp_path, original, source)
assert result.exit_code == 0, result.output
assert text == original
def test_template_never_breaks_the_check(tmp_path):
# the template itself is all comments, so an untouched template is an empty message
_, text = run_template(tmp_path, GIT_DEFAULT_MESSAGE)
assert run(tmp_path, text).exit_code == 0
assert run(tmp_path, "feat: add X\n" + text.lstrip("\n")).exit_code == 0