Skip to content
File

Blob: test/ls-refs.worker.test.ts

typescript111 lines
1import { it, expect } from "vitest";
2import { env, exports as workerExports } from "cloudflare:workers";
3import { uniqueRepoId, runDOWithRetry, toRequestBody } from "./util/test-helpers";
4import { setupRepoForTests } from "./util/repoSeed";
5import { decodePktLinePayloads } from "./util/fetch-protocol";
6 
7function pktLine(s: string | Uint8Array): Uint8Array {
8 const enc = typeof s === "string" ? new TextEncoder().encode(s) : s;
9 const len = enc.byteLength + 4;
10 const hdr = new TextEncoder().encode(len.toString(16).padStart(4, "0"));
11 const out = new Uint8Array(hdr.byteLength + enc.byteLength);
12 out.set(hdr, 0);
13 out.set(enc, hdr.byteLength);
14 return out;
15}
16function delimPkt() {
17 return new TextEncoder().encode("0001");
18}
19function flushPkt() {
20 return new TextEncoder().encode("0000");
21}
22function concatChunks(chunks: Uint8Array[]): Uint8Array {
23 const total = chunks.reduce((a, c) => a + c.byteLength, 0);
24 const out = new Uint8Array(total);
25 let off = 0;
26 for (const c of chunks) {
27 out.set(c, off);
28 off += c.byteLength;
29 }
30 return out;
31}
32 
33function buildLsRefsBody(args: string[] = []) {
34 const chunks: Uint8Array[] = [];
35 chunks.push(pktLine("command=ls-refs\n"));
36 chunks.push(delimPkt());
37 for (const a of args) chunks.push(pktLine(a + "\n"));
38 chunks.push(flushPkt());
39 return concatChunks(chunks);
40}
41 
42it("ls-refs: unborn HEAD advertises correctly", async () => {
43 const owner = "o";
44 const repo = uniqueRepoId("r-lsrefs-unborn");
45 await setupRepoForTests(env, owner, repo);
46 const url = `https://example.com/${owner}/${repo}/git-upload-pack`;
47 const body = buildLsRefsBody(["ref-prefix refs/heads/"]);
48 const res = await workerExports.default.fetch(url, {
49 method: "POST",
50 headers: {
51 "Content-Type": "application/x-git-upload-pack-request",
52 "Git-Protocol": "version=2",
53 },
54 body: toRequestBody(body),
55 });
56 expect(res.status).toBe(200);
57 const bytes = new Uint8Array(await res.arrayBuffer());
58 const lines = decodePktLinePayloads(bytes);
59 // First line should indicate unborn HEAD with symref target
60 expect(lines[0]).toBe("unborn HEAD symref-target:refs/heads/main\n");
61});
62 
63it("ls-refs: .git upload-pack URL resolves to the canonical repository", async () => {
64 const owner = "o";
65 const repo = uniqueRepoId("r-lsrefs-dotgit");
66 await setupRepoForTests(env, owner, repo);
67 const url = `https://example.com/${owner}/${repo}.git/git-upload-pack`;
68 const body = buildLsRefsBody(["ref-prefix refs/heads/"]);
69 const res = await workerExports.default.fetch(url, {
70 method: "POST",
71 headers: {
72 "Content-Type": "application/x-git-upload-pack-request",
73 "Git-Protocol": "version=2",
74 },
75 body: toRequestBody(body),
76 });
77 expect(res.status).toBe(200);
78 expect(res.headers.get("Content-Type")).toContain("git-upload-pack-result");
79});
80 
81it("ls-refs: resolved HEAD and refs are listed after seeding", async () => {
82 const owner = "o";
83 const repo = uniqueRepoId("r-lsrefs-resolved");
84 await setupRepoForTests(env, owner, repo);
85 // Seed directly via DO (runInDurableObject)
86 const repoId = `${owner}/${repo}`;
87 const id = env.REPO_DO.idFromName(repoId);
88 const { commitOid } = await runDOWithRetry(
89 () => env.REPO_DO.get(id),
90 async (instance) => instance.seedMinimalRepo()
91 );
92 
93 const url = `https://example.com/${owner}/${repo}/git-upload-pack`;
94 const body = buildLsRefsBody(["ref-prefix refs/heads/"]);
95 const res = await workerExports.default.fetch(url, {
96 method: "POST",
97 headers: {
98 "Content-Type": "application/x-git-upload-pack-request",
99 "Git-Protocol": "version=2",
100 },
101 body: toRequestBody(body),
102 });
103 expect(res.status).toBe(200);
104 const bytes = new Uint8Array(await res.arrayBuffer());
105 const lines = decodePktLinePayloads(bytes);
106 // First line should show HEAD resolved with symref
107 expect(lines[0]).toBe(`${commitOid} HEAD symref-target:refs/heads/main\n`);
108 // There should be a line for refs/heads/main
109 expect(lines.some((l) => l === `${commitOid} refs/heads/main\n`)).toBe(true);
110});