Files
nexu-io-open-design/tools/pack/tests/cache.test.ts
PerishFire f9433a7a07 release: harden packaged launcher validation (#3995)
* Unify release metadata publishing

* Allow empty release asset suffix

* Harden beta-s release publishing

* Clean beta Windows release namespace

* Clean Windows launcher namespace

* Retry transient mac notarization uploads

* Control mac notarization upload mode

* Notarize release mac DMGs after build

* Harden release mac DMG notarization retries

* Make beta-s mac updates payload-first

* Bake mac updater metadata URL

* Add unsafe DMG install helper

* Fix beta-s feed and About version

* Improve release validation observability

* Build launcher proto before packaging

* Notarize release-stable mac artifacts

* Enable mac notarization in tools-pack

* test: update packaged release workflow assertions
2026-06-09 11:50:01 +00:00

247 lines
9.7 KiB
TypeScript

import { access, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { ToolPackCache } from "../src/cache.js";
async function pathExists(path: string): Promise<boolean> {
try {
await access(path);
return true;
} catch {
return false;
}
}
describe("ToolPackCache", () => {
it("builds once, materializes copies, and reports cache hits", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-cache-"));
const cacheRoot = join(root, "cache");
const firstOut = join(root, "first", "payload");
const secondOut = join(root, "second", "payload");
let builds = 0;
const cache = new ToolPackCache(cacheRoot);
const node = {
id: "test.node",
key: "key-1",
outputs: ["payload"],
invalidate: async () => null,
build: async ({ entryRoot }: { entryRoot: string }) => {
builds += 1;
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), `build-${builds}\n`, "utf8");
return { builds };
},
};
try {
const firstManifest = await cache.acquire({ materialize: [{ from: "payload", to: firstOut }], node });
await writeFile(join(firstOut, "value.txt"), "mutated\n", "utf8");
await cache.acquire({ materialize: [{ from: "payload", to: secondOut }], node });
expect(builds).toBe(1);
expect(firstManifest.entryPath).toContain("test.node");
expect(await readFile(join(secondOut, "value.txt"), "utf8")).toBe("build-1\n");
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "hit"]);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("rebuilds stale entries when declared outputs are missing", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-cache-stale-"));
const cacheRoot = join(root, "cache");
const out = join(root, "out", "payload");
let builds = 0;
const cache = new ToolPackCache(cacheRoot);
const node = {
id: "test.stale",
key: "key-1",
outputs: ["payload"],
invalidate: async () => null,
build: async ({ entryRoot }: { entryRoot: string }) => {
builds += 1;
if (builds === 1) {
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), "first\n", "utf8");
return { builds };
}
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), "second\n", "utf8");
return { builds };
},
};
try {
await cache.acquire({ materialize: [{ from: "payload", to: out }], node });
const entryPath = cache.report().entries[0]?.entryPath;
expect(entryPath).toBeDefined();
await rm(join(entryPath!, "payload"), { force: true, recursive: true });
await cache.acquire({ materialize: [{ from: "payload", to: out }], node });
expect(builds).toBe(2);
expect(await readFile(join(out, "value.txt"), "utf8")).toBe("second\n");
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "stale"]);
expect(await pathExists(join(cacheRoot, "locks", "global.lock"))).toBe(false);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("reads existing hits without building", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-cache-read-hit-"));
const cacheRoot = join(root, "cache");
const out = join(root, "out", "payload");
let builds = 0;
const cache = new ToolPackCache(cacheRoot);
const node = {
id: "test.read-hit",
key: "key-1",
outputs: ["payload"],
invalidate: async () => null,
build: async ({ entryRoot }: { entryRoot: string }) => {
builds += 1;
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), `build-${builds}\n`, "utf8");
return { builds };
},
};
try {
expect(await cache.readHit({ materialize: [{ from: "payload", to: out }], node })).toBeNull();
await cache.acquire({ materialize: [], node });
const hit = await cache.readHit({ materialize: [{ from: "payload", to: out }], node });
expect(hit?.payloadMetadata.builds).toBe(1);
expect(builds).toBe(1);
expect(await readFile(join(out, "value.txt"), "utf8")).toBe("build-1\n");
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "hit"]);
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("reuses stamped materialized targets until the cache key changes", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-cache-reuse-"));
const cacheRoot = join(root, "cache");
const out = join(root, "out", "payload");
let builds = 0;
const cache = new ToolPackCache(cacheRoot);
const createNode = (key: string) => ({
id: "test.reuse",
key,
outputs: ["payload"],
invalidate: async () => null,
build: async ({ entryRoot }: { entryRoot: string }) => {
builds += 1;
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), `build-${builds}\n`, "utf8");
return { builds };
},
});
try {
await cache.acquire({ materialize: [{ from: "payload", reuse: true, to: out }], node: createNode("key-1") });
await writeFile(join(out, "value.txt"), "mutated\n", "utf8");
await cache.acquire({ materialize: [{ from: "payload", reuse: true, to: out }], node: createNode("key-1") });
expect(await readFile(join(out, "value.txt"), "utf8")).toBe("mutated\n");
expect(cache.report().entries.at(-1)?.materialized[0]?.skipped).toBe(true);
await cache.acquire({ materialize: [{ from: "payload", reuse: true, to: out }], node: createNode("key-2") });
expect(await readFile(join(out, "value.txt"), "utf8")).toBe("build-2\n");
expect(cache.report().entries.at(-1)?.materialized[0]?.skipped).toBeUndefined();
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("seeds a miss from an alias but still builds the exact entry", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-cache-seed-"));
const cacheRoot = join(root, "cache");
const out = join(root, "out", "payload");
let builds = 0;
const cache = new ToolPackCache(cacheRoot);
const createNode = (key: string) => ({
id: "test.seed",
key,
outputs: ["payload"],
invalidate: async () => null,
build: async ({ entryRoot }: { entryRoot: string }) => {
builds += 1;
const seed = await readFile(join(out, "value.txt"), "utf8").catch(() => "<none>\n");
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), `build-${builds};seed=${seed.trim()}\n`, "utf8");
return { builds };
},
});
try {
await cache.acquire({
aliases: ["family"],
materialize: [{ from: "payload", reuse: true, to: out }],
node: createNode("key-1"),
});
await rm(out, { force: true, recursive: true });
await cache.acquire({
aliases: ["family"],
materialize: [{ from: "payload", reuse: true, to: out }],
node: createNode("key-2"),
seedFrom: [{ aliasKey: "family", materialize: [{ from: "payload", to: out }] }],
});
expect(builds).toBe(2);
expect(await readFile(join(out, "value.txt"), "utf8")).toBe("build-2;seed=build-1;seed=<none>\n");
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "miss"]);
expect(cache.report().entries.at(-1)?.reason).toContain("seeded from alias");
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("skips alias seeds that cannot materialize the requested source", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-tools-pack-cache-missing-seed-source-"));
const cacheRoot = join(root, "cache");
const out = join(root, "out", "payload");
let builds = 0;
const cache = new ToolPackCache(cacheRoot);
const createNode = (key: string) => ({
id: "test.missing-seed-source",
key,
outputs: ["payload"],
invalidate: async () => null,
build: async ({ entryRoot }: { entryRoot: string }) => {
builds += 1;
const seed = await readFile(join(out, "value.txt"), "utf8").catch(() => "<none>\n");
await mkdir(join(entryRoot, "payload"), { recursive: true });
await writeFile(join(entryRoot, "payload", "value.txt"), `build-${builds};seed=${seed.trim()}\n`, "utf8");
return { builds };
},
});
try {
await cache.acquire({
aliases: ["family"],
materialize: [{ from: "payload", to: out }],
node: createNode("key-1"),
});
await rm(out, { force: true, recursive: true });
await cache.acquire({
aliases: ["family"],
materialize: [{ from: "payload", to: out }],
node: createNode("key-2"),
seedFrom: [{ aliasKey: "family", materialize: [{ from: "missing", to: out }] }],
});
expect(builds).toBe(2);
expect(await readFile(join(out, "value.txt"), "utf8")).toBe("build-2;seed=<none>\n");
expect(cache.report().entries.at(-1)?.reason).not.toContain("seeded from alias");
} finally {
await rm(root, { force: true, recursive: true });
}
});
});