8Integration tests for ``oct git status`` in monorepo layouts where the
9git repo root differs from the OCT project root.
13- Verify that ``oct git status`` resolves file paths correctly when
14 the OCT project lives in a subdirectory of the git repo.
15- Verify that no false "File too short" or "Missing module docstring"
16 warnings appear on well-formed files.
17- Verify that files outside the project scope are excluded.
18- Verify that duplicate entries do not appear across staged/modified
26 L3 — assertion details
31- Every test uses the ``monorepo_project`` fixture.
32- Tests never modify the system git config.
40from pathlib
import Path
51 return shutil.which(
"git")
is not None
55 repo: Path, *args: str, check: bool =
True,
56) -> subprocess.CompletedProcess:
57 """Run a git command inside *repo*."""
58 return subprocess.run(
68 project_dir: Path, *extra_args: str,
69) -> subprocess.CompletedProcess:
70 """Run ``oct git status`` from inside the OCT project subdirectory."""
71 return subprocess.run(
72 [sys.executable,
"-m",
"oct.cli",
"git",
"status", *extra_args],
79_COMPLIANT_TEMPLATE = textwrap.dedent(
"""\
80 #!/usr/bin/env python3
81 # -*- coding: utf-8 -*-
87 Test module for monorepo layout validation.
91 - Placeholder for monorepo integration tests.
107 from oc_diagnostics import _dbg
110 _dbg("TEST", func, "loaded", 2)
121 """Create a monorepo where the OCT project is a subdirectory.
129 └── subproject/ ← OCT project root
134 │ └── ARCHITECTURE.md
140 Returns ``(git_root, project_root)`` where ``project_root`` is
141 ``git_root / "subproject"``.
143 The project directory is named ``subproject`` (not ``oct``) so that
144 running ``python -m oct.cli`` in a subprocess does not shadow the
145 installed ``oct`` package.
148 pytest.skip(
"git binary not available on PATH")
150 git_root = tmp_path /
"monorepo"
154 _git(git_root,
"init",
"--quiet",
"-b",
"main")
155 _git(git_root,
"config",
"user.email",
"test@example.com")
156 _git(git_root,
"config",
"user.name",
"Test")
157 _git(git_root,
"config",
"commit.gpgsign",
"false")
160 (git_root /
"other_project").mkdir()
161 (git_root /
"other_project" /
"README.md").write_text(
162 "# Other project\n", encoding=
"utf-8",
166 project = git_root /
"subproject"
169 (project /
".option_c").mkdir()
170 (project /
"src").mkdir()
171 (project /
"tests").mkdir()
172 (project /
"docs").mkdir()
173 (project /
"logs").mkdir()
174 (project /
"oc_diagnostics").mkdir()
177 "linter": {
"profile":
"compact"},
178 "git": {
"protected_branches": [
"main",
"master"]},
180 (project /
".octrc.json").write_text(
181 json.dumps(octrc, indent=2), encoding=
"utf-8",
186 "GIT": {
"level": 1,
"color":
"cyan"},
187 "TEST": {
"level": 1,
"color":
"white"},
190 (project /
"debug_config.json").write_text(
191 json.dumps(debug_cfg, indent=2), encoding=
"utf-8",
194 (project /
"docs" /
"ARCHITECTURE.md").write_text(
195 "# Architecture\n\nStub for testing.\n", encoding=
"utf-8",
198 (project /
"pyproject.toml").write_text(
199 '[project]\nname = "test_project"\n', encoding=
"utf-8",
203 _git(git_root,
"add",
"-A")
204 _git(git_root,
"commit",
"-m",
"initial: monorepo structure",
"--quiet")
206 return git_root.resolve(), project.resolve()
215 """Verify that files resolve correctly when project root != git root."""
218 """A well-formed file should get [OK], not 'File too short'."""
219 git_root, project = monorepo_project
221 path = project /
"src" /
"good.py"
222 path.parent.mkdir(parents=
True, exist_ok=
True)
224 _COMPLIANT_TEMPLATE.format(rel_path=
"src/good.py"),
229 test_file = project /
"tests" /
"test_good.py"
230 test_file.write_text(
231 '"""Stub test for good."""\n\ndef test_good():\n pass\n',
236 _git(git_root,
"add",
"subproject/src/good.py")
239 assert result.returncode == 0, result.stderr
241 data = json.loads(result.stdout)
242 staged_paths = [e[
"path"]
for e
in data[
"staged"]]
243 staged_statuses = [e[
"status"]
for e
in data[
"staged"]]
246 assert any(
"good.py" in p
for p
in staged_paths), (
247 f
"good.py not in staged: {staged_paths}"
251 for entry
in data[
"staged"]:
252 if "good.py" in entry[
"path"]:
253 assert entry[
"status"] ==
"ok", (
254 f
"Expected ok but got {entry['status']}: "
257 assert "File too short" not in str(entry[
"issues"])
258 assert "Missing module docstring" not in str(entry[
"issues"])
261 """Files in other_project/ should not appear in oct git status."""
262 git_root, project = monorepo_project
265 readme = git_root /
"other_project" /
"README.md"
266 readme.write_text(
"# Changed\n", encoding=
"utf-8")
267 _git(git_root,
"add",
"other_project/README.md")
270 assert result.returncode == 0, result.stderr
272 data = json.loads(result.stdout)
274 [e[
"path"]
for e
in data[
"staged"]]
275 + [e[
"path"]
for e
in data[
"modified"]]
276 + [e[
"path"]
for e
in data[
"untracked"]]
279 assert "other_project" not in p, (
280 f
"File outside project scope appeared: {p}"
290 """Verify no spurious duplicates across sections."""
293 """A file that is only staged should not also appear as modified."""
294 git_root, project = monorepo_project
296 path = project /
"src" /
"only_staged.py"
297 path.parent.mkdir(parents=
True, exist_ok=
True)
299 _COMPLIANT_TEMPLATE.format(rel_path=
"src/only_staged.py"),
303 _git(git_root,
"add",
"subproject/src/only_staged.py")
306 assert result.returncode == 0, result.stderr
308 data = json.loads(result.stdout)
309 staged_paths = {e[
"path"]
for e
in data[
"staged"]}
310 modified_paths = {e[
"path"]
for e
in data[
"modified"]}
312 overlap = staged_paths & modified_paths
313 assert not overlap, f
"Duplicate entries in staged and modified: {overlap}"
test_staged_file_not_duplicated_in_modified(self, monorepo_project)
test_staged_file_no_false_warnings(self, monorepo_project)
test_files_outside_project_excluded(self, monorepo_project)
subprocess.CompletedProcess _git(Path repo, *str args, bool check=True)
tuple[Path, Path] monorepo_project(Path tmp_path)
subprocess.CompletedProcess _run_status(Path project_dir, *str extra_args)