Option C Tools
Loading...
Searching...
No Matches
test_pre_commit_checks.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_git/test_pre_commit_checks.py
4
5"""
6Purpose
7-------
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.
13
14Responsibilities
15----------------
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.
21
22Diagnostics
23-----------
24Domain: GIT-TESTS
25Levels:
26 L2 — test lifecycle
27 L3 — assertion details
28 L4 — deep tracing
29
30Contracts
31---------
32- Tests create a real git repository in ``tmp_path`` via subprocess;
33 no global git state is modified.
34"""
35
36from __future__ import annotations
37
38import subprocess
39from pathlib import Path
40
41from oct.git.quality_gate import PreCommitResult, pre_commit_checks
42
43
44def _git(repo: Path, *args: str) -> subprocess.CompletedProcess:
45 return subprocess.run(
46 ["git", *args], cwd=repo, check=True, capture_output=True, text=True,
47 )
48
49
50def test_invalid_commit_message_yields_exit_3(temp_git_project, make_compliant_py):
51 make_compliant_py("src/good.py")
52 _git(temp_git_project, "add", "src/good.py")
53
54 result = pre_commit_checks(
55 project_root=temp_git_project,
56 branch="feature/x",
57 message="no type prefix",
58 force_commit=False,
59 profile="compact",
60 octrc={"linter": {"profile": "compact"}},
61 )
62
63 assert isinstance(result, PreCommitResult)
64 assert result.ok is False
65 assert result.exit_code == 3
66 assert result.errors
67 assert result.staged_files == []
68
69
70def test_force_commit_bypasses_message_validation(temp_git_project, make_compliant_py):
71 make_compliant_py("src/good.py")
72 _git(temp_git_project, "add", "src/good.py")
73
74 result = pre_commit_checks(
75 project_root=temp_git_project,
76 branch="feature/x",
77 message="still bad",
78 force_commit=True,
79 profile="compact",
80 octrc={"linter": {"profile": "compact"}},
81 )
82
83 assert result.ok is True
84 assert result.exit_code == 0
85 assert len(result.staged_files) >= 1
86
87
89 result = pre_commit_checks(
90 project_root=temp_git_project,
91 branch="feature/x",
92 message="feat: add widget",
93 force_commit=False,
94 profile="compact",
95 octrc={"linter": {"profile": "compact"}},
96 )
97
98 assert result.ok is False
99 assert result.exit_code == 4
100 assert "Nothing to commit." in result.errors
101
102
104 temp_git_project, make_compliant_py
105):
106 make_compliant_py("src/good.py")
107 _git(temp_git_project, "add", "src/good.py")
108 octrc = {
109 "linter": {"profile": "compact"},
110 "git": {
111 "protected_branches": ["main"],
112 "branch_profile_map": {"main": "strict"},
113 },
114 }
115
116 result = pre_commit_checks(
117 project_root=temp_git_project,
118 branch="main",
119 message="feat: add widget",
120 force_commit=False,
121 profile="strict",
122 octrc=octrc,
123 )
124
125 assert result.ok is False
126 assert result.exit_code == 1
127 assert result.pb_advisory != ""
128 assert result.errors
129
130
132 temp_git_project, make_compliant_py
133):
134 make_compliant_py("src/good.py")
135 _git(temp_git_project, "add", "src/good.py")
136
137 result = pre_commit_checks(
138 project_root=temp_git_project,
139 branch="feature/x",
140 message="feat(widget): add new thing",
141 force_commit=False,
142 profile="compact",
143 octrc={"linter": {"profile": "compact"}},
144 )
145
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_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)