refactor(tests): share kitchen sink harness

This commit is contained in:
Vincent Koc 2026-04-29 14:13:22 -07:00
parent dcf61bc18c
commit da085688fd
No known key found for this signature in database
5 changed files with 167 additions and 219 deletions

View File

@ -31,7 +31,7 @@ try {
assert.equal(installedPackageJson.version, JSON.parse(readFileSync("package.json", "utf8")).version);
const probeFile = path.join(projectDir, "probe.mjs");
writeFileSync(probeFile, consumerProbeSource());
writeFileSync(probeFile, readFileSync(new URL("./fixtures/installed-consumer-probe.mjs", import.meta.url), "utf8"));
run(process.execPath, [probeFile], { cwd: projectDir });
const inspectorBin = path.join(repoRoot, "node_modules", ".bin", "plugin-inspector");
@ -66,67 +66,3 @@ function run(command, args, options = {}) {
process.exit(result.status ?? 1);
}
}
function consumerProbeSource() {
return String.raw`
import assert from "node:assert/strict";
import { plugin } from "@openclaw/kitchen-sink";
import { createKitchenSinkRuntime } from "@openclaw/kitchen-sink/runtime";
import { createKitchenSinkImageAsset, kitchenPromptGuidance } from "@openclaw/kitchen-sink/scenarios";
import setup from "@openclaw/kitchen-sink/setup";
const registrations = {};
const api = new Proxy(
{ id: "consumer-install-smoke", registrationMode: "full", config: {}, logger: console },
{
get(target, property) {
if (property in target) return target[property];
if (property === "on") {
return (...args) => {
registrations.on ??= [];
registrations.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) return undefined;
return (...args) => {
registrations[property] ??= [];
registrations[property].push(args);
};
},
},
);
plugin.register(api);
assert.equal(plugin.id, "openclaw-kitchen-sink-fixture");
assert.ok(registrations.registerImageGenerationProvider?.some(([provider]) => provider.id === "kitchen-sink-image"));
assert.ok(registrations.registerProvider?.some(([provider]) => provider.id === "kitchen-sink-llm"));
assert.ok(registrations.registerWebSearchProvider?.some(([provider]) => provider.id === "kitchen-sink-search"));
assert.ok(registrations.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"));
const runtime = createKitchenSinkRuntime({
delayMs: 10_000,
sleep: async () => {},
now: (() => {
let tick = 0;
return () => new Date(Date.UTC(2026, 3, 29, 12, 0, tick++));
})(),
});
const image = await runtime.runImageJob({ prompt: "generate an image with kitchen sink" });
assert.equal(image.job.status, "completed");
assert.equal(image.image.metadata.assetName, "kitchen_sink_office.png");
assert.equal(image.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
const directImage = createKitchenSinkImageAsset({
prompt: "consumer import smoke",
jobId: "ks_consumer_install_smoke",
});
assert.equal(directImage.mimeType, "image/png");
assert.ok(directImage.dataUrl.startsWith("data:image/png;base64,"));
assert.ok(kitchenPromptGuidance().some((line) => line.includes("kitchen_sink_image_job")));
assert.equal(setup.id, "openclaw-kitchen-sink-setup");
assert.equal(typeof setup.setup, "function");
const setupResult = await setup.setup({ config: {} });
assert.equal(setupResult.configured, true);
`;
}

View File

