Files
openclaw/src/plugins/plugin-runtime-artifact-identity.test.ts
2026-07-11 17:51:19 -07:00

104 lines
4.5 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { fingerprintPluginRuntimeArtifact } from "./plugin-runtime-artifact-identity.js";
const tempDirs: string[] = [];
function createPluginFixture(): { rootDir: string; source: string } {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-artifact-"));
tempDirs.push(rootDir);
const source = path.join(rootDir, "dist", "index.js");
fs.mkdirSync(path.dirname(source), { recursive: true });
fs.writeFileSync(source, 'export { run } from "./runtime.js";\n', "utf8");
fs.writeFileSync(path.join(rootDir, "dist", "runtime.js"), "export const run = () => 1;\n");
fs.writeFileSync(path.join(rootDir, "package.json"), '{"name":"fixture"}\n');
return { rootDir, source };
}
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { recursive: true, force: true });
}
});
describe("fingerprintPluginRuntimeArtifact", () => {
it("is stable for unchanged files and changes with plugin-owned imported code", () => {
const fixture = createPluginFixture();
const record = { pluginId: "fixture", origin: "global" as const, ...fixture };
const first = fingerprintPluginRuntimeArtifact(record);
expect(fingerprintPluginRuntimeArtifact(record)).toBe(first);
fs.writeFileSync(
path.join(fixture.rootDir, "dist", "runtime.js"),
"export const run = () => 2;\n",
);
expect(fingerprintPluginRuntimeArtifact(record)).not.toBe(first);
});
it("keeps dependency stores outside the plugin-owned artifact identity", () => {
const fixture = createPluginFixture();
const dependency = path.join(fixture.rootDir, "node_modules", "dependency", "index.js");
fs.mkdirSync(path.dirname(dependency), { recursive: true });
fs.writeFileSync(dependency, "export const value = 1;\n");
const record = { pluginId: "fixture", origin: "global" as const, ...fixture };
const first = fingerprintPluginRuntimeArtifact(record);
fs.writeFileSync(dependency, "export const value = 2;\n");
expect(fingerprintPluginRuntimeArtifact(record)).toBe(first);
});
it("hashes canonical dist content when the registry points at dist-runtime", () => {
const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-artifact-"));
tempDirs.push(packageRoot);
const stagingRoot = path.join(packageRoot, "dist-runtime", "extensions", "fixture");
const canonicalRoot = path.join(packageRoot, "dist", "extensions", "fixture");
const stagingSource = path.join(stagingRoot, "index.js");
const canonicalSource = path.join(canonicalRoot, "index.js");
fs.mkdirSync(stagingRoot, { recursive: true });
fs.mkdirSync(canonicalRoot, { recursive: true });
fs.writeFileSync(stagingSource, "export const revision = 'staging-1';\n");
fs.writeFileSync(canonicalSource, "export const revision = 'canonical-1';\n");
const record = {
pluginId: "fixture",
origin: "bundled" as const,
rootDir: stagingRoot,
source: stagingSource,
};
const first = fingerprintPluginRuntimeArtifact(record);
fs.writeFileSync(stagingSource, "export const revision = 'staging-2';\n");
expect(fingerprintPluginRuntimeArtifact(record)).toBe(first);
fs.writeFileSync(canonicalSource, "export const revision = 'canonical-2';\n");
expect(fingerprintPluginRuntimeArtifact(record)).not.toBe(first);
});
it("hashes source when a bundled plugin opts out of core dist", () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-artifact-"));
tempDirs.push(rootDir);
const source = path.join(rootDir, "index.ts");
const staleDistSource = path.join(rootDir, "dist", "index.js");
fs.mkdirSync(path.dirname(staleDistSource), { recursive: true });
fs.writeFileSync(source, "export const revision = 'source-1';\n");
fs.writeFileSync(staleDistSource, "export const revision = 'stale-1';\n");
const record = {
pluginId: "fixture",
origin: "bundled" as const,
rootDir,
source,
packageBuild: { bundledDist: false },
};
const first = fingerprintPluginRuntimeArtifact(record);
fs.writeFileSync(staleDistSource, "export const revision = 'stale-2';\n");
expect(fingerprintPluginRuntimeArtifact(record)).not.toBe(first);
const afterStaleChange = fingerprintPluginRuntimeArtifact(record);
fs.writeFileSync(source, "export const revision = 'source-2';\n");
expect(fingerprintPluginRuntimeArtifact(record)).not.toBe(afterStaleChange);
});
});