8Unit tests for :mod:`oct.git.policy` — the profile resolution and
9exit-code derivation layer for ``oct git check`` (Phase 4B / G-B4).
13- Verify profile resolution precedence: protected branch > CLI >
14 ``.octrc.json`` > default.
15- Verify the :data:`~oct.git.policy.PROFILE_MATRIX` rows against the
16 blueprint §5.3 / §7.1 specification, byte for byte.
17- Verify the exit-code table 0..5 with every precedence permutation,
18 including the "secrets always blocks" invariant.
22Domain: GIT (pure unit tests; logging exercised only incidentally)
25 L3 — assertion details
30- Every test is hermetic — no filesystem access, no subprocess, no git.
31- ``QualityGateResult`` stand-ins are built from :class:`types.SimpleNamespace`
32 so these tests do not depend on :mod:`oct.git.quality_gate` being
33 implemented yet (it lands later in the same phase).
36from types
import SimpleNamespace
42 DEFAULT_PROTECTED_BRANCHES,
44 SECRETS_ALWAYS_BLOCKS,
48 resolve_effective_profile,
59 lint_violations: int = 0,
60 format_violations: int = 0,
61 secrets_findings: list |
None =
None,
62 test_failures: int = 0,
64 """Return a minimal stand-in for ``QualityGateResult``.
66 ``apply_profile_policy`` only reads the four attributes set here,
67 so a SimpleNamespace is sufficient. Using SimpleNamespace lets the
68 tests run before :mod:`oct.git.quality_gate` exists.
70 return SimpleNamespace(
71 lint_violations=lint_violations,
72 format_violations=format_violations,
73 secrets_findings=list(secrets_findings
or []),
74 test_failures=test_failures,
85 assert set(PROFILE_MATRIX.keys()) == {
"proto",
"compact",
"strict"}
88 assert set(VALID_PROFILES) == set(PROFILE_MATRIX.keys())
91 assert SECRETS_ALWAYS_BLOCKS
is True
96 for profile, row
in PROFILE_MATRIX.items():
97 assert row[
"secrets"] ==
"blocking", (
98 f
"profile {profile} must have secrets=blocking"
103 row = PROFILE_MATRIX[
"proto"]
104 assert row[
"lint"] ==
"warn"
105 assert row[
"format"] ==
"skip"
106 assert row[
"secrets"] ==
"blocking"
107 assert row[
"tests"] ==
"skip"
110 row = PROFILE_MATRIX[
"compact"]
111 assert row[
"lint"] ==
"blocking"
112 assert row[
"format"] ==
"blocking"
113 assert row[
"secrets"] ==
"blocking"
114 assert row[
"tests"] ==
"optional"
117 row = PROFILE_MATRIX[
"strict"]
118 assert row[
"lint"] ==
"blocking"
119 assert row[
"format"] ==
"blocking"
120 assert row[
"secrets"] ==
"blocking"
121 assert row[
"tests"] ==
"blocking"
125 assert DEFAULT_PROFILE ==
"compact"
129 assert DEFAULT_PROTECTED_BRANCHES == (
"main",
"master")
139 assert is_protected_branch(
"main",
None)
is True
142 assert is_protected_branch(
"master",
None)
is True
145 assert is_protected_branch(
"feature/xyz",
None)
is False
148 assert is_protected_branch(
None,
None)
is False
151 assert is_protected_branch(
"",
None)
is False
155 assert is_protected_branch(
"HEAD",
None)
is False
158 octrc = {
"git": {
"protected_branches": [
"release/current"]}}
159 assert is_protected_branch(
"release/current", octrc)
is True
161 assert is_protected_branch(
"main", octrc)
is False
165 octrc = {
"git": {
"protected_branches":
"not-a-list"}}
166 assert is_protected_branch(
"main", octrc)
is True
169 octrc = {
"git": {
"protected_branches": [
"main", 42,
None,
"dev"]}}
170 assert is_protected_branch(
"main", octrc)
is True
171 assert is_protected_branch(
"dev", octrc)
is True
172 assert is_protected_branch(
"feature/x", octrc)
is False
183 assert resolve_effective_profile(
"main",
None,
"proto") ==
"strict"
186 octrc = {
"git": {
"pre_commit_profile":
"proto"}}
187 assert resolve_effective_profile(
"main", octrc,
None) ==
"strict"
190 octrc = {
"git": {
"pre_commit_profile":
"proto"}}
191 assert resolve_effective_profile(
"feature/x", octrc,
"strict") ==
"strict"
194 octrc = {
"git": {
"pre_commit_profile":
"proto"}}
196 assert resolve_effective_profile(
"feature/x", octrc,
"banana") ==
"proto"
199 octrc = {
"git": {
"pre_commit_profile":
"strict"}}
200 assert resolve_effective_profile(
"feature/x", octrc,
None) ==
"strict"
203 octrc = {
"linter": {
"profile":
"proto"}}
204 assert resolve_effective_profile(
"feature/x", octrc,
None) ==
"proto"
208 "git": {
"pre_commit_profile":
"strict"},
209 "linter": {
"profile":
"proto"},
211 assert resolve_effective_profile(
"feature/x", octrc,
None) ==
"strict"
214 assert resolve_effective_profile(
"feature/x",
None,
None) == DEFAULT_PROFILE
217 assert resolve_effective_profile(
"feature/x", {},
None) == DEFAULT_PROFILE
220 octrc = {
"linter": {
"profile":
"not-a-profile"}}
221 assert resolve_effective_profile(
"feature/x", octrc,
None) == DEFAULT_PROFILE
225 assert resolve_effective_profile(
"HEAD",
None,
"strict") ==
"strict"
228 assert resolve_effective_profile(
None,
None,
"proto") ==
"proto"
238 assert apply_profile_policy(
"compact",
_gate_result()) == 0
241 for profile
in VALID_PROFILES:
242 assert apply_profile_policy(profile,
_gate_result()) == 0
245 assert apply_profile_policy(
"nonsense",
_gate_result()) == 0
256 assert apply_profile_policy(
"compact", r) == 1
260 assert apply_profile_policy(
"strict", r) == 1
265 assert apply_profile_policy(
"proto", r) == 0
276 assert apply_profile_policy(
"compact", r) == 2
280 assert apply_profile_policy(
"strict", r) == 2
285 assert apply_profile_policy(
"proto", r) == 0
295 r =
_gate_result(lint_violations=1, format_violations=1)
296 assert apply_profile_policy(
"compact", r) == 3
299 r =
_gate_result(lint_violations=10, format_violations=3)
300 assert apply_profile_policy(
"strict", r) == 3
304 r =
_gate_result(lint_violations=4, format_violations=4)
305 assert apply_profile_policy(
"proto", r) == 0
316 assert apply_profile_policy(
"strict", r) == 4
323 assert apply_profile_policy(
"compact", r) == 0
328 assert apply_profile_policy(
"proto", r) == 0
337 assert apply_profile_policy(
"strict", r) == 4
347 r =
_gate_result(secrets_findings=[(
"f.py", 1,
"password")])
348 assert apply_profile_policy(
"strict", r) == 5
351 r =
_gate_result(secrets_findings=[(
"f.py", 1,
"password")])
352 assert apply_profile_policy(
"compact", r) == 5
356 r =
_gate_result(secrets_findings=[(
"f.py", 1,
"password")])
357 assert apply_profile_policy(
"proto", r) == 5
362 secrets_findings=[(
"f.py", 1,
"password")],
364 assert apply_profile_policy(
"compact", r) == 5
369 secrets_findings=[(
"f.py", 1,
"password")],
371 assert apply_profile_policy(
"compact", r) == 5
377 secrets_findings=[(
"f.py", 1,
"password")],
379 assert apply_profile_policy(
"compact", r) == 5
384 secrets_findings=[(
"f.py", 1,
"password")],
386 assert apply_profile_policy(
"strict", r) == 5
394 (
"f.py", 1,
"password"),
395 (
"g.py", 2,
"api_key"),
398 assert apply_profile_policy(
"strict", r) == 5
test_secrets_beats_everything(self)
test_secret_finding_under_compact(self)
test_secret_finding_under_proto_still_blocks(self)
test_secrets_beats_lint(self)
test_secrets_beats_tests(self)
test_secret_finding_under_strict(self)
test_secrets_beats_lint_and_format(self)
test_secrets_beats_format(self)
test_test_failures_under_strict(self)
test_tests_precedence_over_lint_format(self)
test_test_failures_under_proto_skipped(self)
test_test_failures_under_compact_optional_are_non_blocking(self)
test_lint_only_under_compact(self)
test_lint_only_under_strict(self)
test_lint_under_proto_is_warn_not_blocking(self)
test_lint_and_format_under_proto_collapse_to_zero(self)
test_lint_and_format_under_compact(self)
test_lint_and_format_under_strict(self)
test_format_under_proto_is_skipped_not_blocking(self)
test_format_only_under_compact(self)
test_format_only_under_strict(self)
test_clean_result_returns_zero(self)
test_zero_violations_under_every_profile(self)
test_unknown_profile_degrades_to_default(self)
test_main_is_protected_by_default(self)
test_detached_head_literal_not_protected(self)
test_malformed_octrc_falls_back_to_defaults(self)
test_custom_protected_list_from_octrc(self)
test_none_branch_not_protected(self)
test_master_is_protected_by_default(self)
test_feature_branch_not_protected(self)
test_non_string_entries_are_filtered(self)
test_empty_branch_not_protected(self)
test_matrix_has_three_canonical_profiles(self)
test_compact_row_matches_blueprint(self)
test_secrets_always_blocks_invariant_flag(self)
test_default_profile_is_compact(self)
test_default_protected_branches(self)
test_proto_row_matches_blueprint(self)
test_strict_row_matches_blueprint(self)
test_valid_profiles_tuple_matches_matrix(self)
test_secrets_row_is_blocking_under_every_profile(self)
test_linter_profile_from_octrc_fallback(self)
test_none_branch_with_cli_override(self)
test_detached_head_with_cli_override(self)
test_invalid_linter_profile_is_ignored(self)
test_protected_branch_forces_strict(self)
test_git_pre_commit_profile_from_octrc(self)
test_git_config_takes_priority_over_linter_config(self)
test_cli_unknown_value_falls_through(self)
test_no_octrc_returns_default(self)
test_empty_octrc_returns_default(self)
test_cli_override_beats_octrc(self)
test_protected_branch_beats_octrc_default(self)
SimpleNamespace _gate_result(*, int lint_violations=0, int format_violations=0, list|None secrets_findings=None, int test_failures=0)