mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
test: move plugin helper seams into test helpers
This commit is contained in:
107
test/helpers/plugins/contracts-testkit.ts
Normal file
107
test/helpers/plugins/contracts-testkit.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { createPluginRegistry, type PluginRecord } from "../../../src/plugins/registry.js";
|
||||
import type { PluginRuntime } from "../../../src/plugins/runtime/types.js";
|
||||
import { createPluginRecord } from "../../../src/plugins/status.test-helpers.js";
|
||||
import type { OpenClawPluginApi } from "../../../src/plugins/types.js";
|
||||
|
||||
export {
|
||||
registerProviderPlugins as registerProviders,
|
||||
requireRegisteredProvider as requireProvider,
|
||||
} from "../../../src/test-utils/plugin-registration.js";
|
||||
|
||||
export function uniqueSortedStrings(values: readonly string[]) {
|
||||
return [...new Set(values)].toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function formatImportSideEffectCall(args: readonly unknown[]): string {
|
||||
if (args.length === 0) {
|
||||
return "(no args)";
|
||||
}
|
||||
return args
|
||||
.map((arg) => {
|
||||
try {
|
||||
return JSON.stringify(arg);
|
||||
} catch {
|
||||
return String(arg);
|
||||
}
|
||||
})
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
export function assertNoImportTimeSideEffects(params: {
|
||||
moduleId: string;
|
||||
forbiddenSeam: string;
|
||||
calls: readonly (readonly unknown[])[];
|
||||
why: string;
|
||||
fixHint: string;
|
||||
}) {
|
||||
if (params.calls.length === 0) {
|
||||
return;
|
||||
}
|
||||
const observedCalls = params.calls
|
||||
.slice(0, 3)
|
||||
.map((call, index) => ` ${index + 1}. ${formatImportSideEffectCall(call)}`)
|
||||
.join("\n");
|
||||
throw new Error(
|
||||
[
|
||||
`[runtime contract] ${params.moduleId} touched ${params.forbiddenSeam} during module import.`,
|
||||
`why this is banned: ${params.why}`,
|
||||
`expected fix: ${params.fixHint}`,
|
||||
`observed calls (${params.calls.length}):`,
|
||||
observedCalls,
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
export function createPluginRegistryFixture(config = {} as OpenClawConfig) {
|
||||
return {
|
||||
config,
|
||||
registry: createPluginRegistry({
|
||||
logger: {
|
||||
info() {},
|
||||
warn() {},
|
||||
error() {},
|
||||
debug() {},
|
||||
},
|
||||
runtime: {} as PluginRuntime,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function registerTestPlugin(params: {
|
||||
registry: ReturnType<typeof createPluginRegistry>;
|
||||
config: OpenClawConfig;
|
||||
record: PluginRecord;
|
||||
register(api: OpenClawPluginApi): void;
|
||||
}) {
|
||||
params.registry.registry.plugins.push(params.record);
|
||||
params.register(
|
||||
params.registry.createApi(params.record, {
|
||||
config: params.config,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function registerVirtualTestPlugin(params: {
|
||||
registry: ReturnType<typeof createPluginRegistry>;
|
||||
config: OpenClawConfig;
|
||||
id: string;
|
||||
name: string;
|
||||
source?: string;
|
||||
kind?: PluginRecord["kind"];
|
||||
contracts?: PluginRecord["contracts"];
|
||||
register(this: void, api: OpenClawPluginApi): void;
|
||||
}) {
|
||||
registerTestPlugin({
|
||||
registry: params.registry,
|
||||
config: params.config,
|
||||
record: createPluginRecord({
|
||||
id: params.id,
|
||||
name: params.name,
|
||||
source: params.source ?? `/virtual/${params.id}/index.ts`,
|
||||
...(params.kind ? { kind: params.kind } : {}),
|
||||
...(params.contracts ? { contracts: params.contracts } : {}),
|
||||
}),
|
||||
register: params.register,
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { clearRuntimeAuthProfileStoreSnapshots } from "../../../src/agents/auth-profiles/store.js";
|
||||
import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.js";
|
||||
import { registerProviders, requireProvider } from "../../../src/plugins/contracts/testkit.js";
|
||||
import { createNonExitingRuntime } from "../../../src/runtime.js";
|
||||
import type {
|
||||
WizardMultiSelectParams,
|
||||
@@ -9,6 +8,7 @@ import type {
|
||||
WizardProgress,
|
||||
WizardSelectParams,
|
||||
} from "../../../src/wizard/prompts.js";
|
||||
import { registerProviders, requireProvider } from "./contracts-testkit.js";
|
||||
|
||||
type LoginOpenAICodexOAuth =
|
||||
(typeof import("openclaw/plugin-sdk/provider-auth-login"))["loginOpenAICodexOAuth"];
|
||||
|
||||
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.js";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import type { ModelDefinitionConfig } from "../../../src/config/types.models.js";
|
||||
import { registerProviders, requireProvider } from "../../../src/plugins/contracts/testkit.js";
|
||||
import { registerProviders, requireProvider } from "./contracts-testkit.js";
|
||||
|
||||
const resolveCopilotApiTokenMock = vi.hoisted(() => vi.fn());
|
||||
const buildOllamaProviderMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
53
test/helpers/plugins/public-artifacts.ts
Normal file
53
test/helpers/plugins/public-artifacts.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../../../src/plugins/runtime-sidecar-paths.js";
|
||||
|
||||
function assertUniqueValues<T extends string>(values: readonly T[], label: string): readonly T[] {
|
||||
const seen = new Set<string>();
|
||||
const duplicates = new Set<string>();
|
||||
for (const value of values) {
|
||||
if (seen.has(value)) {
|
||||
duplicates.add(value);
|
||||
continue;
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
if (duplicates.size > 0) {
|
||||
throw new Error(`Duplicate ${label}: ${Array.from(duplicates).join(", ")}`);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
export function getPublicArtifactBasename(relativePath: string): string {
|
||||
return relativePath.split("/").at(-1) ?? relativePath;
|
||||
}
|
||||
|
||||
const EXTRA_GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES = assertUniqueValues(
|
||||
[
|
||||
"action-runtime.runtime.js",
|
||||
"action-runtime-api.js",
|
||||
"allow-from.js",
|
||||
"api.js",
|
||||
"auth-presence.js",
|
||||
"channel-config-api.js",
|
||||
"index.js",
|
||||
"login-qr-api.js",
|
||||
"onboard.js",
|
||||
"openai-codex-catalog.js",
|
||||
"provider-catalog.js",
|
||||
"session-key-api.js",
|
||||
"setup-api.js",
|
||||
"setup-entry.js",
|
||||
"timeouts.js",
|
||||
"x-search.js",
|
||||
] as const,
|
||||
"extra guarded extension public surface basename",
|
||||
);
|
||||
|
||||
export const BUNDLED_RUNTIME_SIDECAR_BASENAMES = assertUniqueValues(
|
||||
[...new Set(BUNDLED_RUNTIME_SIDECAR_PATHS.map(getPublicArtifactBasename))],
|
||||
"bundled runtime sidecar basename",
|
||||
);
|
||||
|
||||
export const GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES = assertUniqueValues(
|
||||
[...BUNDLED_RUNTIME_SIDECAR_BASENAMES, ...EXTRA_GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES],
|
||||
"guarded extension public surface basename",
|
||||
);
|
||||
Reference in New Issue
Block a user