8OI-527 regression coverage — :func:`oct.git.quality_gate.pre_commit_checks`
9replaces the four sequential early-return blocks that previously lived
10in ``git_commit_cmd`` with a single :class:`PreCommitResult` dataclass.
11These tests pin the contract: the helper surfaces the same exit codes
12(3 / 4 / 1) and the same advisory messages the CLI used to emit inline.
16- Invalid commit message → ``ok=False``, ``exit_code=3``.
17- ``force_commit=True`` bypasses the commit-message check.
18- No staged files → ``ok=False``, ``exit_code=4``.
19- Protected-branch block → ``ok=False``, ``exit_code=1`` with advisory.
20- Happy path → ``ok=True``, ``staged_files`` populated, exit_code 0.
27 L3 — assertion details
32- Tests create a real git repository in ``tmp_path`` via subprocess;
33 no global git state is modified.
36from __future__
import annotations
39from pathlib
import Path
44def _git(repo: Path, *args: str) -> subprocess.CompletedProcess:
45 return subprocess.run(
46 [
"git", *args], cwd=repo, check=
True, capture_output=
True, text=
True,
51 make_compliant_py(
"src/good.py")
52 _git(temp_git_project,
"add",
"src/good.py")
54 result = pre_commit_checks(
55 project_root=temp_git_project,
57 message=
"no type prefix",
60 octrc={
"linter": {
"profile":
"compact"}},
63 assert isinstance(result, PreCommitResult)
64 assert result.ok
is False
65 assert result.exit_code == 3
67 assert result.staged_files == []
71 make_compliant_py(
"src/good.py")
72 _git(temp_git_project,
"add",
"src/good.py")
74 result = pre_commit_checks(
75 project_root=temp_git_project,
80 octrc={
"linter": {
"profile":
"compact"}},
83 assert result.ok
is True
84 assert result.exit_code == 0
85 assert len(result.staged_files) >= 1
89 result = pre_commit_checks(
90 project_root=temp_git_project,
92 message=
"feat: add widget",
95 octrc={
"linter": {
"profile":
"compact"}},
98 assert result.ok
is False
99 assert result.exit_code == 4
100 assert "Nothing to commit." in result.errors
104 temp_git_project, make_compliant_py
106 make_compliant_py(
"src/good.py")
107 _git(temp_git_project,
"add",
"src/good.py")
109 "linter": {
"profile":
"compact"},
111 "protected_branches": [
"main"],
112 "branch_profile_map": {
"main":
"strict"},
116 result = pre_commit_checks(
117 project_root=temp_git_project,
119 message=
"feat: add widget",
125 assert result.ok
is False
126 assert result.exit_code == 1
127 assert result.pb_advisory !=
""
132 temp_git_project, make_compliant_py
134 make_compliant_py(
"src/good.py")
135 _git(temp_git_project,
"add",
"src/good.py")
137 result = pre_commit_checks(
138 project_root=temp_git_project,
140 message=
"feat(widget): add new thing",
143 octrc={
"linter": {
"profile":
"compact"}},
146 assert result.ok
is True
147 assert result.exit_code == 0
148 assert result.errors == []
149 assert result.effective_profile ==
"compact"
150 assert any(p.name ==
"good.py" for p
in result.staged_files)
test_no_staged_files_yields_exit_4(temp_git_project)
test_force_commit_bypasses_message_validation(temp_git_project, make_compliant_py)
test_invalid_commit_message_yields_exit_3(temp_git_project, make_compliant_py)
test_protected_branch_block_yields_exit_1_with_advisory(temp_git_project, make_compliant_py)
subprocess.CompletedProcess _git(Path repo, *str args)
test_happy_path_returns_staged_files_and_profile(temp_git_project, make_compliant_py)