Skip to content
File

Blob: test/compute-needed-fast.worker.test.ts

typescript141 lines
1import { it, expect, describe } from "vitest";
2import { env } from "cloudflare:workers";
3import { createExecutionContext } from "cloudflare:test";
4import type { CacheContext } from "@/worker/cache";
5import { computeNeededFast } from "@/worker/git/operations/fetch/neededFast";
6import { uniqueRepoId, runDOWithRetry } from "./util/test-helpers";
7import { setupRepoForTests } from "./util/repoSeed";
8 
9describe("computeNeededFast", () => {
10 it("computes minimal closure with stop set", async () => {
11 const owner = "o";
12 const repo = uniqueRepoId("closure-fast");
13 await setupRepoForTests(env, owner, repo);
14 const repoId = `${owner}/${repo}`;
15 
16 // Create a repo with a commit
17 const id = env.REPO_DO.idFromName(repoId);
18 const { commitOid, treeOid } = await runDOWithRetry(
19 () => env.REPO_DO.get(id),
20 async (instance) => {
21 return await instance.seedMinimalRepo();
22 }
23 );
24 
25 // Test 1: With no haves, should include commit and tree
26 const needed1 = await computeNeededFast(
27 env,
28 repoId,
29 [commitOid], // want commit
30 [], // no haves
31 undefined
32 );
33 
34 // Should include the commit and its tree
35 expect(needed1).toContain(commitOid);
36 expect(needed1).toContain(treeOid);
37 expect(needed1.length).toBe(2); // Just commit and tree
38 
39 // Test 2: With commit as have, should return empty (nothing needed)
40 const needed2 = await computeNeededFast(
41 env,
42 repoId,
43 [commitOid], // want commit
44 [commitOid], // already have it
45 undefined
46 );
47 
48 // Should be empty since we already have what we want
49 expect(needed2.length).toBe(0);
50 
51 // Test 3: With non-existent have, should include everything
52 const fakeOid = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
53 const needed3 = await computeNeededFast(
54 env,
55 repoId,
56 [commitOid], // want commit
57 [fakeOid], // have something that doesn't exist
58 undefined
59 );
60 
61 // Should include everything since the have doesn't exist
62 expect(needed3).toContain(commitOid);
63 expect(needed3).toContain(treeOid);
64 expect(needed3.length).toBe(2);
65 });
66 
67 it("returns the partial closure when the traversal times out", async () => {
68 const owner = "o";
69 const repo = uniqueRepoId("closure-timeout");
70 await setupRepoForTests(env, owner, repo);
71 const repoId = `${owner}/${repo}`;
72 
73 const cacheCtx: CacheContext = {
74 req: new Request("http://test"),
75 ctx: createExecutionContext(),
76 memo: {
77 flags: new Set<string>(),
78 },
79 };
80 
81 // Create a large chain of commits
82 const id = env.REPO_DO.idFromName(repoId);
83 const { commitOid } = await runDOWithRetry(
84 () => env.REPO_DO.get(id),
85 async (instance) => instance.seedMinimalRepo()
86 );
87 
88 const realNow = Date.now;
89 let nowCalls = 0;
90 Date.now = () => {
91 nowCalls++;
92 return nowCalls === 1 ? 0 : 50_000;
93 };
94 
95 try {
96 const needed = await computeNeededFast(env, repoId, [commitOid], [], cacheCtx);
97 expect(cacheCtx.memo!.flags!.has("closure-timeout")).toBe(true);
98 expect(Array.isArray(needed)).toBe(true);
99 } finally {
100 Date.now = realNow;
101 }
102 });
103 
104 it("uses memoization effectively", async () => {
105 const owner = "o";
106 const repo = uniqueRepoId("memo");
107 await setupRepoForTests(env, owner, repo);
108 const repoId = `${owner}/${repo}`;
109 
110 const id = env.REPO_DO.idFromName(repoId);
111 const { commitOid } = await runDOWithRetry(
112 () => env.REPO_DO.get(id),
113 async (instance) => instance.seedMinimalRepo()
114 );
115 
116 const cacheCtx: CacheContext = {
117 req: new Request("http://test"),
118 ctx: createExecutionContext(),
119 memo: {
120 refs: new Map<string, string[]>(),
121 flags: new Set<string>(),
122 },
123 };
124 
125 // First call should populate memo
126 const needed1 = await computeNeededFast(env, repoId, [commitOid], [], cacheCtx);
127 
128 expect(needed1.length).toBeGreaterThan(0);
129 expect(cacheCtx.memo!.refs!.size).toBeGreaterThan(0);
130 
131 // Second call should use memo (faster)
132 const startTime = Date.now();
133 const needed2 = await computeNeededFast(env, repoId, [commitOid], [], cacheCtx);
134 const elapsed = Date.now() - startTime;
135 
136 expect(needed2).toEqual(needed1);
137 // Should be faster due to memoization
138 expect(elapsed).toBeLessThan(100);
139 });
140});