perf(tests): split plugin state runtime imports

This commit is contained in:
Peter Steinberger
2026-04-29 21:21:34 +01:00
parent 12ee7f696f
commit 7fc0859a01
3 changed files with 106 additions and 89 deletions

View File

@@ -0,0 +1,92 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { PluginRecord } from "../plugins/registry-types.js";
import { createPluginRegistry } from "../plugins/registry.js";
import { createPluginRuntime } from "../plugins/runtime/index.js";
import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js";
import { resetPluginStateStoreForTests } from "./plugin-state-store.js";
function createPluginRecord(id: string, origin: PluginRecord["origin"] = "bundled"): PluginRecord {
return {
id,
name: id,
source: `/plugins/${id}/index.ts`,
origin,
enabled: true,
status: "loaded",
toolNames: [],
hookNames: [],
channelIds: [],
cliBackendIds: [],
providerIds: [],
speechProviderIds: [],
realtimeTranscriptionProviderIds: [],
realtimeVoiceProviderIds: [],
mediaUnderstandingProviderIds: [],
imageGenerationProviderIds: [],
videoGenerationProviderIds: [],
musicGenerationProviderIds: [],
webFetchProviderIds: [],
webSearchProviderIds: [],
migrationProviderIds: [],
memoryEmbeddingProviderIds: [],
agentHarnessIds: [],
gatewayMethods: [],
cliCommands: [],
services: [],
gatewayDiscoveryServiceIds: [],
commands: [],
httpRoutes: 0,
hookCount: 0,
configSchema: false,
} as PluginRecord;
}
function createTestPluginRegistry() {
return createPluginRegistry({
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
runtime: createPluginRuntime(),
});
}
afterEach(() => {
resetPluginStateStoreForTests();
});
describe("plugin runtime state proxy", () => {
it("binds openKeyedStore to the bundled plugin id and keeps resolveStateDir", async () => {
await withOpenClawTestState({ label: "plugin-state-runtime" }, async (state) => {
const registry = createTestPluginRegistry();
const record = createPluginRecord("discord", "bundled");
registry.registry.plugins.push(record);
const api = registry.createApi(record, { config: {} });
expect(api.runtime.state.resolveStateDir()).toBe(state.stateDir);
const store = api.runtime.state.openKeyedStore<{ plugin: string }>({
namespace: "runtime",
maxEntries: 10,
});
await store.register("k", { plugin: "discord" });
const telegram = createPluginRecord("telegram", "bundled");
registry.registry.plugins.push(telegram);
const telegramApi = registry.createApi(telegram, { config: {} });
const telegramStore = telegramApi.runtime.state.openKeyedStore<{ plugin: string }>({
namespace: "runtime",
maxEntries: 10,
});
await expect(telegramStore.lookup("k")).resolves.toBeUndefined();
await expect(store.lookup("k")).resolves.toEqual({ plugin: "discord" });
});
});
it("rejects external plugins in this release", () => {
const registry = createTestPluginRegistry();
const record = createPluginRecord("external-plugin", "workspace");
registry.registry.plugins.push(record);
const api = registry.createApi(record, { config: {} });
expect(() =>
api.runtime.state.openKeyedStore({ namespace: "runtime", maxEntries: 10 }),
).toThrow("openKeyedStore is only available for bundled plugins");
});
});

View File

