"""Unit tests for GitOperations.""" import asyncio import os import subprocess import tempfile import pytest from src.models import Workspace from src.services.git.git_operations import GitOperations def _run_git(*args: str, cwd: str) -> None: subprocess.run(["git", *args], cwd=cwd, check=True, capture_output=True) @pytest.fixture def temp_workspace(): """Create a temporary git workspace.""" with tempfile.TemporaryDirectory() as tmpdir: _run_git("init", cwd=tmpdir) _run_git("config", "user.email", "test@test.com", cwd=tmpdir) _run_git("config", "user.name", "Test User", cwd=tmpdir) readme = os.path.join(tmpdir, "README.md") with open(readme, "w") as f: f.write("# Test\n") _run_git("add", "README.md", cwd=tmpdir) _run_git("commit", "-m", "Initial commit", cwd=tmpdir) ws = Workspace( id="00000000-0000-0000-0000-000000000001", name="test-ws", repo_id="00000000-0000-0000-0000-000000000002", user_id="00000000-0000-0000-0000-000000000003", branch="main", path=tmpdir, ) yield ws class TestGitOperationsStatus: """Tests for GitOperations.status.""" def test_status_clean(self, temp_workspace: Workspace): git = GitOperations(temp_workspace) status = asyncio.run(git.status()) assert status.branch == "main" assert status.modified == [] assert status.added == [] assert status.deleted == [] assert status.untracked == [] def test_status_modified(self, temp_workspace: Workspace): with open(os.path.join(temp_workspace.path, "README.md"), "a") as f: f.write("modified\n") git = GitOperations(temp_workspace) status = asyncio.run(git.status()) assert "README.md" in status.modified def test_status_untracked(self, temp_workspace: Workspace): with open(os.path.join(temp_workspace.path, "new.py"), "w") as f: f.write("print('hello')\n") git = GitOperations(temp_workspace) status = asyncio.run(git.status()) assert "new.py" in status.untracked class TestGitOperationsCommit: """Tests for GitOperations.commit.""" def test_commit_stages_and_commits(self, temp_workspace: Workspace): with open(os.path.join(temp_workspace.path, "new.py"), "w") as f: f.write("print('hello')\n") git = GitOperations(temp_workspace) asyncio.run(git.commit("Add new file")) status = asyncio.run(git.status()) assert "new.py" not in status.untracked assert "new.py" not in status.added history = asyncio.run(git.history()) assert history[0].message == "Add new file" def test_commit_fails_without_changes(self, temp_workspace: Workspace): git = GitOperations(temp_workspace) with pytest.raises(RuntimeError): asyncio.run(git.commit("Nothing to commit")) class TestGitOperationsHistory: """Tests for GitOperations.history.""" def test_history_returns_commits(self, temp_workspace: Workspace): git = GitOperations(temp_workspace) history = asyncio.run(git.history()) assert len(history) >= 1 assert history[0].message == "Initial commit" def test_history_filters_by_path(self, temp_workspace: Workspace): with open(os.path.join(temp_workspace.path, "new.py"), "w") as f: f.write("print('hello')\n") git = GitOperations(temp_workspace) asyncio.run(git.commit("Add new file")) history = asyncio.run(git.history(path="new.py")) assert len(history) == 1 assert history[0].message == "Add new file" class TestGitOperationsBranches: """Tests for GitOperations.branches.""" def test_branches_lists_main(self, temp_workspace: Workspace): git = GitOperations(temp_workspace) branches, current = asyncio.run(git.branches()) assert "main" in branches assert current == "main" def test_checkout_switches_branch(self, temp_workspace: Workspace): _run_git("checkout", "-b", "feature", cwd=temp_workspace.path) _run_git("checkout", "main", cwd=temp_workspace.path) git = GitOperations(temp_workspace) asyncio.run(git.checkout("feature")) status = asyncio.run(git.status()) assert status.branch == "feature"