import subprocess from pathlib import Path import pytest from app.services.git_service import GitService def run_git(cwd: Path, *args: str) -> str: result = subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True, check=True) return result.stdout.strip() def commit_file(cwd: Path, relative_path: str, content: str, message: str) -> None: target = cwd / relative_path target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content, encoding="utf-8") run_git(cwd, "add", ".") run_git(cwd, "-c", "user.name=Test", "-c", "user.email=test@example.com", "commit", "-m", message) @pytest.fixture def git_fixture(tmp_path: Path): remote = tmp_path / "remote.git" local = tmp_path / "local" seed = tmp_path / "seed" remote.mkdir() local.mkdir() seed.mkdir() run_git(remote, "init", "--bare", "-b", "main") run_git(seed, "init", "-b", "main") commit_file(seed, "docs/one.md", "one", "initial") commit_file(seed, "other.txt", "other", "other") run_git(seed, "remote", "add", "origin", str(remote)) run_git(seed, "push", "-u", "origin", "main") run_git(local, "clone", "-b", "main", str(remote), ".") return remote, local, seed @pytest.mark.asyncio async def test_directory_push_only_changes_selected_directory(git_fixture): remote, local, _ = git_fixture service = GitService() (local / "docs/one.md").write_text("updated", encoding="utf-8") (local / "other.txt").write_text("local-only", encoding="utf-8") ok, message = await service.push(local, str(remote), sub_path="docs") assert ok, message assert run_git(local, "remote", "get-url", "origin") == str(remote) check = Path(local.parent / "check") run_git(local.parent, "clone", "-b", "main", str(remote), str(check)) assert (check / "docs/one.md").read_text(encoding="utf-8") == "updated" assert (check / "other.txt").read_text(encoding="utf-8") == "other" @pytest.mark.asyncio async def test_whole_push_rejects_remote_ahead(git_fixture): remote, local, seed = git_fixture service = GitService() commit_file(seed, "other.txt", "remote-update", "remote update") run_git(seed, "push", "origin", "main") (local / "docs/one.md").write_text("local-update", encoding="utf-8") ok, message = await service.push(local, str(remote)) assert not ok assert "先执行整库拉取" in message @pytest.mark.asyncio async def test_credentials_are_not_written_to_remote_url(git_fixture): remote, local, _ = git_fixture service = GitService() ok, message = await service.pull(local, str(remote), username="user", token="secret") assert ok, message assert "secret" not in run_git(local, "remote", "get-url", "origin")