8Unit tests for ``oct.git.conventional`` (Phase 4D — G-D1, G-D2, G-D5).
12- Verify Conventional Commits parsing (type, scope, subject, body, footer).
13- Verify commit message validation rules.
14- Verify validation error formatting.
15- Verify diagnostics impact analysis on diffs.
22 L3 — assertion details
27- All tests are pure unit tests (no filesystem or git needed).
38 analyse_diagnostics_impact,
39 format_validation_errors,
41 validate_commit_message,
53 c = parse_commit_message(
"feat: add login page")
55 assert c.type ==
"feat"
56 assert c.scope
is None
57 assert c.subject ==
"add login page"
58 assert c.breaking
is False
61 c = parse_commit_message(
"fix(auth): patch null pointer")
63 assert c.type ==
"fix"
64 assert c.scope ==
"auth"
65 assert c.subject ==
"patch null pointer"
68 c = parse_commit_message(
"docs(readme): update install section")
70 assert c.type ==
"docs"
71 assert c.scope ==
"readme"
72 assert c.subject ==
"update install section"
75 c = parse_commit_message(
"feat!: breaking change")
77 assert c.breaking
is True
80 msg =
"feat: add thing\n\nBREAKING CHANGE: old API removed"
81 c = parse_commit_message(msg)
83 assert c.breaking
is True
86 assert parse_commit_message(
"random garbage")
is None
89 assert parse_commit_message(
"")
is None
92 assert parse_commit_message(
" \n ")
is None
95 msg =
"fix: patch bug\n\nThis fixes the null pointer\nin the auth module."
96 c = parse_commit_message(msg)
98 assert c.subject ==
"patch bug"
99 assert "null pointer" in c.body
102 for t
in COMMIT_TYPES:
103 c = parse_commit_message(f
"{t}: do something")
104 assert c
is not None, f
"Failed to parse type '{t}'"
108 c = parse_commit_message(
"feat(core-git): add function")
110 assert c.scope ==
"core-git"
113 c = parse_commit_message(
"fix:patch bug")
115 assert c.subject ==
"patch bug"
118 c = parse_commit_message(
"fix: lots of spaces")
120 assert c.subject ==
"lots of spaces"
131 errors = validate_commit_message(
"feat(auth): add token validation")
135 errors = validate_commit_message(
"fix: patch bug")
139 errors = validate_commit_message(
"update: change stuff")
140 assert any(
"update" in e
and "not valid" in e
for e
in errors)
142 assert any(
"feat" in e
for e
in errors)
145 long_subject =
"a" * 80
146 errors = validate_commit_message(f
"feat: {long_subject}")
147 assert any(
"exceeds" in e
or "72" in e
for e
in errors)
150 errors = validate_commit_message(
"feat: Add something")
151 assert any(
"lowercase" in e
for e
in errors)
154 errors = validate_commit_message(
"feat: add something.")
155 assert any(
"period" in e
for e
in errors)
158 errors = validate_commit_message(
"")
159 assert len(errors) > 0
160 assert any(
"empty" in e.lower()
for e
in errors)
163 errors = validate_commit_message(
"just a plain message")
164 assert len(errors) > 0
165 assert any(
"Conventional Commits" in e
for e
in errors)
168 errors = validate_commit_message(
"update: Add something.")
170 assert len(errors) >= 2
175 errors = validate_commit_message(f
"feat: {subject}")
176 assert not any(
"exceeds" in e
or "72" in e
for e
in errors)
179 errors = validate_commit_message(
"sec: patch vulnerability")
183 errors = validate_commit_message(
"feat!: breaking api change")
195 result = format_validation_errors([
"Type is invalid."])
196 assert "Commit message validation failed:" in result
197 assert "- Type is invalid." in result
198 assert "Example:" in result
201 errors = [
"Error one.",
"Error two."]
202 result = format_validation_errors(errors)
203 assert "- Error one." in result
204 assert "- Error two." in result
207 result = format_validation_errors([
"bad"])
208 assert "fix(auth):" in result
219 diff = textwrap.dedent(
"""\
223 + _dbg("GIT", func, "starting", 2)
225 impact = analyse_diagnostics_impact(diff)
226 assert any(
"+ Added: GIT" in line
for line
in impact)
229 diff = textwrap.dedent(
"""\
233 - _dbg("AUTH", func, "checking", 3)
235 impact = analyse_diagnostics_impact(diff)
236 assert any(
"- Removed: AUTH" in line
for line
in impact)
239 diff = textwrap.dedent(
"""\
243 - _dbg("AUTH", func, "old", 2)
244 + _dbg("AUTH", func, "new", 3)
246 impact = analyse_diagnostics_impact(diff)
247 assert any(
"~ Modified: AUTH" in line
for line
in impact)
250 diff = textwrap.dedent(
"""\
257 impact = analyse_diagnostics_impact(diff)
261 assert analyse_diagnostics_impact(
"") == []
264 diff = textwrap.dedent(
"""\
265 + _dbg("GIT", func, "new", 2)
266 - _dbg("CACHE", func, "old", 1)
268 impact = analyse_diagnostics_impact(diff)
269 assert any(
"+ Added: GIT" in line
for line
in impact)
270 assert any(
"- Removed: CACHE" in line
for line
in impact)
273 impact = analyse_diagnostics_impact(
"not a real diff\ngarbage\n+++---")
274 assert isinstance(impact, list)
283 """Verify _FOOTER_RE is defined at module scope and not recompiled per call."""
286 from oct.git import conventional
287 assert hasattr(conventional,
"_FOOTER_RE")
289 assert isinstance(conventional._FOOTER_RE, _re.Pattern)
292 from oct.git import conventional
293 before = conventional._FOOTER_RE
294 parse_commit_message(
"feat: first call\n\nbody\n\nRefs: #1")
295 parse_commit_message(
"fix: second call\n\nSigned-off-by: x <x@y>")
296 after = conventional._FOOTER_RE
297 assert before
is after
test_no_dbg_changes(self)
test_modified_domain(self)
test_malformed_diff_no_crash(self)
test_removed_domain(self)
test_scope_with_hyphen(self)
test_multiline_body(self)
test_colon_spacing_flexible(self)
test_extra_space_after_colon(self)
test_docs_with_scope(self)
test_empty_returns_none(self)
test_all_valid_types_parse(self)
test_breaking_footer(self)
test_garbage_returns_none(self)
test_whitespace_only_returns_none(self)
test_multiple_errors(self)
test_breaking_valid(self)
test_sec_type_valid(self)
test_trailing_period(self)
test_subject_too_long(self)
test_exactly_72_chars_ok(self)
test_not_conventional_format(self)
test_uppercase_first_char(self)