mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
chore(qa): cover plugin testing harness contracts (#118933)
* test(qa): cover plugin testing harness contracts * test(qa): narrow plugin harness tool factory
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
title: Plugin testing harness contracts
|
||||
|
||||
scenario:
|
||||
id: plugin-testing-harness-contracts
|
||||
surface: plugins
|
||||
category: plugins.testing-plugins
|
||||
coverage:
|
||||
primary:
|
||||
- plugins.test-fixtures
|
||||
- plugins.local-test-environment
|
||||
- plugins.harness-contracts-tools
|
||||
- plugins.unit-and-integration-scaffolds
|
||||
objective: Verify the repo-local plugin test helpers provide isolated state, typed fixtures, real registry registration, tool execution, and contract diagnostics.
|
||||
successCriteria:
|
||||
- The temporary-home helper creates isolated OpenClaw state and restores the caller environment.
|
||||
- Typed fixture cases drive an actual tool registered through the plugin registry harness.
|
||||
- A virtual plugin with a declared tools contract registers and executes its tool.
|
||||
- A virtual plugin without a tools contract records the canonical diagnostic and registers no tool.
|
||||
docsRefs:
|
||||
- docs/plugins/sdk-testing.md
|
||||
codeRefs:
|
||||
- src/plugin-sdk/plugin-test-contracts.ts
|
||||
- src/plugin-sdk/test-env.ts
|
||||
- src/plugin-sdk/test-fixtures.ts
|
||||
- src/plugins/contracts/plugin-testing-harness.contract.test.ts
|
||||
execution:
|
||||
kind: vitest
|
||||
path: src/plugins/contracts/plugin-testing-harness.contract.test.ts
|
||||
summary: Run the repo-local plugin harness across isolated state, typed fixtures, tool execution, and contract rejection.
|
||||
@@ -0,0 +1,113 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import {
|
||||
createPluginRegistryFixture,
|
||||
registerVirtualTestPlugin,
|
||||
} from "openclaw/plugin-sdk/plugin-test-contracts";
|
||||
import { withTempHome } from "openclaw/plugin-sdk/test-env";
|
||||
import { typedCases } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const echoCases = typedCases([
|
||||
{ message: "alpha", expected: "scoped:alpha" },
|
||||
{ message: "beta", expected: "scoped:beta" },
|
||||
]);
|
||||
|
||||
describe("plugin testing harness contracts", () => {
|
||||
it("executes declared tools and reports missing tool contracts", async () => {
|
||||
const previousHome = process.env.HOME;
|
||||
const previousStateDir = process.env.OPENCLAW_STATE_DIR;
|
||||
|
||||
await withTempHome(async (home) => {
|
||||
const stateDir = path.join(home, ".openclaw");
|
||||
await expect(
|
||||
fs.stat(path.join(stateDir, "agents", "main", "sessions")),
|
||||
).resolves.toBeDefined();
|
||||
expect(process.env.HOME).toBe(home);
|
||||
expect(process.env.OPENCLAW_STATE_DIR).toBe(stateDir);
|
||||
|
||||
const { config, registry } = createPluginRegistryFixture({
|
||||
plugins: {
|
||||
entries: {
|
||||
"fixture-echo": {
|
||||
config: { prefix: "scoped" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
registerVirtualTestPlugin({
|
||||
registry,
|
||||
config,
|
||||
id: "fixture-echo",
|
||||
name: "Fixture Echo",
|
||||
contracts: { tools: ["fixture_echo"] },
|
||||
register(api) {
|
||||
const prefix = api.config.plugins?.entries?.["fixture-echo"]?.config?.prefix;
|
||||
if (typeof prefix !== "string") {
|
||||
throw new Error("fixture prefix missing");
|
||||
}
|
||||
api.registerTool({
|
||||
name: "fixture_echo",
|
||||
label: "Fixture Echo",
|
||||
description: "Echo a fixture value",
|
||||
parameters: {},
|
||||
execute: async (_toolCallId, params) => {
|
||||
const message = (params as { message: string }).message;
|
||||
return {
|
||||
content: [{ type: "text", text: `${prefix}:${message}` }],
|
||||
details: { home, message },
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
expect(registry.registry.tools).toHaveLength(1);
|
||||
const registration = registry.registry.tools[0];
|
||||
expect(registration?.pluginId).toBe("fixture-echo");
|
||||
expect(registration?.names).toEqual(["fixture_echo"]);
|
||||
const tool = registration?.factory({ workspaceDir: home });
|
||||
if (!tool || Array.isArray(tool)) {
|
||||
throw new Error("expected one registered fixture tool");
|
||||
}
|
||||
expect(tool.name).toBe("fixture_echo");
|
||||
|
||||
for (const testCase of echoCases) {
|
||||
await expect(tool.execute("fixture-call", { message: testCase.message })).resolves.toEqual({
|
||||
content: [{ type: "text", text: testCase.expected }],
|
||||
details: { home, message: testCase.message },
|
||||
});
|
||||
}
|
||||
expect(registry.registry.diagnostics).toEqual([]);
|
||||
|
||||
const missingContract = createPluginRegistryFixture();
|
||||
registerVirtualTestPlugin({
|
||||
registry: missingContract.registry,
|
||||
config: missingContract.config,
|
||||
id: "missing-contract",
|
||||
name: "Missing Contract",
|
||||
register(api) {
|
||||
api.registerTool({
|
||||
name: "fixture_echo",
|
||||
label: "Fixture Echo",
|
||||
description: "Echo a fixture value",
|
||||
parameters: {},
|
||||
execute: async () => ({ content: [], details: {} }),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
expect(missingContract.registry.registry.tools).toEqual([]);
|
||||
expect(missingContract.registry.registry.diagnostics).toContainEqual({
|
||||
level: "error",
|
||||
pluginId: "missing-contract",
|
||||
source: "/virtual/missing-contract/index.ts",
|
||||
message: "plugin must declare contracts.tools before registering agent tools",
|
||||
});
|
||||
});
|
||||
|
||||
expect(process.env.HOME).toBe(previousHome);
|
||||
expect(process.env.OPENCLAW_STATE_DIR).toBe(previousStateDir);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user