File size: 4,172 Bytes
4c76b0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import fs from "node:fs/promises";
import path from "node:path";
import {
  createPluginRegistryFixture,
  describePluginRegistrationContract,
  pluginRegistrationContractCases,
  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" },
]);

describePluginRegistrationContract(pluginRegistrationContractCases.brave);

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);
  });
});