8Integration tests for ``oct git status`` (Phase 4B / G-B2).
9Validates terminal output, ``--json`` schema, ``--no-check`` mode,
10and edge cases (detached HEAD, no remote, clean repo).
14- Verify ``[OK]`` / ``[!!]`` / ``---`` badge rendering for staged,
15 modified, and untracked files.
16- Verify ``--json`` output is valid JSON with the expected schema.
17- Verify ``--no-check`` suppresses compliance annotations.
18- Verify clean repo produces empty sections.
19- Verify detached HEAD and no-remote edge cases.
26 L3 — assertion details
31- Every test uses the ``temp_git_project`` fixture (real git repo).
32- Tests never modify the system git config.
37from pathlib
import Path
41from tests.tests_git.conftest
import _git, commit_file
49def _run_status(root: Path, *extra_args: str) -> subprocess.CompletedProcess:
50 """Run ``oct git status`` in a subprocess and return the result."""
52 return subprocess.run(
53 [sys.executable,
"-m",
"oct.cli",
"git",
"status", *extra_args],
68 assert result.returncode == 0
69 assert "On branch main" in result.stdout
73 assert result.returncode == 0
75 assert "Staged" not in result.stdout
76 assert "Modified" not in result.stdout
80 assert result.returncode == 0
81 data = json.loads(result.stdout)
82 assert data[
"branch"] ==
"main"
83 assert data[
"staged"] == []
84 assert data[
"modified"] == []
85 assert data[
"untracked"] == []
95 self, temp_git_project, make_compliant_py
97 path = make_compliant_py(
"src/good.py")
98 _git(temp_git_project,
"add",
"src/good.py")
101 assert result.returncode == 0
102 assert "[OK]" in result.stdout
103 assert "src/good.py" in result.stdout
106 self, temp_git_project, make_noncompliant_py
108 make_noncompliant_py(
"src/bad.py",
"missing_header")
109 _git(temp_git_project,
"add",
"src/bad.py")
112 assert result.returncode == 0
113 assert "[!!]" in result.stdout
114 assert "src/bad.py" in result.stdout
117 readme = temp_git_project /
"src" /
"notes.txt"
118 readme.write_text(
"hello", encoding=
"utf-8")
119 _git(temp_git_project,
"add",
"src/notes.txt")
122 assert result.returncode == 0
123 assert "---" in result.stdout
124 assert "notes.txt" in result.stdout
127 self, temp_git_project, make_compliant_py
130 path = make_compliant_py(
"src/modme.py")
132 temp_git_project,
"src/modme.py",
133 path.read_text(encoding=
"utf-8"),
"add modme",
136 path.read_text(encoding=
"utf-8") +
"\n# modified\n",
141 assert result.returncode == 0
142 assert "Modified" in result.stdout
143 assert "modme.py" in result.stdout
153 self, temp_git_project, make_noncompliant_py
155 make_noncompliant_py(
"src/bad.py",
"missing_header")
156 _git(temp_git_project,
"add",
"src/bad.py")
158 result =
_run_status(temp_git_project,
"--no-check")
159 assert result.returncode == 0
160 assert "[!!]" not in result.stdout
161 assert "[OK]" not in result.stdout
162 assert "bad.py" in result.stdout
172 make_compliant_py(
"src/a.py")
173 _git(temp_git_project,
"add",
"src/a.py")
176 assert result.returncode == 0
177 data = json.loads(result.stdout)
180 for key
in (
"branch",
"detached",
"ahead",
"behind",
181 "staged",
"modified",
"untracked"):
182 assert key
in data, f
"missing key: {key}"
185 assert len(data[
"staged"]) == 1
186 entry = data[
"staged"][0]
187 assert "path" in entry
188 assert "status" in entry
189 assert "issues" in entry
190 assert entry[
"status"]
in (
"ok",
"violations",
"non_python",
"unchecked")
193 self, temp_git_project, make_noncompliant_py
195 make_noncompliant_py(
"src/bad.py",
"missing_header")
196 _git(temp_git_project,
"add",
"src/bad.py")
199 data = json.loads(result.stdout)
200 entry = data[
"staged"][0]
201 assert entry[
"status"] ==
"violations"
202 assert len(entry[
"issues"]) > 0
205 make_compliant_py(
"src/a.py")
206 _git(temp_git_project,
"add",
"src/a.py")
208 result =
_run_status(temp_git_project,
"--json",
"--no-check")
209 data = json.loads(result.stdout)
210 entry = data[
"staged"][0]
211 assert entry[
"status"] ==
"unchecked"
221 sha = _git(temp_git_project,
"rev-parse",
"HEAD").stdout.strip()
222 _git(temp_git_project,
"checkout",
"--detach", sha)
225 assert result.returncode == 0
226 assert "HEAD detached" in result.stdout
229 sha = _git(temp_git_project,
"rev-parse",
"HEAD").stdout.strip()
230 _git(temp_git_project,
"checkout",
"--detach", sha)
233 data = json.loads(result.stdout)
234 assert data[
"detached"]
is True
235 assert data[
"branch"]
is None
240 data = json.loads(result.stdout)
241 assert data[
"ahead"] == 0
242 assert data[
"behind"] == 0
245 (temp_git_project /
"stray.txt").write_text(
"x", encoding=
"utf-8")
248 data = json.loads(result.stdout)
249 paths = [e[
"path"]
for e
in data[
"untracked"]]
250 assert "stray.txt" in paths
test_staged_compliant_py_shows_ok(self, temp_git_project, make_compliant_py)
test_staged_non_python_shows_dash(self, temp_git_project)
test_modified_file_shows_badge(self, temp_git_project, make_compliant_py)
test_staged_noncompliant_py_shows_violation(self, temp_git_project, make_noncompliant_py)
test_clean_repo_json(self, temp_git_project)
test_clean_repo_shows_branch(self, temp_git_project)
test_clean_repo_no_files(self, temp_git_project)
test_detached_head(self, temp_git_project)
test_no_remote_ahead_behind_zero(self, temp_git_project)
test_untracked_file_appears(self, temp_git_project)
test_detached_head_json(self, temp_git_project)
test_json_schema(self, temp_git_project, make_compliant_py)
test_json_violation_includes_issues(self, temp_git_project, make_noncompliant_py)
test_json_no_check(self, temp_git_project, make_compliant_py)
test_no_check_suppresses_badges(self, temp_git_project, make_noncompliant_py)
subprocess.CompletedProcess _run_status(Path root, *str extra_args)