| 1 | """REAL tests for the stdlib smart-HTTP git server: serve a temp repo, clone
|
| 2 | it with the actual git CLI, push a commit back, and observe protocol v2
|
| 3 | passing through. This is the piece of the netshape slice the macOS dev host
|
| 4 | can exercise for real."""
|
| 5 |
|
| 6 | from __future__ import annotations
|
| 7 |
|
| 8 | import os
|
| 9 | import shutil
|
| 10 | import subprocess
|
| 11 | import sys
|
| 12 | import tempfile
|
| 13 | import unittest
|
| 14 | from pathlib import Path
|
| 15 |
|
| 16 | sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
| 17 |
|
| 18 | from oakbench.environment import base_env
|
| 19 | from oakbench.gitserver import GitHTTPServer
|
| 20 |
|
| 21 | GIT = shutil.which("git")
|
| 22 |
|
| 23 |
|
| 24 | def run_git(args: list[str], cwd: Path | None = None) -> subprocess.CompletedProcess:
|
| 25 | return subprocess.run(
|
| 26 | [GIT, *args],
|
| 27 | cwd=cwd,
|
| 28 | env=base_env(),
|
| 29 | stdout=subprocess.PIPE,
|
| 30 | stderr=subprocess.PIPE,
|
| 31 | text=True,
|
| 32 | timeout=120,
|
| 33 | check=False,
|
| 34 | )
|
| 35 |
|
| 36 |
|
| 37 | @unittest.skipIf(GIT is None, "git binary not installed")
|
| 38 | class GitHTTPServerTest(unittest.TestCase):
|
| 39 | def setUp(self) -> None:
|
| 40 | self.tmp = Path(tempfile.mkdtemp(prefix="oak-gitserver-test-"))
|
| 41 | self.addCleanup(shutil.rmtree, self.tmp, True)
|
| 42 |
|
| 43 | src = self.tmp / "src"
|
| 44 | run_git(["-c", "init.defaultBranch=main", "init", str(src)])
|
| 45 | (src / "README.md").write_text("# gitserver fixture\n")
|
| 46 | run_git(["add", "-A"], cwd=src)
|
| 47 | commit = run_git(["commit", "-m", "fixture commit"], cwd=src)
|
| 48 | self.assertEqual(commit.returncode, 0, commit.stderr)
|
| 49 |
|
| 50 | self.served_root = self.tmp / "served"
|
| 51 | self.served_root.mkdir()
|
| 52 | self.bare = self.served_root / "repo.git"
|
| 53 | run_git(["clone", "--bare", str(src), str(self.bare)])
|
| 54 | # Push over http works only when the served repo opts in.
|
| 55 | run_git(["-C", str(self.bare), "config", "http.receivepack", "true"])
|
| 56 |
|
| 57 | self.server = GitHTTPServer(self.served_root).start()
|
| 58 | self.addCleanup(self.server.stop)
|
| 59 | self.repo_url = f"{self.server.url}/repo.git"
|
| 60 |
|
| 61 | def test_clone_succeeds_and_logs_smart_paths(self) -> None:
|
| 62 | dest = self.tmp / "clone"
|
| 63 | result = run_git(["clone", self.repo_url, str(dest)])
|
| 64 | self.assertEqual(result.returncode, 0, result.stderr)
|
| 65 | self.assertEqual((dest / "README.md").read_text(), "# gitserver fixture\n")
|
| 66 | self.assertTrue(
|
| 67 | any(
|
| 68 | "/repo.git/info/refs" in entry and "service=git-upload-pack" in entry
|
| 69 | for entry in self.server.request_log
|
| 70 | ),
|
| 71 | self.server.request_log,
|
| 72 | )
|
| 73 |
|
| 74 | def test_push_over_http_succeeds(self) -> None:
|
| 75 | dest = self.tmp / "pusher"
|
| 76 | clone = run_git(["clone", self.repo_url, str(dest)])
|
| 77 | self.assertEqual(clone.returncode, 0, clone.stderr)
|
| 78 | (dest / "delta.txt").write_text("pushed over http\n")
|
| 79 | run_git(["add", "-A"], cwd=dest)
|
| 80 | commit = run_git(["commit", "-m", "small delta"], cwd=dest)
|
| 81 | self.assertEqual(commit.returncode, 0, commit.stderr)
|
| 82 | local_head = run_git(["rev-parse", "HEAD"], cwd=dest).stdout.strip()
|
| 83 |
|
| 84 | push = run_git(["push", "origin", "main"], cwd=dest)
|
| 85 | self.assertEqual(push.returncode, 0, push.stderr)
|
| 86 | served_head = run_git(["-C", str(self.bare), "rev-parse", "main"]).stdout.strip()
|
| 87 | self.assertEqual(served_head, local_head)
|
| 88 | self.assertTrue(
|
| 89 | any("git-receive-pack" in entry for entry in self.server.request_log),
|
| 90 | self.server.request_log,
|
| 91 | )
|
| 92 |
|
| 93 | def test_push_refused_without_receivepack_optin(self) -> None:
|
| 94 | run_git(["-C", str(self.bare), "config", "--unset", "http.receivepack"])
|
| 95 | dest = self.tmp / "refused"
|
| 96 | clone = run_git(["clone", self.repo_url, str(dest)])
|
| 97 | self.assertEqual(clone.returncode, 0, clone.stderr)
|
| 98 | (dest / "delta.txt").write_text("should be refused\n")
|
| 99 | run_git(["add", "-A"], cwd=dest)
|
| 100 | run_git(["commit", "-m", "refused delta"], cwd=dest)
|
| 101 | push = run_git(["push", "origin", "main"], cwd=dest)
|
| 102 | self.assertNotEqual(push.returncode, 0)
|
| 103 |
|
| 104 | def test_protocol_v2_header_passes_through(self) -> None:
|
| 105 | dest = self.tmp / "clone-v2"
|
| 106 | result = run_git(["-c", "protocol.version=2", "clone", self.repo_url, str(dest)])
|
| 107 | self.assertEqual(result.returncode, 0, result.stderr)
|
| 108 | self.assertTrue(
|
| 109 | any("version=2" in value for value in self.server.git_protocol_headers),
|
| 110 | self.server.git_protocol_headers,
|
| 111 | )
|
| 112 |
|
| 113 | def test_url_requires_started_server(self) -> None:
|
| 114 | stopped = GitHTTPServer(self.served_root)
|
| 115 | with self.assertRaises(RuntimeError):
|
| 116 | _ = stopped.url
|
| 117 |
|
| 118 | def test_missing_repo_is_a_client_error_not_a_crash(self) -> None:
|
| 119 | dest = self.tmp / "missing"
|
| 120 | result = run_git(["clone", f"{self.server.url}/nope.git", str(dest)])
|
| 121 | self.assertNotEqual(result.returncode, 0)
|
| 122 | # Server is still healthy afterwards.
|
| 123 | ok = run_git(["clone", self.repo_url, str(self.tmp / "still-works")])
|
| 124 | self.assertEqual(ok.returncode, 0, ok.stderr)
|
| 125 |
|
| 126 |
|
| 127 | if __name__ == "__main__":
|
| 128 | unittest.main()
|