@@ -1,9 +1,6 @@
import { mkdirSync, statSync } from "node:fs";
import { afterEach, describe, expect, it, vi } from "vitest";
import { requireNodeSqlite } from "../infra/node-sqlite.js";
import type { PluginRecord } from "../plugins/registry-types.js";
import { createPluginRegistry } from "../plugins/registry.js";
import { createPluginRuntime } from "../plugins/runtime/index.js";
import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js";
import {
closePluginStateSqliteStore,
@@ -16,42 +13,6 @@ import {
} from "./plugin-state-store.js";
import { resolvePluginStateDir, resolvePluginStateSqlitePath } from "./plugin-state-store.paths.js";
function createPluginRecord(id: string, origin: PluginRecord["origin"] = "bundled"): PluginRecord {
return {
id,
name: id,
source: `/plugins/${id}/index.ts`,
origin,
enabled: true,
status: "loaded",
toolNames: [],
hookNames: [],
channelIds: [],
cliBackendIds: [],
providerIds: [],
speechProviderIds: [],
realtimeTranscriptionProviderIds: [],
realtimeVoiceProviderIds: [],
mediaUnderstandingProviderIds: [],
imageGenerationProviderIds: [],
videoGenerationProviderIds: [],
musicGenerationProviderIds: [],
webFetchProviderIds: [],
webSearchProviderIds: [],
migrationProviderIds: [],
memoryEmbeddingProviderIds: [],
agentHarnessIds: [],
gatewayMethods: [],
cliCommands: [],
services: [],
gatewayDiscoveryServiceIds: [],
commands: [],
httpRoutes: 0,
hookCount: 0,
configSchema: false,
} as PluginRecord;
}
afterEach(() => {
vi.useRealTimers();
resetPluginStateStoreForTests();
@@ -332,48 +293,3 @@ describe("plugin state keyed store", () => {
});
});
});
describe("plugin runtime state proxy", () => {
it("binds openKeyedStore to the bundled plugin id and keeps resolveStateDir", async () => {
await withOpenClawTestState({ label: "plugin-state-runtime" }, async (state) => {
const registry = createPluginRegistry({
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
runtime: createPluginRuntime(),
});
const record = createPluginRecord("discord", "bundled");
registry.registry.plugins.push(record);
const api = registry.createApi(record, { config: {} });
expect(api.runtime.state.resolveStateDir()).toBe(state.stateDir);
const store = api.runtime.state.openKeyedStore<{ plugin: string }>({
namespace: "runtime",
maxEntries: 10,
});
await store.register("k", { plugin: "discord" });
const telegram = createPluginRecord("telegram", "bundled");
registry.registry.plugins.push(telegram);
const telegramApi = registry.createApi(telegram, { config: {} });
const telegramStore = telegramApi.runtime.state.openKeyedStore<{ plugin: string }>({
namespace: "runtime",
maxEntries: 10,
});
await expect(telegramStore.lookup("k")).resolves.toBeUndefined();
await expect(store.lookup("k")).resolves.toEqual({ plugin: "discord" });
});
});
it("rejects external plugins in this release", () => {
const registry = createPluginRegistry({
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
runtime: createPluginRuntime(),
});
const record = createPluginRecord("external-plugin", "workspace");
registry.registry.plugins.push(record);
const api = registry.createApi(record, { config: {} });
expect(() =>
api.runtime.state.openKeyedStore({ namespace: "runtime", maxEntries: 10 }),
).toThrow("openKeyedStore is only available for bundled plugins");
});
});

View File

@@ -2,11 +2,20 @@ import { describe, expect, it, vi } from "vitest";
import type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
import type { OpenClawConfig } from "../config/config.js";
const { listReadOnlyChannelPluginsForConfigMock, hasConfiguredChannelsForReadOnlyScopeMock } =
vi.hoisted(() => ({
listReadOnlyChannelPluginsForConfigMock: vi.fn(),
hasConfiguredChannelsForReadOnlyScopeMock: vi.fn(),
}));
const {
collectEnabledInsecureOrDangerousFlagsMock,
listReadOnlyChannelPluginsForConfigMock,
hasConfiguredChannelsForReadOnlyScopeMock,
} = vi.hoisted(() => ({
collectEnabledInsecureOrDangerousFlagsMock: vi.fn((_config: OpenClawConfig): string[] => []),
listReadOnlyChannelPluginsForConfigMock: vi.fn(),
hasConfiguredChannelsForReadOnlyScopeMock: vi.fn(),
}));
vi.mock("./dangerous-config-flags.js", () => ({
collectEnabledInsecureOrDangerousFlags: (config: OpenClawConfig) =>
collectEnabledInsecureOrDangerousFlagsMock(config),
}));
vi.mock("../channels/plugins/read-only.js", () => ({
listReadOnlyChannelPluginsForConfig: (...args: unknown[]) =>