146 lines
5.5 KiB
JavaScript
146 lines
5.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { mkdtemp, readFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { test } from "node:test";
|
|
import { buildRefDiff, renderRefDiffMarkdown, validateRefDiff, writeRefDiff } from "../src/ref-diff.js";
|
|
|
|
test("ref diff fails removed plugin-facing hooks and SDK exports", async () => {
|
|
const baseReport = reportFixture({
|
|
hookNames: ["llm_input", "llm_output"],
|
|
sdkExports: ["openclaw/plugin-sdk", "openclaw/plugin-sdk/speech"],
|
|
});
|
|
const headReport = reportFixture({
|
|
hookNames: ["llm_input"],
|
|
sdkExports: ["openclaw/plugin-sdk"],
|
|
});
|
|
|
|
const diff = await buildRefDiff({ baseReport, headReport, baseLabel: "stable", headLabel: "candidate" });
|
|
|
|
assert.equal(diff.status, "fail");
|
|
assert.ok(validateRefDiff(diff).some((error) => error.includes("hookNames.removed-used")));
|
|
assert.ok(validateRefDiff(diff).some((error) => error.includes("sdkExports.removed-used")));
|
|
assert.match(renderRefDiffMarkdown(diff), /Removed used/);
|
|
});
|
|
|
|
test("ref diff reports new P1 issues as hard regressions", async () => {
|
|
const baseReport = reportFixture({ issues: [] });
|
|
const headReport = reportFixture({
|
|
issues: [
|
|
{
|
|
id: "PLUGIN-NEWP1",
|
|
fixture: "fixture",
|
|
severity: "P1",
|
|
code: "sdk-export-missing",
|
|
title: "plugin SDK import aliases are missing from target package exports",
|
|
evidence: ["openclaw/plugin-sdk/example"],
|
|
},
|
|
],
|
|
});
|
|
|
|
const diff = await buildRefDiff({ baseReport, headReport });
|
|
|
|
assert.equal(diff.summary.newP1IssueCount, 1);
|
|
assert.ok(validateRefDiff(diff).some((error) => error.includes("PLUGIN-NEWP1")));
|
|
});
|
|
|
|
test("ref diff strict mode escalates warning regressions", async () => {
|
|
const baseReport = reportFixture({
|
|
sdkExports: ["openclaw/plugin-sdk", "openclaw/plugin-sdk/speech", "openclaw/plugin-sdk/unused"],
|
|
});
|
|
const headReport = reportFixture({
|
|
sdkExports: ["openclaw/plugin-sdk", "openclaw/plugin-sdk/speech"],
|
|
});
|
|
|
|
const diff = await buildRefDiff({ baseReport, headReport });
|
|
|
|
assert.equal(diff.status, "pass");
|
|
assert.equal(diff.summary.warningRegressionCount, 1);
|
|
assert.deepEqual(validateRefDiff(diff), []);
|
|
assert.match(validateRefDiff(diff, { strict: true }).join("\n"), /sdkExports\.removed-unused/);
|
|
});
|
|
|
|
test("ref diff fails when target OpenClaw status regresses", async () => {
|
|
const baseReport = reportFixture();
|
|
const headReport = reportFixture({ targetStatus: "missing" });
|
|
|
|
const diff = await buildRefDiff({ baseReport, headReport });
|
|
|
|
assert.equal(diff.status, "fail");
|
|
assert.match(validateRefDiff(diff).join("\n"), /target\.status\.changed/);
|
|
});
|
|
|
|
test("ref diff writer emits json and markdown artifacts", async () => {
|
|
const dir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-ref-diff-"));
|
|
const jsonPath = path.join(dir, "ref-diff.json");
|
|
const markdownPath = path.join(dir, "ref-diff.md");
|
|
const diff = await buildRefDiff({
|
|
baseReport: reportFixture({ issues: [] }),
|
|
headReport: reportFixture({
|
|
issues: [
|
|
{
|
|
id: "PLUGIN-NEWP2",
|
|
fixture: "fixture",
|
|
severity: "P2",
|
|
code: "manifest-unknown-fields",
|
|
title: "manifest uses unsupported top-level fields",
|
|
evidence: ["extra"],
|
|
},
|
|
],
|
|
}),
|
|
baseLabel: "base",
|
|
headLabel: "head",
|
|
});
|
|
|
|
assert.deepEqual(await writeRefDiff(diff, { jsonPath, markdownPath }), { jsonPath, markdownPath });
|
|
assert.equal(JSON.parse(await readFile(jsonPath, "utf8")).summary.newIssueCount, 1);
|
|
assert.match(await readFile(markdownPath, "utf8"), /PLUGIN-NEWP2/);
|
|
});
|
|
|
|
function reportFixture(overrides = {}) {
|
|
const targetOpenClaw = {
|
|
status: overrides.targetStatus ?? "ok",
|
|
configuredPath: "../openclaw",
|
|
compatRecords: overrides.compatRecords ?? ["legacy-root-sdk-import"],
|
|
hookNames: overrides.hookNames ?? ["llm_input", "llm_output"],
|
|
apiRegistrars: overrides.apiRegistrars ?? ["registerService"],
|
|
capturedRegistrars: overrides.capturedRegistrars ?? ["registerService"],
|
|
sdkExports: overrides.sdkExports ?? ["openclaw/plugin-sdk", "openclaw/plugin-sdk/speech"],
|
|
manifestFields: overrides.manifestFields ?? ["id", "name", "configSchema"],
|
|
manifestContractFields: overrides.manifestContractFields ?? ["tools"],
|
|
};
|
|
|
|
return {
|
|
status: "pass",
|
|
targetOpenClaw: {
|
|
...targetOpenClaw,
|
|
compatRecordCount: targetOpenClaw.compatRecords.length,
|
|
hookNameCount: targetOpenClaw.hookNames.length,
|
|
apiRegistrarCount: targetOpenClaw.apiRegistrars.length,
|
|
capturedRegistrarCount: targetOpenClaw.capturedRegistrars.length,
|
|
sdkExportCount: targetOpenClaw.sdkExports.length,
|
|
manifestFieldCount: targetOpenClaw.manifestFields.length,
|
|
manifestContractFieldCount: targetOpenClaw.manifestContractFields.length,
|
|
},
|
|
summary: {
|
|
breakageCount: 0,
|
|
warningCount: 0,
|
|
suggestionCount: 0,
|
|
issueCount: overrides.issues?.length ?? 0,
|
|
p0IssueCount: overrides.issues?.filter((issue) => issue.severity === "P0").length ?? 0,
|
|
p1IssueCount: overrides.issues?.filter((issue) => issue.severity === "P1").length ?? 0,
|
|
contractProbeCount: 0,
|
|
},
|
|
fixtures: [
|
|
{
|
|
hooks: ["llm_output"],
|
|
registrations: ["registerService"],
|
|
sdkImports: ["openclaw/plugin-sdk/speech"],
|
|
manifestContracts: ["tools"],
|
|
pluginManifests: [{ id: "fixture", name: "Fixture", configSchema: {} }],
|
|
},
|
|
],
|
|
issues: overrides.issues ?? [],
|
|
};
|
|
}
|