nightshift/tests/test_git.py
K. Hodges 8b07876552 Improvement pass from llm
- tests/test_version.py: removed the brittle exact hotdog/topping/version assertion and kept the useful display contract.
  - nightshift/agents.py: normalized review next_stage / context_update values like None, null, and N/A to empty.
  - nightshift/git.py: added clearer non-git and Git safe-directory artifact messages with actionable guidance.
  - nightshift/cli.py: fixed run --all so completed dependencies remain in scope.
  - tests/test_agents.py, tests/test_git.py, tests/test_cli.py: added regression coverage.
  - docs/codex/20260521-next-improvements.md: marked the completed items.
2026-05-20 23:11:20 -07:00

94 lines
3.7 KiB
Python

from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
from nightshift.artifacts import ArtifactStore
from nightshift.errors import SafetyError
from nightshift.git import (
GitCommandResult,
ensure_clean_worktree,
format_git_unavailable_status,
git_failure_reason,
write_diff_artifact,
write_git_artifacts,
)
def git_available() -> bool:
return shutil.which("git") is not None
@unittest.skipUnless(git_available(), "git is not available")
class GitSafetyTests(unittest.TestCase):
def test_clean_worktree_requirement_blocks_dirty_repo(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
subprocess.run(["git", "init"], cwd=root, check=True, capture_output=True, text=True)
(root / "file.txt").write_text("dirty", encoding="utf-8")
with self.assertRaisesRegex(SafetyError, "repository is dirty"):
ensure_clean_worktree(root, True)
def test_git_artifacts_are_written_for_repo(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
subprocess.run(["git", "init"], cwd=root, check=True, capture_output=True, text=True)
(root / "file.txt").write_text("dirty", encoding="utf-8")
artifacts = ArtifactStore(root, ".nightshift", run_id="test-run")
status_path = write_git_artifacts(artifacts, "TASK-001", "before")
diff_path = write_diff_artifact(artifacts, "TASK-001")
self.assertTrue(status_path.exists())
self.assertTrue(diff_path.exists())
self.assertIn("Git Status before", status_path.read_text(encoding="utf-8"))
def test_diff_artifact_is_concise_outside_git_repo(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
artifacts = ArtifactStore(root, ".nightshift", run_id="test-run")
diff_path = write_diff_artifact(artifacts, "TASK-001")
content = diff_path.read_text(encoding="utf-8")
self.assertIn("Project root is not a git repository", content)
self.assertNotIn("usage: git diff", content)
def test_git_status_artifact_is_readable_outside_git_repo(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
artifacts = ArtifactStore(root, ".nightshift", run_id="test-run")
status_path = write_git_artifacts(artifacts, "TASK-001", "before")
content = status_path.read_text(encoding="utf-8")
self.assertIn("Git repository: false", content)
self.assertIn("Project root is not a git repository", content)
self.assertNotIn("fatal:", content)
def test_safe_directory_failure_gets_actionable_guidance(self) -> None:
root = Path("C:/repo/project")
result = GitCommandResult(
available=False,
exit_code=128,
stdout="",
stderr=(
"fatal: detected dubious ownership in repository at 'C:/repo/project'\n"
"To add an exception for this directory, call:\n"
"git config --global --add safe.directory C:/repo/project\n"
),
)
reason = git_failure_reason(result, root)
formatted = format_git_unavailable_status(result, "before", root)
self.assertIn("ownership is not trusted", reason)
self.assertIn("git config --global --add safe.directory C:/repo/project", reason)
self.assertIn("NightShift will not change global Git configuration", formatted)
if __name__ == "__main__":
unittest.main()