Option C Tools
Loading...
Searching...
No Matches
test_git_restore.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_git/test_git_restore.py
4
5"""
6Purpose
7-------
8Integration tests for ``oct git restore`` (L3).
9
10Responsibilities
11----------------
12- Verify ``oct git restore <path>`` reverts unstaged working-tree
13 edits.
14- Verify ``oct git restore --staged <path>`` unstages from the index
15 without touching the working tree.
16- Verify ``--all`` enumerates in-scope paths.
17- Verify the confirmation prompt for ``--staged --all`` (and
18 ``--yes`` bypass).
19- Verify path-scope filtering (paths outside the project root are
20 skipped) and JSON output.
21
22Diagnostics
23-----------
24Domain: GIT-TESTS
25Levels:
26 L2 -- test lifecycle
27 L3 -- assertion details
28
29Contracts
30---------
31- All tests use subprocess invocation against ``oct.cli``.
32- All tests use tmp_path fixtures so they are automatically cleaned up.
33"""
34
35import json
36import shutil
37import subprocess
38import sys
39from pathlib import Path
40
41import pytest
42
43from tests.tests_git.conftest import _git
44
45
46def _git_available() -> bool:
47 return shutil.which("git") is not None
48
49
51 root: Path, *extra_args: str,
52) -> subprocess.CompletedProcess:
53 return subprocess.run(
54 [sys.executable, "-m", "oct.cli", "git", "restore", *extra_args],
55 cwd=root, capture_output=True, text=True,
56 )
57
58
59def _staged_names(repo: Path) -> list[str]:
60 result = subprocess.run(
61 ["git", "diff", "--cached", "--name-only"],
62 cwd=repo, check=True, capture_output=True, text=True,
63 )
64 return [
65 line.strip() for line in result.stdout.splitlines() if line.strip()
66 ]
67
68
69# =====================================================================
70# Restore working-tree edits
71# =====================================================================
72
73
75
76 @pytest.mark.skipif(not _git_available(), reason="git not on PATH")
77 def test_restore_single_file(self, temp_git_project: Path) -> None:
78 f = temp_git_project / "src" / "tracked.txt"
79 f.write_text("v1\n", encoding="utf-8")
80 _git(temp_git_project, "add", "src/tracked.txt")
81 _git(temp_git_project, "commit", "-m", "init", "--quiet")
82
83 # Modify it (unstaged).
84 f.write_text("v2\n", encoding="utf-8")
85
86 result = _run_restore(temp_git_project, "src/tracked.txt")
87 assert result.returncode == 0, result.stderr
88 # Working tree should be back to v1.
89 assert f.read_text(encoding="utf-8") == "v1\n"
90
91
92# =====================================================================
93# --staged: unstage without touching working tree
94# =====================================================================
95
96
98
99 @pytest.mark.skipif(not _git_available(), reason="git not on PATH")
101 self, temp_git_project: Path,
102 ) -> None:
103 f = temp_git_project / "src" / "tracked.txt"
104 f.write_text("v1\n", encoding="utf-8")
105 _git(temp_git_project, "add", "src/tracked.txt")
106 _git(temp_git_project, "commit", "-m", "init", "--quiet")
107
108 # Modify and stage.
109 f.write_text("v2\n", encoding="utf-8")
110 _git(temp_git_project, "add", "src/tracked.txt")
111 assert "src/tracked.txt" in _staged_names(temp_git_project)
112
113 result = _run_restore(
114 temp_git_project, "--staged", "src/tracked.txt",
115 )
116 assert result.returncode == 0, result.stderr
117 # No longer staged.
118 assert "src/tracked.txt" not in _staged_names(temp_git_project)
119 # Working tree still v2.
120 assert f.read_text(encoding="utf-8") == "v2\n"
121
122
123# =====================================================================
124# --all and prompt
125# =====================================================================
126
127
129
130 @pytest.mark.skipif(not _git_available(), reason="git not on PATH")
132 self, temp_git_project: Path,
133 ) -> None:
134 a = temp_git_project / "src" / "a.txt"
135 b = temp_git_project / "src" / "b.txt"
136 a.write_text("v1\n", encoding="utf-8")
137 b.write_text("v1\n", encoding="utf-8")
138 _git(temp_git_project, "add", "src/a.txt", "src/b.txt")
139 _git(temp_git_project, "commit", "-m", "init", "--quiet")
140
141 a.write_text("v2\n", encoding="utf-8")
142 b.write_text("v2\n", encoding="utf-8")
143
144 result = _run_restore(temp_git_project, "--all")
145 assert result.returncode == 0, result.stderr
146 assert a.read_text(encoding="utf-8") == "v1\n"
147 assert b.read_text(encoding="utf-8") == "v1\n"
148
149 @pytest.mark.skipif(not _git_available(), reason="git not on PATH")
151 self, temp_git_project: Path,
152 ) -> None:
153 f = temp_git_project / "src" / "tracked.txt"
154 f.write_text("v1\n", encoding="utf-8")
155 _git(temp_git_project, "add", "src/tracked.txt")
156 _git(temp_git_project, "commit", "-m", "init", "--quiet")
157 f.write_text("v2\n", encoding="utf-8")
158 _git(temp_git_project, "add", "src/tracked.txt")
159
160 result = _run_restore(
161 temp_git_project, "--staged", "--all", "--yes",
162 )
163 assert result.returncode == 0, result.stderr
164 assert "src/tracked.txt" not in _staged_names(temp_git_project)
165
166
167# =====================================================================
168# Empty input
169# =====================================================================
170
171
173
174 @pytest.mark.skipif(not _git_available(), reason="git not on PATH")
176 self, temp_git_project: Path,
177 ) -> None:
178 result = _run_restore(temp_git_project)
179 assert result.returncode == 1
180 assert "provide PATHS" in result.stderr.lower() \
181 or "use --all" in result.stderr.lower()
182
183
184# =====================================================================
185# JSON
186# =====================================================================
187
188
190
191 @pytest.mark.skipif(not _git_available(), reason="git not on PATH")
192 def test_json_schema(self, temp_git_project: Path) -> None:
193 f = temp_git_project / "src" / "tracked.txt"
194 f.write_text("v1\n", encoding="utf-8")
195 _git(temp_git_project, "add", "src/tracked.txt")
196 _git(temp_git_project, "commit", "-m", "init", "--quiet")
197 f.write_text("v2\n", encoding="utf-8")
198
199 result = _run_restore(
200 temp_git_project, "--json", "src/tracked.txt",
201 )
202 assert result.returncode == 0, result.stderr
203 data = json.loads(result.stdout)
204 assert data["operation"] == "restore"
205 assert data["staged"] is False
206 assert "src/tracked.txt" in data["paths_restored"]
207 assert data["exit_code"] == 0
None test_staged_all_with_yes_skips_prompt(self, Path temp_git_project)
None test_all_restores_every_modified(self, Path temp_git_project)
None test_no_paths_no_all_errors(self, Path temp_git_project)
None test_json_schema(self, Path temp_git_project)
None test_unstage_preserves_working_tree(self, Path temp_git_project)
None test_restore_single_file(self, Path temp_git_project)
list[str] _staged_names(Path repo)
subprocess.CompletedProcess _run_restore(Path root, *str extra_args)