Skip to content
File

Blob: test/pack-indexer.connectivity.tags.worker.test.ts

typescript205 lines
1import { describe, expect, it } from "vitest";
2import { env } from "cloudflare:workers";
3import { buildPack, makeCommit, makeTree } from "./util/git-pack";
4import { createTestCacheContext } from "./util/pack-first";
5import { uniqueRepoId } from "./util/test-helpers";
6import { makeLimiter, packIndexerLog as log } from "./util/pack-indexer.helpers";
7 
8import {
9 runPackConnectivityCheck,
10 scanPack,
11 resolveDeltasAndWriteIdx,
12} from "@/worker/git/pack/indexer";
13import { computeOid } from "@/worker/git/core/objects";
14 
15type ConnectivityStatus = { ref: string; ok: boolean; msg?: string };
16 
17describe("runPackConnectivityCheck annotated tags", () => {
18 it("unwraps annotated tags and validates the final target", async () => {
19 const tree = await makeTree();
20 const commit = await makeCommit(tree.oid, "tagged commit");
21 const tagPayload = new TextEncoder().encode(
22 `object ${commit.oid}\ntype commit\ntag v1.0\ntagger You <you@example.com> 0 +0000\n\nrelease\n`
23 );
24 const tagOid = await computeOid("tag", tagPayload);
25 
26 const packBytes = await buildPack([
27 { type: "tree", payload: tree.payload },
28 { type: "commit", payload: commit.payload },
29 { type: "tag", payload: tagPayload },
30 ]);
31 
32 const packKey = "test/conn-tag.pack";
33 await env.REPO_BUCKET.put(packKey, packBytes);
34 const head = await env.REPO_BUCKET.head(packKey);
35 
36 const scanResult = await scanPack({
37 env,
38 packKey,
39 packSize: head!.size,
40 limiter: makeLimiter(),
41 countSubrequest: () => {},
42 log,
43 });
44 
45 const repoId = uniqueRepoId();
46 const resolveResult = await resolveDeltasAndWriteIdx({
47 env,
48 packKey,
49 packSize: head!.size,
50 limiter: makeLimiter(),
51 countSubrequest: () => {},
52 log,
53 scanResult,
54 repoId,
55 });
56 
57 const statuses: ConnectivityStatus[] = [{ ref: "refs/tags/v1.0", ok: true }];
58 const cacheCtx = createTestCacheContext("http://localhost/test");
59 
60 await runPackConnectivityCheck({
61 env,
62 repoId,
63 newPackKey: packKey,
64 newIdxView: resolveResult.idxView,
65 newPackSize: head!.size,
66 activeCatalog: [],
67 commands: [{ oldOid: "0".repeat(40), newOid: tagOid, ref: "refs/tags/v1.0" }],
68 statuses,
69 log,
70 cacheCtx,
71 });
72 
73 expect(statuses[0].ok).toBe(true);
74 });
75 
76 it("accepts annotated tag chains up to the hard limit", async () => {
77 const tree = await makeTree();
78 const commit = await makeCommit(tree.oid, "tag depth limit");
79 const tagPayloads: Uint8Array[] = [];
80 let targetOid = commit.oid;
81 for (let depth = 0; depth < 8; depth++) {
82 const payload = new TextEncoder().encode(
83 `object ${targetOid}\ntype ${depth === 0 ? "commit" : "tag"}\ntag v${depth}\ntagger You <you@example.com> 0 +0000\n\ndepth ${depth}\n`
84 );
85 tagPayloads.push(payload);
86 targetOid = await computeOid("tag", payload);
87 }
88 
89 const packEntries = [
90 { type: "tree" as const, payload: tree.payload },
91 { type: "commit" as const, payload: commit.payload },
92 ...tagPayloads.map((payload) => ({ type: "tag" as const, payload })),
93 ];
94 const packBytes = await buildPack(packEntries);
95 
96 const packKey = "test/conn-tag-depth-limit.pack";
97 await env.REPO_BUCKET.put(packKey, packBytes);
98 const head = await env.REPO_BUCKET.head(packKey);
99 
100 const scanResult = await scanPack({
101 env,
102 packKey,
103 packSize: head!.size,
104 limiter: makeLimiter(),
105 countSubrequest: () => {},
106 log,
107 });
108 
109 const repoId = uniqueRepoId();
110 const resolveResult = await resolveDeltasAndWriteIdx({
111 env,
112 packKey,
113 packSize: head!.size,
114 limiter: makeLimiter(),
115 countSubrequest: () => {},
116 log,
117 scanResult,
118 repoId,
119 });
120 
121 const statuses: ConnectivityStatus[] = [{ ref: "refs/tags/limit", ok: true }];
122 const cacheCtx = createTestCacheContext("http://localhost/test");
123 
124 await runPackConnectivityCheck({
125 env,
126 repoId,
127 newPackKey: packKey,
128 newIdxView: resolveResult.idxView,
129 newPackSize: head!.size,
130 activeCatalog: [],
131 commands: [{ oldOid: "0".repeat(40), newOid: targetOid, ref: "refs/tags/limit" }],
132 statuses,
133 log,
134 cacheCtx,
135 });
136 
137 expect(statuses[0].ok).toBe(true);
138 });
139 
140 it("rejects annotated tag chains deeper than the hard limit", async () => {
141 const tree = await makeTree();
142 const commit = await makeCommit(tree.oid, "deep tag commit");
143 const tagPayloads: Uint8Array[] = [];
144 let targetOid = commit.oid;
145 for (let depth = 0; depth < 9; depth++) {
146 const payload = new TextEncoder().encode(
147 `object ${targetOid}\ntype ${depth === 0 ? "commit" : "tag"}\ntag v${depth}\ntagger You <you@example.com> 0 +0000\n\ndepth ${depth}\n`
148 );
149 tagPayloads.push(payload);
150 targetOid = await computeOid("tag", payload);
151 }
152 
153 const packEntries = [
154 { type: "tree" as const, payload: tree.payload },
155 { type: "commit" as const, payload: commit.payload },
156 ...tagPayloads.map((payload) => ({ type: "tag" as const, payload })),
157 ];
158 const packBytes = await buildPack(packEntries);
159 
160 const packKey = "test/conn-tag-depth.pack";
161 await env.REPO_BUCKET.put(packKey, packBytes);
162 const head = await env.REPO_BUCKET.head(packKey);
163 
164 const scanResult = await scanPack({
165 env,
166 packKey,
167 packSize: head!.size,
168 limiter: makeLimiter(),
169 countSubrequest: () => {},
170 log,
171 });
172 
173 const repoId = uniqueRepoId();
174 const resolveResult = await resolveDeltasAndWriteIdx({
175 env,
176 packKey,
177 packSize: head!.size,
178 limiter: makeLimiter(),
179 countSubrequest: () => {},
180 log,
181 scanResult,
182 repoId,
183 });
184 
185 const statuses: ConnectivityStatus[] = [{ ref: "refs/tags/deep", ok: true }];
186 const cacheCtx = createTestCacheContext("http://localhost/test");
187 
188 await runPackConnectivityCheck({
189 env,
190 repoId,
191 newPackKey: packKey,
192 newIdxView: resolveResult.idxView,
193 newPackSize: head!.size,
194 activeCatalog: [],
195 commands: [{ oldOid: "0".repeat(40), newOid: targetOid, ref: "refs/tags/deep" }],
196 statuses,
197 log,
198 cacheCtx,
199 });
200 
201 expect(statuses[0].ok).toBe(false);
202 expect(statuses[0].msg).toBe("missing-objects");
203 });
204});