plugin-inspector/test/workspace-plan.test.js
2026-05-02 10:51:35 -07:00

275 lines
11 KiB
JavaScript

import assert from "node:assert/strict";
import { access, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import {
buildColdImportReadiness,
buildWorkspacePlan,
renderWorkspacePlanMarkdown,
validateWorkspacePlan,
} from "../src/advanced.js";
test("workspace plan maps blocked entrypoints to opt-in install/build/capture steps", async (t) => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-workspace-"));
t.after(() => rm(rootDir, { recursive: true, force: true }));
await mkdir(path.join(rootDir, "plugins/fixture"), { recursive: true });
await writeFile(
path.join(rootDir, "plugins/fixture/package.json"),
JSON.stringify(
{
name: "fixture",
packageManager: "npm@10.0.0",
scripts: { build: "tsup" },
dependencies: { "left-pad": "^1.3.0", openclaw: "^1.0.0" },
devDependencies: { "@openclaw/plugin-sdk": "workspace:*" },
},
null,
2,
),
"utf8",
);
await writeFile(path.join(rootDir, "plugins/fixture/package-lock.json"), "{}\n", "utf8");
await mkdir(path.join(rootDir, "plugins/build"), { recursive: true });
await writeFile(
path.join(rootDir, "plugins/build/package.json"),
JSON.stringify({ name: "build-fixture", scripts: { build: "tsup" } }, null, 2),
"utf8",
);
const report = readinessReport();
const readiness = buildColdImportReadiness({ report, rootDir });
const plan = await buildWorkspacePlan({
report,
readiness,
rootDir,
optInEnv: "TEST_EXEC=1",
workspaceRoot: ".workspaces",
resultsRoot: ".results",
captureScript: "capture.mjs",
syntheticProbeScript: "synthetic.mjs",
});
assert.deepEqual(validateWorkspacePlan(plan, { optInEnv: "TEST_EXEC=1" }), []);
assert.equal(plan.mode, "plan-only");
assert.equal(plan.optIn.env, "TEST_EXEC=1");
assert.equal(plan.summary.entrypointCount, 2);
assert.equal(plan.summary.artifactStepCount, 2);
assert.equal(plan.summary.installStepCount, 1);
assert.equal(plan.summary.auditStepCount, 1);
assert.equal(plan.summary.pruneDevWorkspaceDependencyStepCount, 1);
assert.equal(plan.summary.buildStepCount, 1);
assert.equal(plan.summary.captureStepCount, 2);
assert.equal(plan.summary.syntheticProbeStepCount, 2);
assert.equal(plan.summary.targetOpenClawLinkStepCount, 1);
assert.equal(plan.summary.tsLoaderEntrypointCount, 1);
assert.equal(plan.summary.jitiAlternativeCount, 1);
const entrypoint = plan.fixtures[0].entrypoints.find((item) => item.packageName === "fixture");
assert.ok(entrypoint);
assert.equal(entrypoint.packageManager, "npm");
assert.equal(entrypoint.lockfile, "plugins/fixture/package-lock.json");
assert.ok(entrypoint.requiredCapabilities.includes("target-openclaw-link"));
assert.ok(entrypoint.requiredCapabilities.includes("dependency-install"));
assert.ok(entrypoint.requiredCapabilities.includes("sdk-alias-compat"));
assert.ok(entrypoint.requiredCapabilities.includes("ts-loader"));
assert.ok(entrypoint.steps.some((step) => step.kind === "install" && step.command === "npm install --ignore-scripts"));
assert.ok(
entrypoint.steps.some(
(step) => step.kind === "prune-dev-workspace-deps" && step.command.includes("prune-workspace-dev-deps-cli.js"),
),
);
assert.ok(entrypoint.steps.some((step) => step.kind === "capture" && step.command.includes("node capture.mjs")));
assert.ok(entrypoint.steps.some((step) => step.kind === "capture" && step.command.includes("--mock-sdk")));
assert.ok(entrypoint.steps.every((step) => !step.command.includes("--import tsx")));
assert.ok(entrypoint.steps.some((step) => step.kind === "synthetic-probe" && step.command.includes("synthetic.mjs")));
const buildEntrypoint = plan.fixtures[0].entrypoints.find((item) => item.packageName === "build-fixture");
assert.ok(buildEntrypoint);
assert.ok(buildEntrypoint.requiredCapabilities.includes("build"));
assert.ok(buildEntrypoint.steps.some((step) => step.kind === "build" && step.command === "npm run build"));
assert.match(renderWorkspacePlanMarkdown(plan), /Entrypoint Workspaces/);
});
test("workspace plan defaults point at packaged helper wrappers", async (t) => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-workspace-defaults-"));
t.after(() => rm(rootDir, { recursive: true, force: true }));
await mkdir(path.join(rootDir, "plugins/fixture"), { recursive: true });
await writeFile(
path.join(rootDir, "plugins/fixture/package.json"),
JSON.stringify(
{
name: "fixture",
packageManager: "npm@10.0.0",
scripts: { build: "tsup" },
dependencies: { "left-pad": "^1.3.0", openclaw: "^1.0.0" },
},
null,
2,
),
"utf8",
);
await writeFile(path.join(rootDir, "plugins/fixture/package-lock.json"), "{}\n", "utf8");
await mkdir(path.join(rootDir, "plugins/build"), { recursive: true });
await writeFile(
path.join(rootDir, "plugins/build/package.json"),
JSON.stringify({ name: "build-fixture", scripts: { build: "tsup" } }, null, 2),
"utf8",
);
const report = readinessReport();
const readiness = buildColdImportReadiness({ report, rootDir });
const plan = await buildWorkspacePlan({ report, readiness, rootDir });
const entrypoint = plan.fixtures[0].entrypoints[0];
const captureStep = entrypoint.steps.find((step) => step.kind === "capture");
const syntheticStep = entrypoint.steps.find((step) => step.kind === "synthetic-probe");
assert.ok(captureStep.command.includes("src/capture-cli.js"));
assert.ok(captureStep.command.includes("--mock-sdk"));
assert.ok(syntheticStep.command.includes("src/synthetic-probes-cli.js"));
assert.ok(syntheticStep.command.includes("--entrypoint ./src/index.ts"));
assert.ok(syntheticStep.command.includes("--mock-sdk"));
assert.ok(syntheticStep.command.includes(".synthetic.json"));
const captureHelper = resolveNodeScriptFromStep(captureStep, rootDir);
const syntheticHelper = resolveNodeScriptFromStep(syntheticStep, rootDir);
await access(captureHelper);
await access(syntheticHelper);
assert.match(captureHelper, /src[\\/]capture-cli\.js$/);
assert.match(syntheticHelper, /src[\\/]synthetic-probes-cli\.js$/);
});
test("workspace plan validation keeps execution opt-in and explicit", () => {
const plan = {
mode: "execute",
optIn: { env: "NOPE=1" },
fixtures: [
{
id: "fixture",
entrypoints: [
{
id: "cold-import.extension:fixture:index",
packagePath: "plugins/fixture/package.json",
entrypoint: "plugins/fixture/index.js",
loaderStrategy: { primary: "node", alternatives: [], reason: "test" },
requiredCapabilities: ["dependency-install"],
blockers: [],
steps: [{ kind: "capture", command: "node capture.js", cwd: ".", reason: "capture" }],
},
],
},
],
};
const errors = validateWorkspacePlan(plan);
assert.ok(errors.some((error) => error.includes("plan-only")));
assert.ok(errors.some((error) => error.includes("PLUGIN_INSPECTOR_EXECUTE_ISOLATED")));
assert.ok(errors.some((error) => error.includes("missing prepare step")));
assert.ok(errors.some((error) => error.includes("dependency install capability has no install step")));
assert.ok(errors.some((error) => error.includes("dependency install capability has no audit step")));
plan.fixtures[0].entrypoints[0].loaderStrategy = null;
const loaderErrors = validateWorkspacePlan(plan);
assert.ok(loaderErrors.some((error) => error.includes("missing loader strategy")));
plan.fixtures[0].entrypoints[0].loaderStrategy = { primary: "node", alternatives: [], reason: "test" };
plan.fixtures[0].entrypoints[0].requiredCapabilities = ["target-openclaw-link"];
const linkErrors = validateWorkspacePlan(plan);
assert.ok(linkErrors.some((error) => error.includes("target-openclaw-link capability has no link-openclaw step")));
plan.fixtures[0].entrypoints[0].requiredCapabilities = ["build", "ts-loader"];
const buildErrors = validateWorkspacePlan(plan);
assert.ok(buildErrors.some((error) => error.includes("build capability has no build step")));
assert.ok(buildErrors.some((error) => error.includes("jiti fallback")));
plan.fixtures[0].entrypoints[0].requiredCapabilities = [];
plan.fixtures[0].entrypoints[0].steps = [{ kind: "prepare", command: "", cwd: ".", reason: "" }];
const stepErrors = validateWorkspacePlan(plan);
assert.ok(stepErrors.some((error) => error.includes("prepare step missing command, cwd, or reason")));
});
function resolveNodeScriptFromStep(step, rootDir) {
const tokens = step.command.trim().split(/\s+/);
const nodeIndex = tokens.findIndex((token) => token === "node");
assert.notEqual(nodeIndex, -1, `expected node invocation in command: ${step.command}`);
let scriptToken = null;
for (let index = nodeIndex + 1; index < tokens.length; index += 1) {
const token = tokens[index];
if (token === "--import") {
index += 1;
continue;
}
if (token.startsWith("-")) {
continue;
}
scriptToken = token;
break;
}
assert.ok(scriptToken, `expected script path in command: ${step.command}`);
return path.resolve(rootDir, step.cwd, scriptToken);
}
function readinessReport() {
return {
generatedAt: "test",
targetOpenClaw: {
status: "ok",
configuredPath: "../openclaw",
sdkExports: ["openclaw/plugin-sdk"],
sdkExportCount: 1,
},
fixtures: [
{
id: "fixture",
priority: "high",
sdkImportDetails: [
{
specifier: "openclaw/plugin-sdk/legacy-helper",
ref: "plugins/fixture/src/index.ts:1",
},
],
packages: [
{
path: "plugins\\fixture\\package.json",
name: "fixture",
dependencies: ["left-pad", "openclaw"],
peerDependencies: [],
optionalDependencies: [],
openclaw: {
entrypoints: [
{
kind: "extension",
specifier: "./src/index.ts",
relativePath: "plugins/fixture/src/index.ts",
exists: true,
},
],
},
},
{
path: "plugins\\build\\package.json",
name: "build-fixture",
dependencies: [],
peerDependencies: [],
optionalDependencies: [],
openclaw: {
entrypoints: [
{
kind: "extension",
specifier: "./dist/index.js",
relativePath: "plugins/build/dist/index.js",
exists: false,
requiresBuild: true,
},
],
},
},
],
},
],
};
}