Skip to content
134 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { CONTENT_ORIGIN, type SharedCache, WeightedLru, contentCache, contentKey, weightOfLines } from "./content-cache.ts";
5
6test("a content key is a hash of every part, in order, and never of a join that could collide", async () => {
7 const key = await contentKey(["typescript", "const a = 1;"]);
8 assert.match(key, /^[0-9a-f]{64}$/);
9 assert.equal(key, await contentKey(["typescript", "const a = 1;"]));
10 assert.notEqual(key, await contentKey(["tsx", "const a = 1;"]));
11 assert.notEqual(key, await contentKey(["typescript", "const a = 2;"]));
12 // The same characters split differently are different content.
13 assert.notEqual(await contentKey(["ab", "c"]), await contentKey(["a", "bc"]));
14 assert.notEqual(await contentKey(["a:b"]), await contentKey(["a", "b"]));
15});
16
17test("the isolate's memory keeps the most recently used within its weight", () => {
18 const lru = new WeightedLru<string>(100, (value) => value.length);
19 lru.set("a", "x".repeat(20));
20 lru.set("b", "x".repeat(20));
21 lru.set("c", "x".repeat(20));
22 assert.equal(lru.weight, 60);
23 // Reading `a` makes it the newest; `b` is now the oldest.
24 assert.ok(lru.get("a"));
25 lru.set("d", "x".repeat(20));
26 lru.set("e", "x".repeat(20));
27 lru.set("f", "x".repeat(20));
28 assert.equal(lru.get("b"), undefined);
29 assert.ok(lru.get("a"));
30 assert.ok(lru.weight <= 100);
31 // Replacing an entry does not count it twice.
32 lru.set("a", "y");
33 assert.equal(lru.get("a"), "y");
34 assert.ok(lru.weight <= 100);
35 // One entry heavier than a quarter of the whole is never kept.
36 lru.set("big", "x".repeat(26));
37 assert.equal(lru.get("big"), undefined);
38});
39
40test("lines weigh their characters", () => {
41 assert.equal(weightOfLines(["ab", null, ""]), 2 + 8 + 0 + 8 + 0 + 8);
42});
43
44/** A data centre's cache in memory, as caches.default answers. */
45function fakeShared() {
46 const kept = new Map<string, string>();
47 const shared: SharedCache = {
48 async match(url) {
49 const body = kept.get(url);
50 return body === undefined ? undefined : new Response(body);
51 },
52 async put(url, response) {
53 kept.set(url, await response.text());
54 },
55 };
56 return { kept, shared };
57}
58
59test("an answer is computed once, then read from the isolate, then from the data centre", async () => {
60 const { kept, shared } = fakeShared();
61 const deferred: Promise<unknown>[] = [];
62 const options = {
63 name: "test-lines",
64 version: "v1",
65 maxWeight: 1_000,
66 weigh: weightOfLines,
67 shared: () => shared,
68 defer: (work: Promise<unknown>) => void deferred.push(work),
69 };
70 const cached = contentCache<string[]>(options);
71 let runs = 0;
72 const compute = async () => {
73 runs += 1;
74 return ["<b>a</b>", "b"];
75 };
76 assert.deepEqual(await cached(["rust", "a\nb"], compute), ["<b>a</b>", "b"]);
77 await Promise.all(deferred);
78 assert.equal(runs, 1);
79 assert.equal(kept.size, 1);
80 const [url] = kept.keys();
81 assert.ok(url!.startsWith(`${CONTENT_ORIGIN}test-lines/v1/`));
82 // The isolate has it.
83 assert.deepEqual(await cached(["rust", "a\nb"], compute), ["<b>a</b>", "b"]);
84 assert.equal(runs, 1);
85 // Another isolate (a fresh cache) finds the data centre's copy.
86 const other = contentCache<string[]>(options);
87 assert.deepEqual(await other(["rust", "a\nb"], compute), ["<b>a</b>", "b"]);
88 assert.equal(runs, 1);
89 // Other content, or the same content for another language, is another entry.
90 await cached(["rust", "a\nc"], compute);
91 await cached(["go", "a\nb"], compute);
92 assert.equal(runs, 3);
93});
94
95test("a new version never reads what an old one kept", async () => {
96 const { shared } = fakeShared();
97 const make = (version: string) =>
98 contentCache<string[]>({ name: "test", version, maxWeight: 1_000, weigh: weightOfLines, shared: () => shared, defer: () => {} });
99 const deferred: Promise<unknown>[] = [];
100 const first = contentCache<string[]>({
101 name: "test",
102 version: "v1",
103 maxWeight: 1_000,
104 weigh: weightOfLines,
105 shared: () => shared,
106 defer: (work) => void deferred.push(work),
107 });
108 await first(["x"], async () => ["old"]);
109 await Promise.all(deferred);
110 assert.deepEqual(await make("v1")(["x"], async () => ["new"]), ["old"]);
111 assert.deepEqual(await make("v2")(["x"], async () => ["new"]), ["new"]);
112});
113
114test("nothing to keep is not kept, and a broken shared entry counts as missing", async () => {
115 const { kept, shared } = fakeShared();
116 const cached = contentCache<string[]>({ name: "t", version: "v1", maxWeight: 1_000, weigh: weightOfLines, shared: () => shared, defer: () => {} });
117 let runs = 0;
118 assert.equal(await cached(["none"], async () => (runs++, null)), null);
119 assert.equal(await cached(["none"], async () => (runs++, null)), null);
120 assert.equal(runs, 2);
121 assert.equal(kept.size, 0);
122
123 const url = `${CONTENT_ORIGIN}t/v1/${await contentKey(["t", "v1", "broken"])}`;
124 kept.set(url, "{not json");
125 assert.deepEqual(await cached(["broken"], async () => ["fresh"]), ["fresh"]);
126});
127
128test("without a data centre's cache the isolate still keeps answers", async () => {
129 const cached = contentCache<string[]>({ name: "t", version: "v1", maxWeight: 1_000, weigh: weightOfLines, shared: () => null, defer: () => {} });
130 let runs = 0;
131 await cached(["a"], async () => (runs++, ["x"]));
132 await cached(["a"], async () => (runs++, ["x"]));
133 assert.equal(runs, 1);
134});