@ -3,32 +3,14 @@
import assert from "node:assert/strict";
import { mkdirSync, writeFileSync } from "node:fs";
import { plugin } from "../src/index.js";
import {
capturePluginRegistration,
createHookFinder,
registrationSummary,
} from "./lib/plugin-registration-harness.mjs";
const registrations = {};
const api = new Proxy(
{
id: "openclaw-kitchen-sink-fixture",
registrationMode: "full",
config: {},
logger: console,
},
{
get(target, property) {
if (property in target) {
return target[property];
}
if (property === "on") {
return (...args) => capture("on", args);
}
if (typeof property !== "string" || !property.startsWith("register")) {
return undefined;
}
return (...args) => capture(property, args);
},
},
);
plugin.register(api);
const registrations = capturePluginRegistration(plugin);
const findHook = createHookFinder(registrations);
const beforeToolCall = findHook("before_tool_call");
const llmInput = findHook("llm_input");
@ -49,7 +31,7 @@ const probes = {
end: await agentEnd(secretEvent("kitchen final answer"), secretContext()),
},
channel: await captureChannelProbe(),
runtimeRegistrations: registrationSummary(),
runtimeRegistrations: registrationSummary(registrations),
};
assert.equal(probes.beforeToolCall.allow.decision, "allow");
@ -90,50 +72,6 @@ console.log(
`Kitchen contract probes OK: ${Object.keys(probes.runtimeRegistrations).length} registration methods, before_tool_call allow/block/approval, conversation privacy, channel envelope`,
);
function capture(method, args) {
registrations[method] ??= [];
registrations[method].push(args);
}
function findHook(name) {
const entry = registrations.on?.find(([hookName]) => hookName === name);
assert.ok(entry, `hook ${name} registered`);
return entry[1];
}
function registrationSummary() {
return Object.fromEntries(
Object.entries(registrations)
.filter(([method]) => method !== "on")
.sort(([a], [b]) => a.localeCompare(b))
.map(([method, entries]) => [
method,
{
count: entries.length,
ids: entries.map((args) => idForRegistration(method, args)).filter(Boolean).sort(),
},
]),
);
}
function idForRegistration(method, args) {
const [value, second] = args;
if (method === "registerGatewayMethod" && typeof value === "string") {
return value;
}
if (method === "registerCli" && second?.descriptors?.length > 0) {
return second.descriptors.map((descriptor) => descriptor.name).join(", ");
}
if (value?.id || value?.name) {
return value.id || value.name;
}
if (typeof second === "string") {
return second;
}
const slug = method.slice("register".length).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
return `kitchen-sink-${slug}`;
}
async function captureChannelProbe() {
const channel = registrations.registerChannel?.map(([value]) => value).find((value) => value.id === "kitchen-sink-channel");
assert.ok(channel, "kitchen-sink-channel registered");

View File

@ -2,38 +2,16 @@
import assert from "node:assert/strict";
import { plugin } from "../src/index.js";
import {
capturePluginRegistration,
createHookFinder,
createRegistrationFinder,
fixedNow,
} from "./lib/plugin-registration-harness.mjs";
const registrations = {};
const api = new Proxy(
{
id: "openclaw-kitchen-sink-fixture",
registrationMode: "full",
config: {},
logger: console,
},
{
get(target, property) {
if (property in target) {
return target[property];
}
if (property === "on") {
return (...args) => {
registrations.on ??= [];
registrations.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) {
return undefined;
}
return (...args) => {
registrations[property] ??= [];
registrations[property].push(args);
};
},
},
);
plugin.register(api);
const registrations = capturePluginRegistration(plugin);
const findRegistration = createRegistrationFinder(registrations);
const findHook = createHookFinder(registrations);
const commands = registrations.registerCommand?.map(([command]) => command) ?? [];
assert.ok(commands.some((command) => command.name === "kitchen"), "registers kitchen command");
@ -400,7 +378,7 @@ assert.ok(
),
);
const conformance = capturePluginRegistration({ personality: "conformance" });
const conformance = capturePluginRegistration(plugin, { personality: "conformance" });
assert.ok(
conformance.registerCommand?.some(([command]) => command.name === "kitchen"),
"conformance registers usable commands",
@ -415,7 +393,7 @@ assert.equal(
false,
);
const adversarial = capturePluginRegistration({ personality: "adversarial" });
const adversarial = capturePluginRegistration(plugin, { personality: "adversarial" });
assert.equal(
adversarial.registerCommand?.some(([command]) => command.name === "kitchen"),
false,
@ -434,54 +412,3 @@ assert.ok(
);
console.log("Kitchen runtime OK");
function capturePluginRegistration(config) {
const captured = {};
const captureApi = new Proxy(
{
id: "openclaw-kitchen-sink-fixture",
registrationMode: "full",
config,
logger: console,
},
{
get(target, property) {
if (property in target) {
return target[property];
}
if (property === "on") {
return (...args) => {
captured.on ??= [];
captured.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) {
return undefined;
}
return (...args) => {
captured[property] ??= [];
captured[property].push(args);
};
},
},
);
plugin.register(captureApi);
return captured;
}
function findRegistration(method, id) {
const entry = registrations[method]?.map(([value]) => value).find((value) => value?.id === id);
assert.ok(entry, `${method} ${id} registered`);
return entry;
}
function findHook(name) {
const entry = registrations.on?.find(([hookName]) => hookName === name);
assert.ok(entry, `hook ${name} registered`);
return entry[1];
}
function fixedNow() {
let tick = 0;
return () => new Date(Date.UTC(2026, 3, 28, 12, 0, tick++));
}

View File

@ -0,0 +1,59 @@
import assert from "node:assert/strict";
import { plugin } from "@openclaw/kitchen-sink";
import { createKitchenSinkRuntime } from "@openclaw/kitchen-sink/runtime";
import { createKitchenSinkImageAsset, kitchenPromptGuidance } from "@openclaw/kitchen-sink/scenarios";
import setup from "@openclaw/kitchen-sink/setup";
const registrations = {};
const api = new Proxy(
{ id: "consumer-install-smoke", registrationMode: "full", config: {}, logger: console },
{
get(target, property) {
if (property in target) return target[property];
if (property === "on") {
return (...args) => {
registrations.on ??= [];
registrations.on.push(args);
};
}
if (typeof property !== "string" || !property.startsWith("register")) return undefined;
return (...args) => {
registrations[property] ??= [];
registrations[property].push(args);
};
},
},
);
plugin.register(api);
assert.equal(plugin.id, "openclaw-kitchen-sink-fixture");
assert.ok(registrations.registerImageGenerationProvider?.some(([provider]) => provider.id === "kitchen-sink-image"));
assert.ok(registrations.registerProvider?.some(([provider]) => provider.id === "kitchen-sink-llm"));
assert.ok(registrations.registerWebSearchProvider?.some(([provider]) => provider.id === "kitchen-sink-search"));
assert.ok(registrations.registerChannel?.some(([channel]) => channel.id === "kitchen-sink-channel"));
const runtime = createKitchenSinkRuntime({
delayMs: 10_000,
sleep: async () => {},
now: (() => {
let tick = 0;
return () => new Date(Date.UTC(2026, 3, 29, 12, 0, tick++));
})(),
});
const image = await runtime.runImageJob({ prompt: "generate an image with kitchen sink" });
assert.equal(image.job.status, "completed");
assert.equal(image.image.metadata.assetName, "kitchen_sink_office.png");
assert.equal(image.image.metadata.sha256, "e126064123bb13d8ee01a22c204e079bc22397c103ed1c3a191c60d5ae3319aa");
const directImage = createKitchenSinkImageAsset({
prompt: "consumer import smoke",
jobId: "ks_consumer_install_smoke",
});
assert.equal(directImage.mimeType, "image/png");
assert.ok(directImage.dataUrl.startsWith("data:image/png;base64,"));
assert.ok(kitchenPromptGuidance().some((line) => line.includes("kitchen_sink_image_job")));
assert.equal(setup.id, "openclaw-kitchen-sink-setup");
assert.equal(typeof setup.setup, "function");
const setupResult = await setup.setup({ config: {} });
assert.equal(setupResult.configured, true);

View File

@ -0,0 +1,88 @@
import assert from "node:assert/strict";
export function capturePluginRegistration(plugin, config = {}) {
const captured = {};
const api = new Proxy(
{
id: "openclaw-kitchen-sink-fixture",
registrationMode: "full",
config,
logger: console,
},
{
get(target, property) {
if (property in target) {
return target[property];
}
if (property === "on") {
return (...args) => capture(captured, "on", args);
}
if (typeof property !== "string" || !property.startsWith("register")) {
return undefined;
}
return (...args) => capture(captured, property, args);
},
},
);
plugin.register(api);
return captured;
}
export function createRegistrationFinder(registrations) {
return (method, id) => {
const entry = registrations[method]?.map(([value]) => value).find((value) => value?.id === id);
assert.ok(entry, `${method} ${id} registered`);
return entry;
};
}
export function createHookFinder(registrations) {
return (name) => {
const entry = registrations.on?.find(([hookName]) => hookName === name);
assert.ok(entry, `hook ${name} registered`);
return entry[1];
};
}
export function registrationSummary(registrations) {
return Object.fromEntries(
Object.entries(registrations)
.filter(([method]) => method !== "on")
.sort(([a], [b]) => a.localeCompare(b))
.map(([method, entries]) => [
method,
{
count: entries.length,
ids: entries.map((args) => idForRegistration(method, args)).filter(Boolean).sort(),
},
]),
);
}
export function fixedNow(start = Date.UTC(2026, 3, 28, 12, 0, 0)) {
let tick = 0;
return () => new Date(start + tick++ * 1000);
}
function capture(registrations, method, args) {
registrations[method] ??= [];
registrations[method].push(args);
}
function idForRegistration(method, args) {
const [value, second] = args;
if (method === "registerGatewayMethod" && typeof value === "string") {
return value;
}
if (method === "registerCli" && second?.descriptors?.length > 0) {
return second.descriptors.map((descriptor) => descriptor.name).join(", ");
}
if (value?.id || value?.name) {
return value.id || value.name;
}
if (typeof second === "string") {
return second;
}
const slug = method.slice("register".length).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
return `kitchen-sink-${slug}`;
}