mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 05:16:17 +00:00
147 lines
5.4 KiB
TypeScript
147 lines
5.4 KiB
TypeScript
import { spawnSync } from "node:child_process";
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const ASSERTIONS_SCRIPT = "scripts/e2e/lib/kitchen-sink-plugin/assertions.mjs";
|
|
const REQUIRED_FULL_DIAGNOSTIC_CANARIES = [
|
|
"only bundled plugins can register trusted tool policies",
|
|
"plugin must declare contracts.tools for: kitchen-sink-tool",
|
|
'channel "kitchen-sink-channel-probe" registration missing required config helpers',
|
|
'agent harness "kitchen-sink-agent-harness" registration missing required runtime methods',
|
|
"session scheduler job registration requires unique id, sessionKey, and kind",
|
|
];
|
|
|
|
function writeJson(filePath: string, value: unknown) {
|
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
}
|
|
|
|
function fullSurfaceInspectPayload(pluginId: string) {
|
|
return {
|
|
commands: ["kitchen"],
|
|
diagnostics: [],
|
|
plugin: {
|
|
id: pluginId,
|
|
enabled: true,
|
|
status: "loaded",
|
|
channelIds: ["kitchen-sink-channel"],
|
|
providerIds: ["kitchen-sink-provider"],
|
|
speechProviderIds: ["kitchen-sink-speech"],
|
|
realtimeTranscriptionProviderIds: ["kitchen-sink-realtime-transcription"],
|
|
realtimeVoiceProviderIds: ["kitchen-sink-realtime-voice"],
|
|
mediaUnderstandingProviderIds: ["kitchen-sink-media"],
|
|
imageGenerationProviderIds: ["kitchen-sink-image"],
|
|
videoGenerationProviderIds: ["kitchen-sink-video"],
|
|
musicGenerationProviderIds: ["kitchen-sink-music"],
|
|
webFetchProviderIds: ["kitchen-sink-fetch"],
|
|
webSearchProviderIds: ["kitchen-sink-search"],
|
|
migrationProviderIds: ["kitchen-sink-migration-providers"],
|
|
agentHarnessIds: [],
|
|
hookCount: 30,
|
|
},
|
|
services: ["kitchen-sink-service"],
|
|
tools: [{ names: ["kitchen_sink_text"] }],
|
|
typedHooks: Array.from({ length: 30 }, (_, index) => `hook-${index}`),
|
|
};
|
|
}
|
|
|
|
function diagnosticErrors(messages: string[]) {
|
|
return messages.map((message) => ({ level: "error", message }));
|
|
}
|
|
|
|
function runAssertInstalled({
|
|
diagnostics = [],
|
|
env = {},
|
|
}: {
|
|
diagnostics?: Array<{ level: string; message: string }>;
|
|
env?: NodeJS.ProcessEnv;
|
|
} = {}) {
|
|
const label = `diagnostics-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
const pluginId = "openclaw-kitchen-sink-fixture";
|
|
const home = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-sink-home-"));
|
|
const installPath = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-sink-install-"));
|
|
const scratchRoot = tmpdir();
|
|
const pluginsJsonPath = path.join(scratchRoot, `kitchen-sink-${label}-plugins.json`);
|
|
const inspectJsonPath = path.join(scratchRoot, `kitchen-sink-${label}-inspect.json`);
|
|
const inspectAllJsonPath = path.join(scratchRoot, `kitchen-sink-${label}-inspect-all.json`);
|
|
const installPathMarker = path.join(scratchRoot, `kitchen-sink-${label}-install-path.txt`);
|
|
const installsPath = path.join(home, ".openclaw", "plugins", "installs.json");
|
|
const spawnEnv = { ...process.env };
|
|
delete spawnEnv.KITCHEN_SINK_REQUIRE_ALL_DIAGNOSTICS;
|
|
|
|
try {
|
|
writeJson(pluginsJsonPath, {
|
|
diagnostics,
|
|
plugins: [{ id: pluginId, status: "loaded" }],
|
|
});
|
|
writeJson(inspectJsonPath, fullSurfaceInspectPayload(pluginId));
|
|
writeJson(inspectAllJsonPath, { diagnostics: [] });
|
|
writeJson(installsPath, {
|
|
installRecords: {
|
|
[pluginId]: {
|
|
installPath,
|
|
resolvedSpec: "@openclaw/kitchen-sink@latest",
|
|
resolvedVersion: "1.0.0",
|
|
source: "npm",
|
|
spec: "@openclaw/kitchen-sink@latest",
|
|
},
|
|
},
|
|
});
|
|
|
|
return spawnSync(process.execPath, [ASSERTIONS_SCRIPT, "assert-installed"], {
|
|
encoding: "utf8",
|
|
env: {
|
|
...spawnEnv,
|
|
...env,
|
|
HOME: home,
|
|
KITCHEN_SINK_ID: pluginId,
|
|
KITCHEN_SINK_LABEL: label,
|
|
KITCHEN_SINK_SOURCE: "npm",
|
|
KITCHEN_SINK_SPEC: "npm:@openclaw/kitchen-sink@latest",
|
|
KITCHEN_SINK_SURFACE_MODE: "full",
|
|
KITCHEN_SINK_TMP_DIR: scratchRoot,
|
|
},
|
|
});
|
|
} finally {
|
|
rmSync(home, { force: true, recursive: true });
|
|
rmSync(installPath, { force: true, recursive: true });
|
|
rmSync(pluginsJsonPath, { force: true });
|
|
rmSync(inspectJsonPath, { force: true });
|
|
rmSync(inspectAllJsonPath, { force: true });
|
|
rmSync(installPathMarker, { force: true });
|
|
}
|
|
}
|
|
|
|
describe("kitchen-sink plugin assertions", () => {
|
|
it("fails full-surface installs when stable diagnostic canaries disappear", () => {
|
|
const result = runAssertInstalled();
|
|
|
|
expect(result.status).not.toBe(0);
|
|
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
|
"missing expected kitchen-sink diagnostic error",
|
|
);
|
|
});
|
|
|
|
it("accepts published full-surface installs with stable diagnostic canaries", () => {
|
|
const result = runAssertInstalled({
|
|
diagnostics: diagnosticErrors(REQUIRED_FULL_DIAGNOSTIC_CANARIES),
|
|
});
|
|
|
|
expect(result.status).toBe(0);
|
|
});
|
|
|
|
it("keeps exhaustive diagnostic matching available for synchronized fixtures", () => {
|
|
const result = runAssertInstalled({
|
|
diagnostics: diagnosticErrors(REQUIRED_FULL_DIAGNOSTIC_CANARIES),
|
|
env: { KITCHEN_SINK_REQUIRE_ALL_DIAGNOSTICS: "1" },
|
|
});
|
|
|
|
expect(result.status).not.toBe(0);
|
|
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
|
"cli registration missing explicit commands metadata",
|
|
);
|
|
});
|
|
});
|