Files
openclaw/src/plugins/public-surface-runtime.test.ts
Vincent Koc e3cba98f39 refactor(pdf): move document extraction to plugin
* refactor(pdf): move document extraction to plugin

* fix(deps): sync document extract lockfile

* fix(pdf): harden document extraction plugin
2026-04-24 17:15:05 -07:00

109 lines
3.8 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 {
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
normalizeBundledPluginArtifactSubpath,
normalizeBundledPluginDirName,
resolveBundledPluginPublicSurfacePath,
resolveBundledPluginSourcePublicSurfacePath,
} from "./public-surface-runtime.js";
const tempDirs: string[] = [];
afterEach(() => {
for (const tempDir of tempDirs.splice(0)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
function createTempDir(): string {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-public-surface-runtime-"));
tempDirs.push(tempDir);
return tempDir;
}
describe("bundled plugin public surface runtime", () => {
it("exports the canonical public surface source extension list", () => {
expect(PUBLIC_SURFACE_SOURCE_EXTENSIONS).toEqual([
".ts",
".mts",
".js",
".mjs",
".cts",
".cjs",
]);
});
it("resolves source public surfaces from the shared extension list", () => {
const sourceRoot = createTempDir();
const modulePath = path.join(sourceRoot, "demo", "api.mts");
fs.mkdirSync(path.dirname(modulePath), { recursive: true });
fs.writeFileSync(modulePath, "export {};\n", "utf8");
expect(
resolveBundledPluginSourcePublicSurfacePath({
sourceRoot,
dirName: "demo",
artifactBasename: "api.js",
}),
).toBe(modulePath);
});
it("falls back from package dist overrides to the source extension tree", () => {
const packageRoot = createTempDir();
const sourceModulePath = path.join(packageRoot, "extensions", "demo", "api.ts");
fs.mkdirSync(path.dirname(sourceModulePath), { recursive: true });
fs.writeFileSync(sourceModulePath, "export const marker = 'source';\n", "utf8");
const bundledPluginsDir = path.join(packageRoot, "dist", "extensions");
fs.mkdirSync(path.join(bundledPluginsDir, "demo"), { recursive: true });
expect(
resolveBundledPluginPublicSurfacePath({
rootDir: packageRoot,
bundledPluginsDir,
dirName: "demo",
artifactBasename: "api.js",
}),
).toBe(sourceModulePath);
});
it("allows plugin-local nested artifact paths", () => {
expect(normalizeBundledPluginArtifactSubpath("src/outbound-adapter.js")).toBe(
"src/outbound-adapter.js",
);
expect(normalizeBundledPluginArtifactSubpath("./test-api.js")).toBe("test-api.js");
});
it("rejects artifact paths that escape the plugin root", () => {
expect(() => normalizeBundledPluginArtifactSubpath("../outside.js")).toThrow(
/must stay plugin-local/,
);
expect(() => normalizeBundledPluginArtifactSubpath("src/../outside.js")).toThrow(
/must stay plugin-local/,
);
expect(() => normalizeBundledPluginArtifactSubpath("/tmp/outside.js")).toThrow(
/must stay plugin-local/,
);
expect(() => normalizeBundledPluginArtifactSubpath("..\\outside.js")).toThrow(
/must stay plugin-local/,
);
expect(() => normalizeBundledPluginArtifactSubpath("C:outside.js")).toThrow(
/must stay plugin-local/,
);
expect(() => normalizeBundledPluginArtifactSubpath("src/C:outside.js")).toThrow(
/must stay plugin-local/,
);
});
it("rejects bundled plugin directory traversal", () => {
expect(normalizeBundledPluginDirName("document-extract")).toBe("document-extract");
expect(() => normalizeBundledPluginDirName("../outside")).toThrow(/single directory/);
expect(() => normalizeBundledPluginDirName("nested/plugin")).toThrow(/single directory/);
expect(() => normalizeBundledPluginDirName("nested\\plugin")).toThrow(/single directory/);
expect(() => normalizeBundledPluginDirName("C:plugin")).toThrow(/single directory/);
});
});