8Integration tests for the ``--scope`` flag on ``oct git status``
9(Phase 4G). Validates the project / repo / auto modes that control
10whether files outside the OCT project root are surfaced.
14- ``project`` (default) — only files inside the OCT project root.
15- ``repo`` — every file the git index/work-tree knows about.
16- ``auto`` — collapses to project when project_root == repo_root,
17 otherwise behaves like repo.
24 L3 -- assertion details
28- All tests use subprocess invocation against ``oct.cli``.
29- All tests use tmp_path fixtures so they are automatically cleaned up.
37from pathlib
import Path
43 return shutil.which(
"git")
is not None
47 repo: Path, *args: str, check: bool =
True,
48) -> subprocess.CompletedProcess:
49 return subprocess.run(
50 [
"git", *args], cwd=repo, check=check,
51 capture_output=
True, text=
True,
56 project_dir: Path, *extra_args: str,
57) -> subprocess.CompletedProcess:
58 return subprocess.run(
59 [sys.executable,
"-m",
"oct.cli",
"git",
"status", *extra_args],
73 """Create a monorepo and place a file outside the OCT project."""
75 pytest.skip(
"git binary not available on PATH")
77 git_root = tmp_path /
"monorepo"
79 _git(git_root,
"init",
"--quiet",
"-b",
"main")
80 _git(git_root,
"config",
"user.email",
"test@example.com")
81 _git(git_root,
"config",
"user.name",
"Test")
82 _git(git_root,
"config",
"commit.gpgsign",
"false")
85 (git_root /
"outside").mkdir()
86 (git_root /
"outside" /
"README.md").write_text(
87 "# Outside\n", encoding=
"utf-8",
91 project = git_root /
"subproject"
93 (project /
".option_c").mkdir()
94 (project /
"src").mkdir()
95 (project /
"tests").mkdir()
96 (project /
"docs").mkdir()
97 (project /
"logs").mkdir()
98 (project /
"oc_diagnostics").mkdir()
100 (project /
".octrc.json").write_text(
102 "linter": {
"profile":
"compact"},
103 "git": {
"protected_branches": [
"main",
"master"]},
107 (project /
"debug_config.json").write_text(
108 json.dumps({
"domains": {
"GIT": {
"level": 1,
"color":
"cyan"}}}),
111 (project /
"docs" /
"ARCHITECTURE.md").write_text(
112 "# Architecture\n", encoding=
"utf-8",
114 (project /
"pyproject.toml").write_text(
115 '[project]\nname = "test_project"\n', encoding=
"utf-8",
118 _git(git_root,
"add",
"-A")
119 _git(git_root,
"commit",
"-m",
"initial",
"--quiet")
122 (project /
"src" /
"inside.py").write_text(
123 "x = 1\n", encoding=
"utf-8",
125 (git_root /
"outside" /
"outside_change.txt").write_text(
126 "y\n", encoding=
"utf-8",
129 return git_root.resolve(), project.resolve()
140 self, monorepo_with_outside,
142 _git_root, project = monorepo_with_outside
144 assert result.returncode == 0, result.stderr
145 data = json.loads(result.stdout)
147 [e[
"path"]
for e
in data[
"staged"]]
148 + [e[
"path"]
for e
in data[
"modified"]]
149 + [e[
"path"]
for e
in data[
"untracked"]]
151 assert all(
"outside" not in p
for p
in all_paths)
152 assert data[
"scope"] ==
"project"
154 assert data[
"out_of_project"] == []
157 self, monorepo_with_outside,
159 _git_root, project = monorepo_with_outside
160 result =
_run_status(project,
"--scope=project",
"--json")
161 data = json.loads(result.stdout)
162 assert data[
"scope"] ==
"project"
173 _git_root, project = monorepo_with_outside
174 result =
_run_status(project,
"--scope=repo",
"--json")
175 assert result.returncode == 0, result.stderr
176 data = json.loads(result.stdout)
177 assert data[
"scope"] ==
"repo"
178 out_paths = [e[
"path"]
for e
in data[
"out_of_project"]]
179 assert any(
"outside" in p
for p
in out_paths), (
180 f
"Expected an 'outside/...' path in out_of_project: {out_paths}"
184 self, monorepo_with_outside,
186 _git_root, project = monorepo_with_outside
188 assert result.returncode == 0, result.stderr
189 assert "Other repo files" in result.stdout
200 self, monorepo_with_outside,
202 _git_root, project = monorepo_with_outside
203 result =
_run_status(project,
"--scope=auto",
"--json")
204 data = json.loads(result.stdout)
206 assert data[
"scope"] ==
"repo"
209 self, temp_git_project: Path,
212 result = subprocess.run(
214 sys.executable,
"-m",
"oct.cli",
215 "git",
"status",
"--scope=auto",
"--json",
217 cwd=temp_git_project, capture_output=
True, text=
True,
219 assert result.returncode == 0, result.stderr
220 data = json.loads(result.stdout)
221 assert data[
"scope"] ==
"project"
None test_auto_in_simple_repo_collapses_to_project(self, Path temp_git_project)
None test_auto_in_monorepo_collapses_to_repo(self, monorepo_with_outside)
None test_default_excludes_outside(self, monorepo_with_outside)
None test_explicit_project_scope(self, monorepo_with_outside)
None test_repo_includes_outside(self, monorepo_with_outside)
None test_terminal_section_rendered(self, monorepo_with_outside)
tuple[Path, Path] monorepo_with_outside(Path tmp_path)
subprocess.CompletedProcess _run_status(Path project_dir, *str extra_args)
subprocess.CompletedProcess _git(Path repo, *str args, bool check=True)