mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 05:00:21 +00:00
refactor: move plugin-owned test support into plugins
This commit is contained in:
@@ -1 +0,0 @@
|
||||
export { expectGeneratedTokenPersistedToGatewayAuth } from "../../../src/test-utils/auth-token-assertions.js";
|
||||
@@ -1,151 +0,0 @@
|
||||
import type { HistoryEntry, PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
|
||||
import { vi } from "vitest";
|
||||
import {
|
||||
_resetBlueBubblesShortIdState,
|
||||
clearBlueBubblesWebhookSecurityStateForTest,
|
||||
} from "../../../extensions/bluebubbles/src/monitor.js";
|
||||
import { setBlueBubblesRuntime } from "../../../extensions/bluebubbles/src/runtime.js";
|
||||
import { createPluginRuntimeMock } from "./plugin-runtime-mock.js";
|
||||
|
||||
type BlueBubblesHistoryFetchResult = {
|
||||
entries: HistoryEntry[];
|
||||
resolved: boolean;
|
||||
};
|
||||
|
||||
export type DispatchReplyParams = Parameters<
|
||||
PluginRuntime["channel"]["reply"]["dispatchReplyWithBufferedBlockDispatcher"]
|
||||
>[0];
|
||||
|
||||
export const EMPTY_DISPATCH_RESULT = {
|
||||
queuedFinal: false,
|
||||
counts: { tool: 0, block: 0, final: 0 },
|
||||
} as const;
|
||||
|
||||
type BlueBubblesMonitorTestRuntimeMocks = {
|
||||
enqueueSystemEvent: unknown;
|
||||
chunkMarkdownText: unknown;
|
||||
chunkByNewline: unknown;
|
||||
chunkMarkdownTextWithMode: unknown;
|
||||
chunkTextWithMode: unknown;
|
||||
resolveChunkMode: unknown;
|
||||
hasControlCommand: unknown;
|
||||
dispatchReplyWithBufferedBlockDispatcher: unknown;
|
||||
formatAgentEnvelope: unknown;
|
||||
formatInboundEnvelope: unknown;
|
||||
resolveEnvelopeFormatOptions: unknown;
|
||||
resolveAgentRoute: unknown;
|
||||
buildPairingReply: unknown;
|
||||
readAllowFromStore: unknown;
|
||||
upsertPairingRequest: unknown;
|
||||
saveMediaBuffer: unknown;
|
||||
resolveStorePath: unknown;
|
||||
readSessionUpdatedAt: unknown;
|
||||
buildMentionRegexes: unknown;
|
||||
matchesMentionPatterns: unknown;
|
||||
matchesMentionWithExplicit: unknown;
|
||||
resolveGroupPolicy: unknown;
|
||||
resolveRequireMention: unknown;
|
||||
resolveCommandAuthorizedFromAuthorizers: unknown;
|
||||
};
|
||||
|
||||
export function createBlueBubblesMonitorTestRuntime(
|
||||
mocks: BlueBubblesMonitorTestRuntimeMocks,
|
||||
): PluginRuntime {
|
||||
return createPluginRuntimeMock({
|
||||
system: {
|
||||
enqueueSystemEvent: mocks.enqueueSystemEvent as PluginRuntime["system"]["enqueueSystemEvent"],
|
||||
},
|
||||
channel: {
|
||||
text: {
|
||||
chunkMarkdownText:
|
||||
mocks.chunkMarkdownText as PluginRuntime["channel"]["text"]["chunkMarkdownText"],
|
||||
chunkByNewline: mocks.chunkByNewline as PluginRuntime["channel"]["text"]["chunkByNewline"],
|
||||
chunkMarkdownTextWithMode:
|
||||
mocks.chunkMarkdownTextWithMode as PluginRuntime["channel"]["text"]["chunkMarkdownTextWithMode"],
|
||||
chunkTextWithMode:
|
||||
mocks.chunkTextWithMode as PluginRuntime["channel"]["text"]["chunkTextWithMode"],
|
||||
resolveChunkMode:
|
||||
mocks.resolveChunkMode as PluginRuntime["channel"]["text"]["resolveChunkMode"],
|
||||
hasControlCommand:
|
||||
mocks.hasControlCommand as PluginRuntime["channel"]["text"]["hasControlCommand"],
|
||||
},
|
||||
reply: {
|
||||
dispatchReplyWithBufferedBlockDispatcher:
|
||||
mocks.dispatchReplyWithBufferedBlockDispatcher as PluginRuntime["channel"]["reply"]["dispatchReplyWithBufferedBlockDispatcher"],
|
||||
formatAgentEnvelope:
|
||||
mocks.formatAgentEnvelope as PluginRuntime["channel"]["reply"]["formatAgentEnvelope"],
|
||||
formatInboundEnvelope:
|
||||
mocks.formatInboundEnvelope as PluginRuntime["channel"]["reply"]["formatInboundEnvelope"],
|
||||
resolveEnvelopeFormatOptions:
|
||||
mocks.resolveEnvelopeFormatOptions as PluginRuntime["channel"]["reply"]["resolveEnvelopeFormatOptions"],
|
||||
},
|
||||
routing: {
|
||||
resolveAgentRoute:
|
||||
mocks.resolveAgentRoute as PluginRuntime["channel"]["routing"]["resolveAgentRoute"],
|
||||
},
|
||||
pairing: {
|
||||
buildPairingReply:
|
||||
mocks.buildPairingReply as PluginRuntime["channel"]["pairing"]["buildPairingReply"],
|
||||
readAllowFromStore:
|
||||
mocks.readAllowFromStore as PluginRuntime["channel"]["pairing"]["readAllowFromStore"],
|
||||
upsertPairingRequest:
|
||||
mocks.upsertPairingRequest as PluginRuntime["channel"]["pairing"]["upsertPairingRequest"],
|
||||
},
|
||||
media: {
|
||||
saveMediaBuffer:
|
||||
mocks.saveMediaBuffer as PluginRuntime["channel"]["media"]["saveMediaBuffer"],
|
||||
},
|
||||
session: {
|
||||
resolveStorePath:
|
||||
mocks.resolveStorePath as PluginRuntime["channel"]["session"]["resolveStorePath"],
|
||||
readSessionUpdatedAt:
|
||||
mocks.readSessionUpdatedAt as PluginRuntime["channel"]["session"]["readSessionUpdatedAt"],
|
||||
},
|
||||
mentions: {
|
||||
buildMentionRegexes:
|
||||
mocks.buildMentionRegexes as PluginRuntime["channel"]["mentions"]["buildMentionRegexes"],
|
||||
matchesMentionPatterns:
|
||||
mocks.matchesMentionPatterns as PluginRuntime["channel"]["mentions"]["matchesMentionPatterns"],
|
||||
matchesMentionWithExplicit:
|
||||
mocks.matchesMentionWithExplicit as PluginRuntime["channel"]["mentions"]["matchesMentionWithExplicit"],
|
||||
},
|
||||
groups: {
|
||||
resolveGroupPolicy:
|
||||
mocks.resolveGroupPolicy as PluginRuntime["channel"]["groups"]["resolveGroupPolicy"],
|
||||
resolveRequireMention:
|
||||
mocks.resolveRequireMention as PluginRuntime["channel"]["groups"]["resolveRequireMention"],
|
||||
},
|
||||
commands: {
|
||||
resolveCommandAuthorizedFromAuthorizers:
|
||||
mocks.resolveCommandAuthorizedFromAuthorizers as PluginRuntime["channel"]["commands"]["resolveCommandAuthorizedFromAuthorizers"],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function resetBlueBubblesMonitorTestState(params: {
|
||||
createRuntime: () => PluginRuntime;
|
||||
fetchHistoryMock: { mockResolvedValue: (value: BlueBubblesHistoryFetchResult) => unknown };
|
||||
readAllowFromStoreMock: { mockResolvedValue: (value: string[]) => unknown };
|
||||
upsertPairingRequestMock: {
|
||||
mockResolvedValue: (value: { code: string; created: boolean }) => unknown;
|
||||
};
|
||||
resolveRequireMentionMock: { mockReturnValue: (value: boolean) => unknown };
|
||||
hasControlCommandMock: { mockReturnValue: (value: boolean) => unknown };
|
||||
resolveCommandAuthorizedFromAuthorizersMock: { mockReturnValue: (value: boolean) => unknown };
|
||||
buildMentionRegexesMock: { mockReturnValue: (value: RegExp[]) => unknown };
|
||||
extraReset?: () => void;
|
||||
}) {
|
||||
vi.clearAllMocks();
|
||||
_resetBlueBubblesShortIdState();
|
||||
clearBlueBubblesWebhookSecurityStateForTest();
|
||||
params.extraReset?.();
|
||||
params.fetchHistoryMock.mockResolvedValue({ entries: [], resolved: true });
|
||||
params.readAllowFromStoreMock.mockResolvedValue([]);
|
||||
params.upsertPairingRequestMock.mockResolvedValue({ code: "TESTCODE", created: true });
|
||||
params.resolveRequireMentionMock.mockReturnValue(false);
|
||||
params.hasControlCommandMock.mockReturnValue(false);
|
||||
params.resolveCommandAuthorizedFromAuthorizersMock.mockReturnValue(false);
|
||||
params.buildMentionRegexesMock.mockReturnValue([/\bbert\b/i]);
|
||||
setBlueBubblesRuntime(params.createRuntime());
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { countLines, hasBalancedFences } from "../../../src/test-utils/chunk-test-helpers.js";
|
||||
@@ -1,19 +0,0 @@
|
||||
export async function createConfiguredBindingConversationRuntimeModuleMock(
|
||||
params: {
|
||||
ensureConfiguredBindingRouteReadyMock: (...args: unknown[]) => unknown;
|
||||
resolveConfiguredBindingRouteMock: (...args: unknown[]) => unknown;
|
||||
},
|
||||
importOriginal: () => Promise<{
|
||||
ensureConfiguredBindingRouteReady: (...args: unknown[]) => unknown;
|
||||
resolveConfiguredBindingRoute: (...args: unknown[]) => unknown;
|
||||
}>,
|
||||
) {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
ensureConfiguredBindingRouteReady: (...args: unknown[]) =>
|
||||
params.ensureConfiguredBindingRouteReadyMock(...args),
|
||||
resolveConfiguredBindingRoute: (...args: unknown[]) =>
|
||||
params.resolveConfiguredBindingRouteMock(...args),
|
||||
};
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
const runtimeMocks = vi.hoisted(() => ({
|
||||
readAllowFromStoreMock: vi.fn(),
|
||||
upsertPairingRequestMock: vi.fn(),
|
||||
recordInboundSessionMock: vi.fn(),
|
||||
resolvePluginConversationBindingApprovalMock: vi.fn(),
|
||||
buildPluginBindingResolvedTextMock: vi.fn(),
|
||||
}));
|
||||
|
||||
export const readAllowFromStoreMock = runtimeMocks.readAllowFromStoreMock;
|
||||
export const upsertPairingRequestMock = runtimeMocks.upsertPairingRequestMock;
|
||||
export const recordInboundSessionMock = runtimeMocks.recordInboundSessionMock;
|
||||
export const resolvePluginConversationBindingApprovalMock =
|
||||
runtimeMocks.resolvePluginConversationBindingApprovalMock;
|
||||
export const buildPluginBindingResolvedTextMock = runtimeMocks.buildPluginBindingResolvedTextMock;
|
||||
|
||||
async function createConversationRuntimeMock(
|
||||
importOriginal: () => Promise<typeof import("openclaw/plugin-sdk/conversation-runtime")>,
|
||||
) {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
|
||||
resolvePluginConversationBindingApproval: (...args: unknown[]) =>
|
||||
resolvePluginConversationBindingApprovalMock(...args),
|
||||
buildPluginBindingResolvedText: (...args: unknown[]) =>
|
||||
buildPluginBindingResolvedTextMock(...args),
|
||||
recordInboundSession: (...args: unknown[]) => recordInboundSessionMock(...args),
|
||||
};
|
||||
}
|
||||
|
||||
async function createAllowFromRuntimeMock<TModule>(
|
||||
importOriginal: () => Promise<TModule>,
|
||||
): Promise<TModule & { readStoreAllowFromForDmPolicy: typeof readStoreAllowFromForDmPolicy }> {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
readStoreAllowFromForDmPolicy,
|
||||
};
|
||||
}
|
||||
|
||||
async function readStoreAllowFromForDmPolicy(params: {
|
||||
provider: string;
|
||||
accountId: string;
|
||||
dmPolicy?: string | null;
|
||||
shouldRead?: boolean | null;
|
||||
}) {
|
||||
if (params.shouldRead === false || params.dmPolicy === "allowlist") {
|
||||
return [];
|
||||
}
|
||||
return await readAllowFromStoreMock(params.provider, params.accountId);
|
||||
}
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/security-runtime", (importOriginal) =>
|
||||
createAllowFromRuntimeMock(importOriginal),
|
||||
);
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", createConversationRuntimeMock);
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime.js", createConversationRuntimeMock);
|
||||
vi.mock("../../../src/pairing/pairing-store.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../../src/pairing/pairing-store.js")>();
|
||||
return {
|
||||
...actual,
|
||||
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
|
||||
};
|
||||
});
|
||||
vi.mock("../../../src/security/dm-policy-shared.js", (importOriginal) =>
|
||||
createAllowFromRuntimeMock(importOriginal),
|
||||
);
|
||||
|
||||
export function resetDiscordComponentRuntimeMocks() {
|
||||
readAllowFromStoreMock.mockClear().mockResolvedValue([]);
|
||||
upsertPairingRequestMock.mockClear().mockResolvedValue({ code: "PAIRCODE", created: true });
|
||||
recordInboundSessionMock.mockClear().mockResolvedValue(undefined);
|
||||
resolvePluginConversationBindingApprovalMock.mockReset().mockResolvedValue({
|
||||
status: "approved",
|
||||
binding: {
|
||||
bindingId: "binding-1",
|
||||
pluginId: "openclaw-codex-app-server",
|
||||
pluginName: "OpenClaw App Server",
|
||||
pluginRoot: "/plugins/codex",
|
||||
channel: "discord",
|
||||
accountId: "default",
|
||||
conversationId: "user:123456789",
|
||||
boundAt: Date.now(),
|
||||
},
|
||||
request: {
|
||||
id: "approval-1",
|
||||
pluginId: "openclaw-codex-app-server",
|
||||
pluginName: "OpenClaw App Server",
|
||||
pluginRoot: "/plugins/codex",
|
||||
requestedAt: Date.now(),
|
||||
conversation: {
|
||||
channel: "discord",
|
||||
accountId: "default",
|
||||
conversationId: "user:123456789",
|
||||
},
|
||||
},
|
||||
decision: "allow-once",
|
||||
});
|
||||
buildPluginBindingResolvedTextMock.mockReset().mockReturnValue("Binding approved.");
|
||||
}
|
||||
@@ -1,503 +0,0 @@
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import type { Mock } from "vitest";
|
||||
import { expect, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/plugin-sdk/discord.js";
|
||||
|
||||
export type NativeCommandSpecMock = {
|
||||
name: string;
|
||||
description: string;
|
||||
acceptsArgs: boolean;
|
||||
};
|
||||
|
||||
export type PluginCommandSpecMock = {
|
||||
name: string;
|
||||
description: string;
|
||||
acceptsArgs: boolean;
|
||||
};
|
||||
|
||||
type ProviderMonitorTestMocks = {
|
||||
clientHandleDeployRequestMock: Mock<() => Promise<void>>;
|
||||
clientFetchUserMock: Mock<(target: string) => Promise<{ id: string }>>;
|
||||
clientGetPluginMock: Mock<(name: string) => unknown>;
|
||||
clientConstructorOptionsMock: Mock<(options?: unknown) => void>;
|
||||
createDiscordAutoPresenceControllerMock: Mock<() => unknown>;
|
||||
createDiscordNativeCommandMock: Mock<(params?: { command?: { name?: string } }) => unknown>;
|
||||
createDiscordMessageHandlerMock: Mock<() => unknown>;
|
||||
createNoopThreadBindingManagerMock: Mock<() => { stop: ReturnType<typeof vi.fn> }>;
|
||||
createThreadBindingManagerMock: Mock<() => { stop: ReturnType<typeof vi.fn> }>;
|
||||
reconcileAcpThreadBindingsOnStartupMock: Mock<() => unknown>;
|
||||
createdBindingManagers: Array<{ stop: ReturnType<typeof vi.fn> }>;
|
||||
getAcpSessionStatusMock: Mock<
|
||||
(params: {
|
||||
cfg: OpenClawConfig;
|
||||
sessionKey: string;
|
||||
signal?: AbortSignal;
|
||||
}) => Promise<{ state: string }>
|
||||
>;
|
||||
getPluginCommandSpecsMock: Mock<(provider?: string) => PluginCommandSpecMock[]>;
|
||||
listNativeCommandSpecsForConfigMock: Mock<
|
||||
(
|
||||
cfg?: unknown,
|
||||
params?: { skillCommands?: unknown[]; provider?: string },
|
||||
) => NativeCommandSpecMock[]
|
||||
>;
|
||||
listSkillCommandsForAgentsMock: Mock<
|
||||
(params?: { cfg?: unknown; agentIds?: string[] }) => unknown[]
|
||||
>;
|
||||
monitorLifecycleMock: Mock<(params: { threadBindings: { stop: () => void } }) => Promise<void>>;
|
||||
resolveDiscordAccountMock: Mock<
|
||||
(params?: { cfg?: unknown; accountId?: string | null; token?: string | null }) => unknown
|
||||
>;
|
||||
resolveDiscordAllowlistConfigMock: Mock<() => Promise<unknown>>;
|
||||
resolveNativeCommandsEnabledMock: Mock<(params?: unknown) => boolean>;
|
||||
resolveNativeSkillsEnabledMock: Mock<(params?: unknown) => boolean>;
|
||||
isVerboseMock: Mock<() => boolean>;
|
||||
shouldLogVerboseMock: Mock<() => boolean>;
|
||||
voiceRuntimeModuleLoadedMock: Mock<() => void>;
|
||||
};
|
||||
|
||||
export function baseDiscordAccountConfig() {
|
||||
return {
|
||||
commands: { native: true, nativeSkills: false },
|
||||
voice: { enabled: false },
|
||||
agentComponents: { enabled: false },
|
||||
execApprovals: { enabled: false },
|
||||
};
|
||||
}
|
||||
|
||||
const providerMonitorTestMocks: ProviderMonitorTestMocks = vi.hoisted(() => {
|
||||
const createdBindingManagers: Array<{ stop: ReturnType<typeof vi.fn> }> = [];
|
||||
const isVerboseMock = vi.fn(() => false);
|
||||
const shouldLogVerboseMock = vi.fn(() => false);
|
||||
|
||||
return {
|
||||
clientHandleDeployRequestMock: vi.fn(async () => undefined),
|
||||
clientFetchUserMock: vi.fn(async (_target: string) => ({ id: "bot-1" })),
|
||||
clientGetPluginMock: vi.fn<(_name: string) => unknown>(() => undefined),
|
||||
clientConstructorOptionsMock: vi.fn(),
|
||||
createDiscordAutoPresenceControllerMock: vi.fn(() => ({
|
||||
enabled: false,
|
||||
start: vi.fn(),
|
||||
stop: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
runNow: vi.fn(),
|
||||
})),
|
||||
createDiscordNativeCommandMock: vi.fn((params?: { command?: { name?: string } }) => ({
|
||||
name: params?.command?.name ?? "mock-command",
|
||||
})),
|
||||
createDiscordMessageHandlerMock: vi.fn(() =>
|
||||
Object.assign(
|
||||
vi.fn(async () => undefined),
|
||||
{
|
||||
deactivate: vi.fn(),
|
||||
},
|
||||
),
|
||||
),
|
||||
createNoopThreadBindingManagerMock: vi.fn(() => {
|
||||
const manager = { stop: vi.fn() };
|
||||
createdBindingManagers.push(manager);
|
||||
return manager;
|
||||
}),
|
||||
createThreadBindingManagerMock: vi.fn(() => {
|
||||
const manager = { stop: vi.fn() };
|
||||
createdBindingManagers.push(manager);
|
||||
return manager;
|
||||
}),
|
||||
reconcileAcpThreadBindingsOnStartupMock: vi.fn(() => ({
|
||||
checked: 0,
|
||||
removed: 0,
|
||||
staleSessionKeys: [],
|
||||
})),
|
||||
createdBindingManagers,
|
||||
getAcpSessionStatusMock: vi.fn(
|
||||
async (_params: { cfg: OpenClawConfig; sessionKey: string; signal?: AbortSignal }) => ({
|
||||
state: "idle",
|
||||
}),
|
||||
),
|
||||
getPluginCommandSpecsMock: vi.fn<(provider?: string) => PluginCommandSpecMock[]>(() => []),
|
||||
listNativeCommandSpecsForConfigMock: vi.fn<
|
||||
(
|
||||
cfg?: unknown,
|
||||
params?: { skillCommands?: unknown[]; provider?: string },
|
||||
) => NativeCommandSpecMock[]
|
||||
>(() => [{ name: "cmd", description: "built-in", acceptsArgs: false }]),
|
||||
listSkillCommandsForAgentsMock: vi.fn<
|
||||
(params?: { cfg?: unknown; agentIds?: string[] }) => unknown[]
|
||||
>(() => []),
|
||||
monitorLifecycleMock: vi.fn(async (params: { threadBindings: { stop: () => void } }) => {
|
||||
params.threadBindings.stop();
|
||||
}),
|
||||
resolveDiscordAccountMock: vi.fn((_) => ({
|
||||
accountId: "default",
|
||||
token: "cfg-token",
|
||||
config: baseDiscordAccountConfig(),
|
||||
})),
|
||||
resolveDiscordAllowlistConfigMock: vi.fn(async () => ({
|
||||
guildEntries: undefined,
|
||||
allowFrom: undefined,
|
||||
})),
|
||||
resolveNativeCommandsEnabledMock: vi.fn((_params) => true),
|
||||
resolveNativeSkillsEnabledMock: vi.fn((_params) => false),
|
||||
isVerboseMock,
|
||||
shouldLogVerboseMock,
|
||||
voiceRuntimeModuleLoadedMock: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
function buildDiscordSourceModuleId(artifactBasename: string): string {
|
||||
return `../../../extensions/discord/src/${artifactBasename}`;
|
||||
}
|
||||
|
||||
const {
|
||||
clientHandleDeployRequestMock,
|
||||
clientFetchUserMock,
|
||||
clientGetPluginMock,
|
||||
clientConstructorOptionsMock,
|
||||
createDiscordAutoPresenceControllerMock,
|
||||
createDiscordNativeCommandMock,
|
||||
createDiscordMessageHandlerMock,
|
||||
createNoopThreadBindingManagerMock,
|
||||
createThreadBindingManagerMock,
|
||||
reconcileAcpThreadBindingsOnStartupMock,
|
||||
createdBindingManagers,
|
||||
getAcpSessionStatusMock,
|
||||
getPluginCommandSpecsMock,
|
||||
listNativeCommandSpecsForConfigMock,
|
||||
listSkillCommandsForAgentsMock,
|
||||
monitorLifecycleMock,
|
||||
resolveDiscordAccountMock,
|
||||
resolveDiscordAllowlistConfigMock,
|
||||
resolveNativeCommandsEnabledMock,
|
||||
resolveNativeSkillsEnabledMock,
|
||||
isVerboseMock,
|
||||
shouldLogVerboseMock,
|
||||
voiceRuntimeModuleLoadedMock,
|
||||
} = providerMonitorTestMocks;
|
||||
|
||||
export function getProviderMonitorTestMocks(): typeof providerMonitorTestMocks {
|
||||
return providerMonitorTestMocks;
|
||||
}
|
||||
|
||||
export function mockResolvedDiscordAccountConfig(overrides: Record<string, unknown>) {
|
||||
resolveDiscordAccountMock.mockImplementation(() => ({
|
||||
accountId: "default",
|
||||
token: "cfg-token",
|
||||
config: {
|
||||
...baseDiscordAccountConfig(),
|
||||
...overrides,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function getFirstDiscordMessageHandlerParams<T extends object>() {
|
||||
expect(createDiscordMessageHandlerMock).toHaveBeenCalledTimes(1);
|
||||
const firstCall = createDiscordMessageHandlerMock.mock.calls.at(0) as [T] | undefined;
|
||||
return firstCall?.[0];
|
||||
}
|
||||
|
||||
export function resetDiscordProviderMonitorMocks(params?: {
|
||||
nativeCommands?: NativeCommandSpecMock[];
|
||||
}) {
|
||||
clientHandleDeployRequestMock.mockClear().mockResolvedValue(undefined);
|
||||
clientFetchUserMock.mockClear().mockResolvedValue({ id: "bot-1" });
|
||||
clientGetPluginMock.mockClear().mockReturnValue(undefined);
|
||||
clientConstructorOptionsMock.mockClear();
|
||||
createDiscordAutoPresenceControllerMock.mockClear().mockImplementation(() => ({
|
||||
enabled: false,
|
||||
start: vi.fn(),
|
||||
stop: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
runNow: vi.fn(),
|
||||
}));
|
||||
createDiscordNativeCommandMock.mockClear().mockImplementation((input) => ({
|
||||
name: input?.command?.name ?? "mock-command",
|
||||
}));
|
||||
createDiscordMessageHandlerMock.mockClear().mockImplementation(() =>
|
||||
Object.assign(
|
||||
vi.fn(async () => undefined),
|
||||
{
|
||||
deactivate: vi.fn(),
|
||||
},
|
||||
),
|
||||
);
|
||||
createNoopThreadBindingManagerMock.mockClear();
|
||||
createThreadBindingManagerMock.mockClear();
|
||||
reconcileAcpThreadBindingsOnStartupMock.mockClear().mockReturnValue({
|
||||
checked: 0,
|
||||
removed: 0,
|
||||
staleSessionKeys: [],
|
||||
});
|
||||
createdBindingManagers.length = 0;
|
||||
getAcpSessionStatusMock.mockClear().mockResolvedValue({ state: "idle" });
|
||||
getPluginCommandSpecsMock.mockClear().mockReturnValue([]);
|
||||
listNativeCommandSpecsForConfigMock
|
||||
.mockClear()
|
||||
.mockReturnValue(
|
||||
params?.nativeCommands ?? [{ name: "cmd", description: "built-in", acceptsArgs: false }],
|
||||
);
|
||||
listSkillCommandsForAgentsMock.mockClear().mockReturnValue([]);
|
||||
monitorLifecycleMock.mockClear().mockImplementation(async (monitorParams) => {
|
||||
monitorParams.threadBindings.stop();
|
||||
});
|
||||
resolveDiscordAccountMock.mockClear().mockReturnValue({
|
||||
accountId: "default",
|
||||
token: "cfg-token",
|
||||
config: baseDiscordAccountConfig(),
|
||||
});
|
||||
resolveDiscordAllowlistConfigMock.mockClear().mockResolvedValue({
|
||||
guildEntries: undefined,
|
||||
allowFrom: undefined,
|
||||
});
|
||||
resolveNativeCommandsEnabledMock.mockClear().mockReturnValue(true);
|
||||
resolveNativeSkillsEnabledMock.mockClear().mockReturnValue(false);
|
||||
isVerboseMock.mockClear().mockReturnValue(false);
|
||||
shouldLogVerboseMock.mockClear().mockReturnValue(false);
|
||||
voiceRuntimeModuleLoadedMock.mockClear();
|
||||
}
|
||||
|
||||
export const baseRuntime = (): RuntimeEnv => ({
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
});
|
||||
|
||||
export const baseConfig = (): OpenClawConfig =>
|
||||
({
|
||||
channels: {
|
||||
discord: {
|
||||
accounts: {
|
||||
default: {
|
||||
token: "MTIz.abc.def",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}) as OpenClawConfig;
|
||||
|
||||
vi.mock("@buape/carbon", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@buape/carbon")>();
|
||||
class RateLimitError extends Error {
|
||||
status = 429;
|
||||
discordCode?: number;
|
||||
retryAfter: number;
|
||||
scope: string | null;
|
||||
bucket: string | null;
|
||||
constructor(
|
||||
response: Response,
|
||||
body: { message: string; retry_after: number; global: boolean },
|
||||
) {
|
||||
super(body.message);
|
||||
this.retryAfter = body.retry_after;
|
||||
this.scope = body.global ? "global" : response.headers.get("X-RateLimit-Scope");
|
||||
this.bucket = response.headers.get("X-RateLimit-Bucket");
|
||||
}
|
||||
}
|
||||
class Client {
|
||||
listeners: unknown[];
|
||||
rest: { put: ReturnType<typeof vi.fn> };
|
||||
options: unknown;
|
||||
constructor(options: unknown, handlers: { listeners?: unknown[] }) {
|
||||
this.options = options;
|
||||
this.listeners = handlers.listeners ?? [];
|
||||
this.rest = { put: vi.fn(async () => undefined) };
|
||||
clientConstructorOptionsMock(options);
|
||||
}
|
||||
async handleDeployRequest() {
|
||||
return await clientHandleDeployRequestMock();
|
||||
}
|
||||
async fetchUser(target: string) {
|
||||
return await clientFetchUserMock(target);
|
||||
}
|
||||
getPlugin(name: string) {
|
||||
return clientGetPluginMock(name);
|
||||
}
|
||||
}
|
||||
return { ...actual, Client, RateLimitError };
|
||||
});
|
||||
|
||||
vi.mock("@buape/carbon/gateway", () => ({
|
||||
GatewayCloseCodes: { DisallowedIntents: 4014 },
|
||||
}));
|
||||
|
||||
vi.mock("@buape/carbon/voice", () => ({
|
||||
VoicePlugin: class VoicePlugin {},
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/acp-runtime", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/acp-runtime")>(
|
||||
"openclaw/plugin-sdk/acp-runtime",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
getAcpSessionManager: () => ({
|
||||
getSessionStatus: getAcpSessionStatusMock,
|
||||
}),
|
||||
isAcpRuntimeError: (error: unknown): error is { code: string } =>
|
||||
error instanceof Error && "code" in error,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/command-auth", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/command-auth")>(
|
||||
"openclaw/plugin-sdk/command-auth",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
listNativeCommandSpecsForConfig: listNativeCommandSpecsForConfigMock,
|
||||
listSkillCommandsForAgents: listSkillCommandsForAgentsMock,
|
||||
};
|
||||
});
|
||||
vi.mock("openclaw/plugin-sdk/reply-runtime", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/reply-runtime")>(
|
||||
"openclaw/plugin-sdk/reply-runtime",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolveTextChunkLimit: () => 2000,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/config-runtime", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/config-runtime")>(
|
||||
"openclaw/plugin-sdk/config-runtime",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
isNativeCommandsExplicitlyDisabled: () => false,
|
||||
loadConfig: () => ({}),
|
||||
resolveNativeCommandsEnabled: resolveNativeCommandsEnabledMock,
|
||||
resolveNativeSkillsEnabled: resolveNativeSkillsEnabledMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/runtime-env")>(
|
||||
"openclaw/plugin-sdk/runtime-env",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
danger: (value: string) => value,
|
||||
isVerbose: isVerboseMock,
|
||||
logVerbose: vi.fn(),
|
||||
shouldLogVerbose: shouldLogVerboseMock,
|
||||
warn: (value: string) => value,
|
||||
createSubsystemLogger: () => {
|
||||
const logger = {
|
||||
child: vi.fn(() => logger),
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
};
|
||||
return logger;
|
||||
},
|
||||
createNonExitingRuntime: () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() }),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/infra-runtime", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/infra-runtime")>(
|
||||
"openclaw/plugin-sdk/infra-runtime",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
formatErrorMessage: (error: unknown) => String(error),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("accounts.js"), () => ({
|
||||
resolveDiscordAccount: resolveDiscordAccountMock,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("probe.js"), () => ({
|
||||
fetchDiscordApplicationId: async () => "app-1",
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("token.js"), () => ({
|
||||
normalizeDiscordToken: (value?: string) => value,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("voice/command.js"), () => ({
|
||||
createDiscordVoiceCommand: () => ({ name: "voice-command" }),
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/agent-components.js"), () => ({
|
||||
createAgentComponentButton: () => ({ id: "btn" }),
|
||||
createAgentSelectMenu: () => ({ id: "menu" }),
|
||||
createDiscordComponentButton: () => ({ id: "btn2" }),
|
||||
createDiscordComponentChannelSelect: () => ({ id: "channel" }),
|
||||
createDiscordComponentMentionableSelect: () => ({ id: "mentionable" }),
|
||||
createDiscordComponentModal: () => ({ id: "modal" }),
|
||||
createDiscordComponentRoleSelect: () => ({ id: "role" }),
|
||||
createDiscordComponentStringSelect: () => ({ id: "string" }),
|
||||
createDiscordComponentUserSelect: () => ({ id: "user" }),
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/auto-presence.js"), () => ({
|
||||
createDiscordAutoPresenceController: createDiscordAutoPresenceControllerMock,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/commands.js"), () => ({
|
||||
resolveDiscordSlashCommandConfig: () => ({ ephemeral: false }),
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/exec-approvals.js"), () => ({
|
||||
createExecApprovalButton: () => ({ id: "exec-approval" }),
|
||||
DiscordExecApprovalHandler: class DiscordExecApprovalHandler {
|
||||
async start() {
|
||||
return undefined;
|
||||
}
|
||||
async stop() {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/gateway-plugin.js"), () => ({
|
||||
createDiscordGatewayPlugin: () => ({ id: "gateway-plugin" }),
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/listeners.js"), () => ({
|
||||
DiscordMessageListener: class DiscordMessageListener {},
|
||||
DiscordPresenceListener: class DiscordPresenceListener {},
|
||||
DiscordReactionListener: class DiscordReactionListener {},
|
||||
DiscordReactionRemoveListener: class DiscordReactionRemoveListener {},
|
||||
DiscordThreadUpdateListener: class DiscordThreadUpdateListener {},
|
||||
registerDiscordListener: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/message-handler.js"), () => ({
|
||||
createDiscordMessageHandler: createDiscordMessageHandlerMock,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/native-command.js"), () => ({
|
||||
createDiscordCommandArgFallbackButton: () => ({ id: "arg-fallback" }),
|
||||
createDiscordModelPickerFallbackButton: () => ({ id: "model-fallback-btn" }),
|
||||
createDiscordModelPickerFallbackSelect: () => ({ id: "model-fallback-select" }),
|
||||
createDiscordNativeCommand: createDiscordNativeCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/presence.js"), () => ({
|
||||
resolveDiscordPresenceUpdate: () => undefined,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/provider.allowlist.js"), () => ({
|
||||
resolveDiscordAllowlistConfig: resolveDiscordAllowlistConfigMock,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/provider.lifecycle.js"), () => ({
|
||||
runDiscordGatewayLifecycle: monitorLifecycleMock,
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/rest-fetch.js"), () => ({
|
||||
resolveDiscordRestFetch: () => async () => {
|
||||
throw new Error("offline");
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock(buildDiscordSourceModuleId("monitor/thread-bindings.js"), () => ({
|
||||
createNoopThreadBindingManager: createNoopThreadBindingManagerMock,
|
||||
createThreadBindingManager: createThreadBindingManagerMock,
|
||||
reconcileAcpThreadBindingsOnStartup: reconcileAcpThreadBindingsOnStartupMock,
|
||||
resolveThreadBindingIdleTimeoutMs: vi.fn(() => 24 * 60 * 60 * 1000),
|
||||
}));
|
||||
@@ -1 +0,0 @@
|
||||
export { useFrozenTime, useRealTime } from "../../../src/test-utils/frozen-time.js";
|
||||
@@ -1,8 +0,0 @@
|
||||
export type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
export {
|
||||
__testing,
|
||||
registerSessionBindingAdapter,
|
||||
} from "../../../src/infra/outbound/session-binding-service.js";
|
||||
export { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
|
||||
export { resolveAgentRoute } from "../../../src/routing/resolve-route.js";
|
||||
export { createTestRegistry } from "../../../src/test-utils/channel-plugins.js";
|
||||
@@ -1,8 +0,0 @@
|
||||
export type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
export {
|
||||
__testing as sessionBindingTesting,
|
||||
registerSessionBindingAdapter,
|
||||
} from "../../../src/infra/outbound/session-binding-service.js";
|
||||
export { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
|
||||
export { resolveAgentRoute } from "../../../src/routing/resolve-route.js";
|
||||
export { createTestRegistry } from "../../../src/test-utils/channel-plugins.js";
|
||||
@@ -1 +0,0 @@
|
||||
export type { OpenClawPluginCommandDefinition } from "openclaw/plugin-sdk/core";
|
||||
@@ -1,7 +0,0 @@
|
||||
export {
|
||||
__testing as sessionBindingTesting,
|
||||
registerSessionBindingAdapter,
|
||||
} from "../../../src/infra/outbound/session-binding-service.js";
|
||||
export { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
|
||||
export { resolveAgentRoute } from "../../../src/routing/resolve-route.js";
|
||||
export { createTestRegistry } from "../../../src/test-utils/channel-plugins.js";
|
||||
@@ -1,114 +0,0 @@
|
||||
import { beforeAll, beforeEach, describe, it, vi } from "vitest";
|
||||
import {
|
||||
expectAugmentedCodexCatalog,
|
||||
expectCodexBuiltInSuppression,
|
||||
expectCodexMissingAuthHint,
|
||||
} from "../../../src/plugins/provider-runtime.test-support.js";
|
||||
import type { ProviderPlugin } from "../../../src/plugins/types.js";
|
||||
import { loadBundledPluginPublicSurfaceSync } from "../../../src/test-utils/bundled-plugin-public-surface.js";
|
||||
import { registerProviderPlugin, requireRegisteredProvider } from "./provider-registration.js";
|
||||
|
||||
const PROVIDER_CATALOG_CONTRACT_TIMEOUT_MS = 300_000;
|
||||
|
||||
type ResolvePluginProviders =
|
||||
typeof import("../../../src/plugins/providers.runtime.js").resolvePluginProviders;
|
||||
type ResolveOwningPluginIdsForProvider =
|
||||
typeof import("../../../src/plugins/providers.js").resolveOwningPluginIdsForProvider;
|
||||
type ResolveCatalogHookProviderPluginIds =
|
||||
typeof import("../../../src/plugins/providers.js").resolveCatalogHookProviderPluginIds;
|
||||
|
||||
const resolvePluginProvidersMock = vi.hoisted(() => vi.fn<ResolvePluginProviders>(() => []));
|
||||
const resolveOwningPluginIdsForProviderMock = vi.hoisted(() =>
|
||||
vi.fn<ResolveOwningPluginIdsForProvider>(() => undefined),
|
||||
);
|
||||
const resolveCatalogHookProviderPluginIdsMock = vi.hoisted(() =>
|
||||
vi.fn<ResolveCatalogHookProviderPluginIds>((_) => [] as string[]),
|
||||
);
|
||||
|
||||
vi.mock("../../../src/plugins/providers.js", () => ({
|
||||
resolveOwningPluginIdsForProvider: (params: unknown) =>
|
||||
resolveOwningPluginIdsForProviderMock(params as never),
|
||||
resolveCatalogHookProviderPluginIds: (params: unknown) =>
|
||||
resolveCatalogHookProviderPluginIdsMock(params as never),
|
||||
}));
|
||||
|
||||
vi.mock("../../../src/plugins/providers.runtime.js", () => ({
|
||||
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
|
||||
}));
|
||||
|
||||
export function describeOpenAIProviderCatalogContract() {
|
||||
let augmentModelCatalogWithProviderPlugins: typeof import("../../../src/plugins/provider-runtime.js").augmentModelCatalogWithProviderPlugins;
|
||||
let resetProviderRuntimeHookCacheForTest: typeof import("../../../src/plugins/provider-runtime.js").resetProviderRuntimeHookCacheForTest;
|
||||
let resolveProviderBuiltInModelSuppression: typeof import("../../../src/plugins/provider-runtime.js").resolveProviderBuiltInModelSuppression;
|
||||
let openaiProviders: ProviderPlugin[];
|
||||
let openaiProvider: ProviderPlugin;
|
||||
|
||||
describe(
|
||||
"openai provider catalog contract",
|
||||
{ timeout: PROVIDER_CATALOG_CONTRACT_TIMEOUT_MS },
|
||||
() => {
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
const openaiPlugin = loadBundledPluginPublicSurfaceSync<{
|
||||
default: Parameters<typeof registerProviderPlugin>[0]["plugin"];
|
||||
}>({
|
||||
pluginId: "openai",
|
||||
artifactBasename: "index.js",
|
||||
});
|
||||
openaiProviders = registerProviderPlugin({
|
||||
plugin: openaiPlugin.default,
|
||||
id: "openai",
|
||||
name: "OpenAI",
|
||||
}).providers;
|
||||
openaiProvider = requireRegisteredProvider(openaiProviders, "openai", "provider");
|
||||
({
|
||||
augmentModelCatalogWithProviderPlugins,
|
||||
resetProviderRuntimeHookCacheForTest,
|
||||
resolveProviderBuiltInModelSuppression,
|
||||
} = await import("../../../src/plugins/provider-runtime.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetProviderRuntimeHookCacheForTest();
|
||||
|
||||
resolvePluginProvidersMock.mockReset();
|
||||
resolvePluginProvidersMock.mockImplementation((params?: { onlyPluginIds?: string[] }) => {
|
||||
const onlyPluginIds = params?.onlyPluginIds;
|
||||
if (!onlyPluginIds || onlyPluginIds.length === 0) {
|
||||
return openaiProviders;
|
||||
}
|
||||
return onlyPluginIds.includes("openai") ? openaiProviders : [];
|
||||
});
|
||||
|
||||
resolveOwningPluginIdsForProviderMock.mockReset();
|
||||
resolveOwningPluginIdsForProviderMock.mockImplementation((params) => {
|
||||
switch (params.provider) {
|
||||
case "azure-openai-responses":
|
||||
case "openai":
|
||||
case "openai-codex":
|
||||
return ["openai"];
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
resolveCatalogHookProviderPluginIdsMock.mockReset();
|
||||
resolveCatalogHookProviderPluginIdsMock.mockReturnValue(["openai"]);
|
||||
});
|
||||
|
||||
it("keeps codex-only missing-auth hints wired through the provider runtime", () => {
|
||||
expectCodexMissingAuthHint(
|
||||
(params) => openaiProvider.buildMissingAuthMessage?.(params.context) ?? undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps built-in model suppression wired through the provider runtime", () => {
|
||||
expectCodexBuiltInSuppression(resolveProviderBuiltInModelSuppression);
|
||||
});
|
||||
|
||||
it("keeps bundled model augmentation wired through the provider runtime", async () => {
|
||||
await expectAugmentedCodexCatalog(augmentModelCatalogWithProviderPlugins);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
export const pluginCommandMocks = {
|
||||
getPluginCommandSpecs: vi.fn(() => []),
|
||||
matchPluginCommand: vi.fn(() => null),
|
||||
executePluginCommand: vi.fn(async () => ({ text: "ok" })),
|
||||
};
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/plugin-runtime", () => ({
|
||||
getPluginCommandSpecs: pluginCommandMocks.getPluginCommandSpecs,
|
||||
matchPluginCommand: pluginCommandMocks.matchPluginCommand,
|
||||
executePluginCommand: pluginCommandMocks.executePluginCommand,
|
||||
}));
|
||||
|
||||
export function resetPluginCommandMocks() {
|
||||
pluginCommandMocks.getPluginCommandSpecs.mockClear();
|
||||
pluginCommandMocks.getPluginCommandSpecs.mockReturnValue([]);
|
||||
pluginCommandMocks.matchPluginCommand.mockClear();
|
||||
pluginCommandMocks.matchPluginCommand.mockReturnValue(null);
|
||||
pluginCommandMocks.executePluginCommand.mockClear();
|
||||
pluginCommandMocks.executePluginCommand.mockResolvedValue({ text: "ok" });
|
||||
}
|
||||
Reference in New Issue
Block a user