mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-10 08:41:13 +00:00
fix(types): annotate portable exported helper types
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import { expect, vi } from "vitest";
|
||||
import { expect, vi, type Mock } from "vitest";
|
||||
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
|
||||
import { handleBlueBubblesWebhookRequest } from "./monitor.js";
|
||||
import { registerBlueBubblesWebhookTarget } from "./monitor.js";
|
||||
@@ -16,6 +16,11 @@ export type WebhookRequestParams = {
|
||||
};
|
||||
|
||||
export const LOOPBACK_REMOTE_ADDRESSES_FOR_TEST = ["127.0.0.1", "::1", "::ffff:127.0.0.1"] as const;
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type HangingWebhookRequestForTest = {
|
||||
req: IncomingMessage;
|
||||
destroyMock: UnknownMock;
|
||||
};
|
||||
|
||||
export function createMockAccount(
|
||||
overrides: Partial<ResolvedBlueBubblesAccount["config"]> = {},
|
||||
@@ -182,7 +187,7 @@ export function createLoopbackWebhookRequestParamsForTest(
|
||||
export function createHangingWebhookRequestForTest(
|
||||
url = "/bluebubbles-webhook?password=test-password",
|
||||
remoteAddress = "127.0.0.1",
|
||||
) {
|
||||
): HangingWebhookRequestForTest {
|
||||
const req = new EventEmitter() as IncomingMessage;
|
||||
const destroyMock = vi.fn();
|
||||
req.method = "POST";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Command } from "commander";
|
||||
import { vi } from "vitest";
|
||||
import * as parentCoreApiModule from "../core-api.js";
|
||||
import * as browserCliSharedModule from "./browser-cli-shared.js";
|
||||
@@ -56,7 +57,7 @@ vi.spyOn(cliCoreApiModule.defaultRuntime, "exit").mockImplementation(browserCliR
|
||||
|
||||
const { registerBrowserManageCommands } = await import("./browser-cli-manage.js");
|
||||
|
||||
export function createBrowserManageProgram(params?: { withParentTimeout?: boolean }) {
|
||||
export function createBrowserManageProgram(params?: { withParentTimeout?: boolean }): Command {
|
||||
const { program, browser, parentOpts } = createBrowserProgram();
|
||||
if (params?.withParentTimeout) {
|
||||
browser.option("--timeout <ms>", "Timeout in ms", "30000");
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
import { expect, vi } from "vitest";
|
||||
import { expect, vi, type Mock } from "vitest";
|
||||
|
||||
export function createDiscordOutboundHoisted() {
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
|
||||
type DiscordOutboundHoisted = {
|
||||
sendMessageDiscordMock: AsyncUnknownMock;
|
||||
sendDiscordComponentMessageMock: AsyncUnknownMock;
|
||||
sendPollDiscordMock: AsyncUnknownMock;
|
||||
sendWebhookMessageDiscordMock: AsyncUnknownMock;
|
||||
getThreadBindingManagerMock: UnknownMock;
|
||||
};
|
||||
|
||||
type DiscordSendModule = typeof import("./send.js");
|
||||
type DiscordSendComponentsModule = typeof import("./send.components.js");
|
||||
type DiscordThreadBindingsModule = typeof import("./monitor/thread-bindings.js");
|
||||
|
||||
function invokeMock<TArgs extends unknown[], TResult>(
|
||||
mock: (...args: unknown[]) => unknown,
|
||||
...args: TArgs
|
||||
): TResult {
|
||||
return mock(...args) as TResult;
|
||||
}
|
||||
|
||||
export function createDiscordOutboundHoisted(): DiscordOutboundHoisted {
|
||||
const sendMessageDiscordMock = vi.fn();
|
||||
const sendDiscordComponentMessageMock = vi.fn();
|
||||
const sendPollDiscordMock = vi.fn();
|
||||
@@ -21,28 +43,94 @@ export const DEFAULT_DISCORD_SEND_RESULT = {
|
||||
channelId: "ch-1",
|
||||
} as const;
|
||||
|
||||
type DiscordOutboundHoisted = ReturnType<typeof createDiscordOutboundHoisted>;
|
||||
export async function createDiscordSendModuleMock(
|
||||
hoisted: DiscordOutboundHoisted,
|
||||
importOriginal: () => Promise<DiscordSendModule>,
|
||||
): Promise<DiscordSendModule> {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
sendMessageDiscord: (...args: Parameters<DiscordSendModule["sendMessageDiscord"]>) =>
|
||||
invokeMock<
|
||||
Parameters<DiscordSendModule["sendMessageDiscord"]>,
|
||||
ReturnType<DiscordSendModule["sendMessageDiscord"]>
|
||||
>(hoisted.sendMessageDiscordMock, ...args),
|
||||
sendPollDiscord: (...args: Parameters<DiscordSendModule["sendPollDiscord"]>) =>
|
||||
invokeMock<
|
||||
Parameters<DiscordSendModule["sendPollDiscord"]>,
|
||||
ReturnType<DiscordSendModule["sendPollDiscord"]>
|
||||
>(hoisted.sendPollDiscordMock, ...args),
|
||||
sendWebhookMessageDiscord: (
|
||||
...args: Parameters<DiscordSendModule["sendWebhookMessageDiscord"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<DiscordSendModule["sendWebhookMessageDiscord"]>,
|
||||
ReturnType<DiscordSendModule["sendWebhookMessageDiscord"]>
|
||||
>(hoisted.sendWebhookMessageDiscordMock, ...args),
|
||||
};
|
||||
}
|
||||
|
||||
export async function createDiscordSendComponentsModuleMock(
|
||||
hoisted: DiscordOutboundHoisted,
|
||||
importOriginal: () => Promise<DiscordSendComponentsModule>,
|
||||
): Promise<DiscordSendComponentsModule> {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
sendDiscordComponentMessage: (
|
||||
...args: Parameters<DiscordSendComponentsModule["sendDiscordComponentMessage"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<DiscordSendComponentsModule["sendDiscordComponentMessage"]>,
|
||||
ReturnType<DiscordSendComponentsModule["sendDiscordComponentMessage"]>
|
||||
>(hoisted.sendDiscordComponentMessageMock, ...args),
|
||||
};
|
||||
}
|
||||
|
||||
export async function createDiscordThreadBindingsModuleMock(
|
||||
hoisted: DiscordOutboundHoisted,
|
||||
importOriginal: () => Promise<DiscordThreadBindingsModule>,
|
||||
): Promise<DiscordThreadBindingsModule> {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
getThreadBindingManager: (
|
||||
...args: Parameters<DiscordThreadBindingsModule["getThreadBindingManager"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<DiscordThreadBindingsModule["getThreadBindingManager"]>,
|
||||
ReturnType<DiscordThreadBindingsModule["getThreadBindingManager"]>
|
||||
>(hoisted.getThreadBindingManagerMock, ...args),
|
||||
};
|
||||
}
|
||||
|
||||
export async function installDiscordOutboundModuleSpies(hoisted: DiscordOutboundHoisted) {
|
||||
const sendModule = await import("./send.js");
|
||||
vi.spyOn(sendModule, "sendMessageDiscord").mockImplementation((...args: unknown[]) =>
|
||||
hoisted.sendMessageDiscordMock(...args),
|
||||
const mockedSendModule = await createDiscordSendModuleMock(hoisted, async () => sendModule);
|
||||
vi.spyOn(sendModule, "sendMessageDiscord").mockImplementation(
|
||||
mockedSendModule.sendMessageDiscord,
|
||||
);
|
||||
vi.spyOn(sendModule, "sendPollDiscord").mockImplementation((...args: unknown[]) =>
|
||||
hoisted.sendPollDiscordMock(...args),
|
||||
);
|
||||
vi.spyOn(sendModule, "sendWebhookMessageDiscord").mockImplementation((...args: unknown[]) =>
|
||||
hoisted.sendWebhookMessageDiscordMock(...args),
|
||||
vi.spyOn(sendModule, "sendPollDiscord").mockImplementation(mockedSendModule.sendPollDiscord);
|
||||
vi.spyOn(sendModule, "sendWebhookMessageDiscord").mockImplementation(
|
||||
mockedSendModule.sendWebhookMessageDiscord,
|
||||
);
|
||||
|
||||
const sendComponentsModule = await import("./send.components.js");
|
||||
const mockedSendComponentsModule = await createDiscordSendComponentsModuleMock(
|
||||
hoisted,
|
||||
async () => sendComponentsModule,
|
||||
);
|
||||
vi.spyOn(sendComponentsModule, "sendDiscordComponentMessage").mockImplementation(
|
||||
(...args: unknown[]) => hoisted.sendDiscordComponentMessageMock(...args),
|
||||
mockedSendComponentsModule.sendDiscordComponentMessage,
|
||||
);
|
||||
|
||||
const threadBindingsModule = await import("./monitor/thread-bindings.js");
|
||||
const mockedThreadBindingsModule = await createDiscordThreadBindingsModuleMock(
|
||||
hoisted,
|
||||
async () => threadBindingsModule,
|
||||
);
|
||||
vi.spyOn(threadBindingsModule, "getThreadBindingManager").mockImplementation(
|
||||
(...args: unknown[]) => hoisted.getThreadBindingManagerMock(...args),
|
||||
mockedThreadBindingsModule.getThreadBindingManager,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,51 @@
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
import { parsePluginBindingApprovalCustomId } from "../../../../src/plugins/conversation-binding.js";
|
||||
import { resolvePinnedMainDmOwnerFromAllowlist } from "../../../../src/security/dm-policy-shared.js";
|
||||
|
||||
const runtimeMocks = vi.hoisted(() => ({
|
||||
buildPluginBindingResolvedTextMock: vi.fn(),
|
||||
dispatchPluginInteractiveHandlerMock: vi.fn(),
|
||||
dispatchReplyMock: vi.fn(),
|
||||
enqueueSystemEventMock: vi.fn(),
|
||||
readAllowFromStoreMock: vi.fn(),
|
||||
readSessionUpdatedAtMock: vi.fn(),
|
||||
recordInboundSessionMock: vi.fn(),
|
||||
resolveStorePathMock: vi.fn(),
|
||||
resolvePluginConversationBindingApprovalMock: vi.fn(),
|
||||
upsertPairingRequestMock: vi.fn(),
|
||||
}));
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
|
||||
export const readAllowFromStoreMock = runtimeMocks.readAllowFromStoreMock;
|
||||
export const dispatchPluginInteractiveHandlerMock =
|
||||
type DiscordComponentRuntimeMocks = {
|
||||
buildPluginBindingResolvedTextMock: UnknownMock;
|
||||
dispatchPluginInteractiveHandlerMock: AsyncUnknownMock;
|
||||
dispatchReplyMock: UnknownMock;
|
||||
enqueueSystemEventMock: UnknownMock;
|
||||
readAllowFromStoreMock: AsyncUnknownMock;
|
||||
readSessionUpdatedAtMock: UnknownMock;
|
||||
recordInboundSessionMock: AsyncUnknownMock;
|
||||
resolveStorePathMock: UnknownMock;
|
||||
resolvePluginConversationBindingApprovalMock: AsyncUnknownMock;
|
||||
upsertPairingRequestMock: AsyncUnknownMock;
|
||||
};
|
||||
|
||||
const runtimeMocks = vi.hoisted(
|
||||
(): DiscordComponentRuntimeMocks => ({
|
||||
buildPluginBindingResolvedTextMock: vi.fn(),
|
||||
dispatchPluginInteractiveHandlerMock: vi.fn(),
|
||||
dispatchReplyMock: vi.fn(),
|
||||
enqueueSystemEventMock: vi.fn(),
|
||||
readAllowFromStoreMock: vi.fn(),
|
||||
readSessionUpdatedAtMock: vi.fn(),
|
||||
recordInboundSessionMock: vi.fn(),
|
||||
resolveStorePathMock: vi.fn(),
|
||||
resolvePluginConversationBindingApprovalMock: vi.fn(),
|
||||
upsertPairingRequestMock: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
export const readAllowFromStoreMock: AsyncUnknownMock = runtimeMocks.readAllowFromStoreMock;
|
||||
export const dispatchPluginInteractiveHandlerMock: AsyncUnknownMock =
|
||||
runtimeMocks.dispatchPluginInteractiveHandlerMock;
|
||||
export const dispatchReplyMock = runtimeMocks.dispatchReplyMock;
|
||||
export const enqueueSystemEventMock = runtimeMocks.enqueueSystemEventMock;
|
||||
export const upsertPairingRequestMock = runtimeMocks.upsertPairingRequestMock;
|
||||
export const recordInboundSessionMock = runtimeMocks.recordInboundSessionMock;
|
||||
export const readSessionUpdatedAtMock = runtimeMocks.readSessionUpdatedAtMock;
|
||||
export const resolveStorePathMock = runtimeMocks.resolveStorePathMock;
|
||||
export const resolvePluginConversationBindingApprovalMock =
|
||||
export const dispatchReplyMock: UnknownMock = runtimeMocks.dispatchReplyMock;
|
||||
export const enqueueSystemEventMock: UnknownMock = runtimeMocks.enqueueSystemEventMock;
|
||||
export const upsertPairingRequestMock: AsyncUnknownMock = runtimeMocks.upsertPairingRequestMock;
|
||||
export const recordInboundSessionMock: AsyncUnknownMock = runtimeMocks.recordInboundSessionMock;
|
||||
export const readSessionUpdatedAtMock: UnknownMock = runtimeMocks.readSessionUpdatedAtMock;
|
||||
export const resolveStorePathMock: UnknownMock = runtimeMocks.resolveStorePathMock;
|
||||
export const resolvePluginConversationBindingApprovalMock: AsyncUnknownMock =
|
||||
runtimeMocks.resolvePluginConversationBindingApprovalMock;
|
||||
export const buildPluginBindingResolvedTextMock = runtimeMocks.buildPluginBindingResolvedTextMock;
|
||||
export const buildPluginBindingResolvedTextMock: UnknownMock =
|
||||
runtimeMocks.buildPluginBindingResolvedTextMock;
|
||||
|
||||
async function readStoreAllowFromForDmPolicy(params: {
|
||||
provider: string;
|
||||
|
||||
@@ -1,31 +1,77 @@
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
|
||||
type BoundConversation = {
|
||||
bindingId: string;
|
||||
targetSessionKey: string;
|
||||
};
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type FinalizeInboundContextMock = Mock<
|
||||
(ctx: Record<string, unknown>, opts?: unknown) => Record<string, unknown>
|
||||
>;
|
||||
type DispatchReplyCounts = {
|
||||
final: number;
|
||||
block?: number;
|
||||
tool?: number;
|
||||
};
|
||||
type DispatchReplyContext = Record<string, unknown> & {
|
||||
SessionKey?: string;
|
||||
};
|
||||
type DispatchReplyDispatcher = {
|
||||
sendFinalReply: (payload: { text: string }) => unknown | Promise<unknown>;
|
||||
};
|
||||
type DispatchReplyFromConfigMock = Mock<
|
||||
(params: {
|
||||
ctx: DispatchReplyContext;
|
||||
dispatcher: DispatchReplyDispatcher;
|
||||
}) => Promise<{ queuedFinal: boolean; counts: DispatchReplyCounts }>
|
||||
>;
|
||||
type WithReplyDispatcherMock = Mock<
|
||||
(params: { run: () => unknown | Promise<unknown> }) => Promise<unknown>
|
||||
>;
|
||||
type FeishuLifecycleTestMocks = {
|
||||
createEventDispatcherMock: UnknownMock;
|
||||
monitorWebSocketMock: AsyncUnknownMock;
|
||||
monitorWebhookMock: AsyncUnknownMock;
|
||||
createFeishuThreadBindingManagerMock: UnknownMock;
|
||||
createFeishuReplyDispatcherMock: UnknownMock;
|
||||
resolveBoundConversationMock: Mock<() => BoundConversation | null>;
|
||||
touchBindingMock: UnknownMock;
|
||||
resolveAgentRouteMock: UnknownMock;
|
||||
resolveConfiguredBindingRouteMock: UnknownMock;
|
||||
ensureConfiguredBindingRouteReadyMock: UnknownMock;
|
||||
dispatchReplyFromConfigMock: DispatchReplyFromConfigMock;
|
||||
withReplyDispatcherMock: WithReplyDispatcherMock;
|
||||
finalizeInboundContextMock: FinalizeInboundContextMock;
|
||||
getMessageFeishuMock: AsyncUnknownMock;
|
||||
listFeishuThreadMessagesMock: AsyncUnknownMock;
|
||||
sendMessageFeishuMock: AsyncUnknownMock;
|
||||
sendCardFeishuMock: AsyncUnknownMock;
|
||||
};
|
||||
|
||||
const feishuLifecycleTestMocks = vi.hoisted(() => ({
|
||||
createEventDispatcherMock: vi.fn(),
|
||||
monitorWebSocketMock: vi.fn(async () => {}),
|
||||
monitorWebhookMock: vi.fn(async () => {}),
|
||||
createFeishuThreadBindingManagerMock: vi.fn(() => ({ stop: vi.fn() })),
|
||||
createFeishuReplyDispatcherMock: vi.fn(),
|
||||
resolveBoundConversationMock: vi.fn<() => BoundConversation | null>(() => null),
|
||||
touchBindingMock: vi.fn(),
|
||||
resolveAgentRouteMock: vi.fn(),
|
||||
resolveConfiguredBindingRouteMock: vi.fn(),
|
||||
ensureConfiguredBindingRouteReadyMock: vi.fn(),
|
||||
dispatchReplyFromConfigMock: vi.fn(),
|
||||
withReplyDispatcherMock: vi.fn(),
|
||||
finalizeInboundContextMock: vi.fn((ctx) => ctx),
|
||||
getMessageFeishuMock: vi.fn(async () => null),
|
||||
listFeishuThreadMessagesMock: vi.fn(async () => []),
|
||||
sendMessageFeishuMock: vi.fn(async () => ({ messageId: "om_sent", chatId: "chat_default" })),
|
||||
sendCardFeishuMock: vi.fn(async () => ({ messageId: "om_card", chatId: "chat_default" })),
|
||||
}));
|
||||
const feishuLifecycleTestMocks = vi.hoisted(
|
||||
(): FeishuLifecycleTestMocks => ({
|
||||
createEventDispatcherMock: vi.fn(),
|
||||
monitorWebSocketMock: vi.fn(async () => {}),
|
||||
monitorWebhookMock: vi.fn(async () => {}),
|
||||
createFeishuThreadBindingManagerMock: vi.fn(() => ({ stop: vi.fn() })),
|
||||
createFeishuReplyDispatcherMock: vi.fn(),
|
||||
resolveBoundConversationMock: vi.fn<() => BoundConversation | null>(() => null),
|
||||
touchBindingMock: vi.fn(),
|
||||
resolveAgentRouteMock: vi.fn(),
|
||||
resolveConfiguredBindingRouteMock: vi.fn(),
|
||||
ensureConfiguredBindingRouteReadyMock: vi.fn(),
|
||||
dispatchReplyFromConfigMock: vi.fn(),
|
||||
withReplyDispatcherMock: vi.fn(),
|
||||
finalizeInboundContextMock: vi.fn((ctx) => ctx),
|
||||
getMessageFeishuMock: vi.fn(async () => null),
|
||||
listFeishuThreadMessagesMock: vi.fn(async () => []),
|
||||
sendMessageFeishuMock: vi.fn(async () => ({ messageId: "om_sent", chatId: "chat_default" })),
|
||||
sendCardFeishuMock: vi.fn(async () => ({ messageId: "om_card", chatId: "chat_default" })),
|
||||
}),
|
||||
);
|
||||
|
||||
export function getFeishuLifecycleTestMocks() {
|
||||
export function getFeishuLifecycleTestMocks(): FeishuLifecycleTestMocks {
|
||||
return feishuLifecycleTestMocks;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { expect, vi } from "vitest";
|
||||
import { expect, vi, type Mock } from "vitest";
|
||||
import { createPluginRuntimeMock } from "../../../../test/helpers/plugins/plugin-runtime-mock.js";
|
||||
import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "../../runtime-api.js";
|
||||
import { setFeishuRuntime } from "../runtime.js";
|
||||
@@ -9,6 +9,37 @@ type InboundDebouncerParams<T> = {
|
||||
onFlush?: (items: T[]) => Promise<void>;
|
||||
onError?: (err: unknown, items: T[]) => void;
|
||||
};
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type FeishuDispatchReplyCounts = {
|
||||
final: number;
|
||||
block?: number;
|
||||
tool?: number;
|
||||
};
|
||||
type FeishuDispatchReplyContext = Record<string, unknown> & {
|
||||
SessionKey?: string;
|
||||
};
|
||||
type FeishuDispatchReplyDispatcher = {
|
||||
sendFinalReply: (payload: { text: string }) => unknown | Promise<unknown>;
|
||||
};
|
||||
type FeishuDispatchReplyMock = Mock<
|
||||
(args: {
|
||||
ctx: FeishuDispatchReplyContext;
|
||||
dispatcher: FeishuDispatchReplyDispatcher;
|
||||
}) => Promise<{ queuedFinal: boolean; counts: FeishuDispatchReplyCounts }>
|
||||
>;
|
||||
type FeishuLifecycleReplyDispatcher = {
|
||||
dispatcher: {
|
||||
sendToolResult: UnknownMock;
|
||||
sendBlockReply: UnknownMock;
|
||||
sendFinalReply: AsyncUnknownMock;
|
||||
waitForIdle: AsyncUnknownMock;
|
||||
getQueuedCounts: UnknownMock;
|
||||
markComplete: UnknownMock;
|
||||
};
|
||||
replyOptions: Record<string, never>;
|
||||
markDispatchIdle: UnknownMock;
|
||||
};
|
||||
|
||||
export function setFeishuLifecycleStateDir(prefix: string) {
|
||||
process.env.OPENCLAW_STATE_DIR = `/tmp/${prefix}-${randomUUID()}`;
|
||||
@@ -28,7 +59,7 @@ export const FEISHU_PREFETCHED_BOT_OPEN_ID_SOURCE = {
|
||||
botName: "Bot",
|
||||
} as const;
|
||||
|
||||
export function createFeishuLifecycleReplyDispatcher() {
|
||||
export function createFeishuLifecycleReplyDispatcher(): FeishuLifecycleReplyDispatcher {
|
||||
return {
|
||||
dispatcher: {
|
||||
sendToolResult: vi.fn(() => false),
|
||||
@@ -134,16 +165,7 @@ export function installFeishuLifecycleReplyRuntime(params: {
|
||||
}
|
||||
|
||||
export function mockFeishuReplyOnceDispatch(params: {
|
||||
dispatchReplyFromConfigMock: {
|
||||
mockImplementation: (
|
||||
fn: (args: {
|
||||
ctx?: unknown;
|
||||
dispatcher?: {
|
||||
sendFinalReply?: (payload: { text: string }) => Promise<unknown>;
|
||||
};
|
||||
}) => Promise<unknown>,
|
||||
) => void;
|
||||
};
|
||||
dispatchReplyFromConfigMock: FeishuDispatchReplyMock;
|
||||
replyText: string;
|
||||
shouldSendFinalReply?: (ctx: unknown) => boolean;
|
||||
}) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
|
||||
const hoisted = vi.hoisted((): { recordInboundSessionMock: AsyncUnknownMock } => ({
|
||||
recordInboundSessionMock: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
export const recordInboundSessionMock = hoisted.recordInboundSessionMock;
|
||||
export const recordInboundSessionMock: AsyncUnknownMock = hoisted.recordInboundSessionMock;
|
||||
|
||||
vi.mock("./bot-message-context.session.runtime.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./bot-message-context.session.runtime.js")>();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { expect, vi } from "vitest";
|
||||
import { expect, vi, type Mock } from "vitest";
|
||||
import type { OpenClawConfig } from "../runtime-api.js";
|
||||
import type { TelegramNativeCommandDeps } from "./bot-native-command-deps.runtime.js";
|
||||
import {
|
||||
@@ -12,6 +12,7 @@ type RegisteredCommand = {
|
||||
command: string;
|
||||
description: string;
|
||||
};
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
|
||||
type CreateCommandBotResult = {
|
||||
bot: RegisterTelegramNativeCommandsParams["bot"];
|
||||
@@ -36,7 +37,7 @@ const deliveryMocks = vi.hoisted(() => ({
|
||||
export const listSkillCommandsForAgents = skillCommandMocks.listSkillCommandsForAgents;
|
||||
export const deliverReplies = deliveryMocks.deliverReplies;
|
||||
export const editMessageTelegram = deliveryMocks.editMessageTelegram;
|
||||
export const emitTelegramMessageSentHooks = deliveryMocks.emitTelegramMessageSentHooks;
|
||||
export const emitTelegramMessageSentHooks: UnknownMock = deliveryMocks.emitTelegramMessageSentHooks;
|
||||
|
||||
vi.mock("./bot/delivery.js", () => ({
|
||||
deliverReplies,
|
||||
|
||||
@@ -73,6 +73,7 @@ type TelegramBotRuntime = {
|
||||
sequentialize: typeof sequentialize;
|
||||
apiThrottler: typeof apiThrottler;
|
||||
};
|
||||
type TelegramBotInstance = InstanceType<TelegramBotRuntime["Bot"]>;
|
||||
|
||||
const DEFAULT_TELEGRAM_BOT_RUNTIME: TelegramBotRuntime = {
|
||||
Bot,
|
||||
@@ -134,7 +135,7 @@ function extractTelegramApiMethod(input: TelegramFetchInput): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
export function createTelegramBot(opts: TelegramBotOptions) {
|
||||
export function createTelegramBot(opts: TelegramBotOptions): TelegramBotInstance {
|
||||
const botRuntime = telegramBotRuntimeForTest ?? DEFAULT_TELEGRAM_BOT_RUNTIME;
|
||||
const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime();
|
||||
const telegramDeps = opts.telegramDeps ?? defaultTelegramBotDeps;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/testing";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi, type Mock } from "vitest";
|
||||
|
||||
export const readConfigFileSnapshotForWrite = vi.fn();
|
||||
export const writeConfigFile = vi.fn();
|
||||
export const loadCronStore = vi.fn();
|
||||
export const resolveCronStorePath = vi.fn();
|
||||
export const saveCronStore = vi.fn();
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
|
||||
export const readConfigFileSnapshotForWrite: AsyncUnknownMock = vi.fn();
|
||||
export const writeConfigFile: AsyncUnknownMock = vi.fn();
|
||||
export const loadCronStore: AsyncUnknownMock = vi.fn();
|
||||
export const resolveCronStorePath: UnknownMock = vi.fn();
|
||||
export const saveCronStore: AsyncUnknownMock = vi.fn();
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
|
||||
|
||||
@@ -4,7 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { resetLogger, setLoggerOverride } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, vi, type Mock } from "vitest";
|
||||
import type { WebInboundMessage, WebListenerCloseReason } from "./inbound.js";
|
||||
import {
|
||||
resetBaileysMocks as _resetBaileysMocks,
|
||||
@@ -25,6 +25,18 @@ type MockWebListener = {
|
||||
sendReaction: () => Promise<void>;
|
||||
sendComposingTo: () => Promise<void>;
|
||||
};
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type WebAutoReplyRuntime = {
|
||||
log: UnknownMock;
|
||||
error: UnknownMock;
|
||||
exit: UnknownMock;
|
||||
};
|
||||
type WebAutoReplyMonitorHarness = {
|
||||
runtime: WebAutoReplyRuntime;
|
||||
controller: AbortController;
|
||||
run: Promise<unknown>;
|
||||
};
|
||||
|
||||
export const TEST_NET_IP = "203.0.113.10";
|
||||
|
||||
@@ -228,7 +240,7 @@ export function createWebInboundDeliverySpies(): AnyExport {
|
||||
};
|
||||
}
|
||||
|
||||
export function createWebAutoReplyRuntime() {
|
||||
export function createWebAutoReplyRuntime(): WebAutoReplyRuntime {
|
||||
return {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
@@ -239,13 +251,13 @@ export function createWebAutoReplyRuntime() {
|
||||
export function startWebAutoReplyMonitor(params: {
|
||||
monitorWebChannelFn: (...args: unknown[]) => Promise<unknown>;
|
||||
listenerFactory: unknown;
|
||||
sleep: ReturnType<typeof vi.fn>;
|
||||
sleep: UnknownMock | AsyncUnknownMock;
|
||||
signal?: AbortSignal;
|
||||
heartbeatSeconds?: number;
|
||||
messageTimeoutMs?: number;
|
||||
watchdogCheckMs?: number;
|
||||
reconnect?: { initialMs: number; maxMs: number; maxAttempts: number; factor: number };
|
||||
}) {
|
||||
}): WebAutoReplyMonitorHarness {
|
||||
const runtime = createWebAutoReplyRuntime();
|
||||
const controller = new AbortController();
|
||||
const run = params.monitorWebChannelFn(
|
||||
|
||||
@@ -29,7 +29,7 @@ export const DEFAULT_WEB_INBOX_CONFIG = {
|
||||
responsePrefix: undefined,
|
||||
},
|
||||
} as const;
|
||||
export const mockLoadConfig = loadConfigMock;
|
||||
export const mockLoadConfig: typeof loadConfigMock = loadConfigMock;
|
||||
export const readAllowFromStoreMock = pairingReadAllowFromStoreMock;
|
||||
export const upsertPairingRequestMock = pairingUpsertPairingRequestMock;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
resolveOpenProviderRuntimeGroupPolicy,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "openclaw/plugin-sdk/runtime-group-policy";
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
|
||||
export type AsyncMock<TArgs extends unknown[] = unknown[], TResult = unknown> = {
|
||||
(...args: TArgs): Promise<TResult>;
|
||||
@@ -11,8 +11,9 @@ export type AsyncMock<TArgs extends unknown[] = unknown[], TResult = unknown> =
|
||||
mockResolvedValue: (value: TResult) => AsyncMock<TArgs, TResult>;
|
||||
mockResolvedValueOnce: (value: TResult) => AsyncMock<TArgs, TResult>;
|
||||
};
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
|
||||
export const loadConfigMock = vi.fn();
|
||||
export const loadConfigMock: UnknownMock = vi.fn();
|
||||
export const readAllowFromStoreMock = vi.fn() as AsyncMock;
|
||||
export const upsertPairingRequestMock = vi.fn() as AsyncMock;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/zalo";
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
import {
|
||||
createEmptyPluginRegistry,
|
||||
setActivePluginRegistry,
|
||||
@@ -16,19 +16,34 @@ const secretInputModuleUrl = new URL("../src/secret-input.ts", import.meta.url).
|
||||
const apiModuleId = new URL("../src/api.js", import.meta.url).pathname;
|
||||
const runtimeModuleId = new URL("../src/runtime.js", import.meta.url).pathname;
|
||||
|
||||
const lifecycleMocks = vi.hoisted(() => ({
|
||||
setWebhookMock: vi.fn(async () => ({ ok: true, result: { url: "" } })),
|
||||
deleteWebhookMock: vi.fn(async () => ({ ok: true, result: { url: "" } })),
|
||||
getWebhookInfoMock: vi.fn(async () => ({ ok: true, result: { url: "" } })),
|
||||
getUpdatesMock: vi.fn(() => new Promise(() => {})),
|
||||
sendChatActionMock: vi.fn(async () => ({ ok: true })),
|
||||
sendMessageMock: vi.fn(async () => ({
|
||||
ok: true,
|
||||
result: { message_id: "zalo-test-reply-1" },
|
||||
})),
|
||||
sendPhotoMock: vi.fn(async () => ({ ok: true })),
|
||||
getZaloRuntimeMock: vi.fn(),
|
||||
}));
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type ZaloLifecycleMocks = {
|
||||
setWebhookMock: AsyncUnknownMock;
|
||||
deleteWebhookMock: AsyncUnknownMock;
|
||||
getWebhookInfoMock: AsyncUnknownMock;
|
||||
getUpdatesMock: UnknownMock;
|
||||
sendChatActionMock: AsyncUnknownMock;
|
||||
sendMessageMock: AsyncUnknownMock;
|
||||
sendPhotoMock: AsyncUnknownMock;
|
||||
getZaloRuntimeMock: UnknownMock;
|
||||
};
|
||||
|
||||
const lifecycleMocks = vi.hoisted(
|
||||
(): ZaloLifecycleMocks => ({
|
||||
setWebhookMock: vi.fn(async () => ({ ok: true, result: { url: "" } })),
|
||||
deleteWebhookMock: vi.fn(async () => ({ ok: true, result: { url: "" } })),
|
||||
getWebhookInfoMock: vi.fn(async () => ({ ok: true, result: { url: "" } })),
|
||||
getUpdatesMock: vi.fn(() => new Promise(() => {})),
|
||||
sendChatActionMock: vi.fn(async () => ({ ok: true })),
|
||||
sendMessageMock: vi.fn(async () => ({
|
||||
ok: true,
|
||||
result: { message_id: "zalo-test-reply-1" },
|
||||
})),
|
||||
sendPhotoMock: vi.fn(async () => ({ ok: true })),
|
||||
getZaloRuntimeMock: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
export const setWebhookMock = lifecycleMocks.setWebhookMock;
|
||||
export const deleteWebhookMock = lifecycleMocks.deleteWebhookMock;
|
||||
@@ -37,7 +52,7 @@ export const getUpdatesMock = lifecycleMocks.getUpdatesMock;
|
||||
export const sendChatActionMock = lifecycleMocks.sendChatActionMock;
|
||||
export const sendMessageMock = lifecycleMocks.sendMessageMock;
|
||||
export const sendPhotoMock = lifecycleMocks.sendPhotoMock;
|
||||
export const getZaloRuntimeMock = lifecycleMocks.getZaloRuntimeMock;
|
||||
export const getZaloRuntimeMock: UnknownMock = lifecycleMocks.getZaloRuntimeMock;
|
||||
|
||||
function installLifecycleModuleMocks() {
|
||||
vi.doMock(apiModuleId, async (importOriginal) => {
|
||||
|
||||
@@ -1,31 +1,59 @@
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
|
||||
const zaloJsMocks = vi.hoisted(() => ({
|
||||
checkZaloAuthenticatedMock: vi.fn(async () => false),
|
||||
getZaloUserInfoMock: vi.fn(async () => null),
|
||||
listZaloFriendsMock: vi.fn(async () => []),
|
||||
listZaloFriendsMatchingMock: vi.fn(async () => []),
|
||||
listZaloGroupMembersMock: vi.fn(async () => []),
|
||||
listZaloGroupsMock: vi.fn(async () => []),
|
||||
listZaloGroupsMatchingMock: vi.fn(async () => []),
|
||||
logoutZaloProfileMock: vi.fn(async () => {}),
|
||||
resolveZaloAllowFromEntriesMock: vi.fn(async ({ entries }: { entries: string[] }) =>
|
||||
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
|
||||
),
|
||||
resolveZaloGroupContextMock: vi.fn(async () => null),
|
||||
resolveZaloGroupsByEntriesMock: vi.fn(async ({ entries }: { entries: string[] }) =>
|
||||
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
|
||||
),
|
||||
startZaloListenerMock: vi.fn(async () => ({ stop: vi.fn() })),
|
||||
startZaloQrLoginMock: vi.fn(async () => ({
|
||||
message: "qr pending",
|
||||
qrDataUrl: undefined,
|
||||
})),
|
||||
waitForZaloQrLoginMock: vi.fn(async () => ({
|
||||
connected: false,
|
||||
message: "login pending",
|
||||
})),
|
||||
}));
|
||||
type ZaloJsModule = typeof import("./zalo-js.js");
|
||||
type ZaloJsMocks = {
|
||||
checkZaloAuthenticatedMock: Mock<ZaloJsModule["checkZaloAuthenticated"]>;
|
||||
getZaloUserInfoMock: Mock<ZaloJsModule["getZaloUserInfo"]>;
|
||||
listZaloFriendsMock: Mock<ZaloJsModule["listZaloFriends"]>;
|
||||
listZaloFriendsMatchingMock: Mock<ZaloJsModule["listZaloFriendsMatching"]>;
|
||||
listZaloGroupMembersMock: Mock<ZaloJsModule["listZaloGroupMembers"]>;
|
||||
listZaloGroupsMock: Mock<ZaloJsModule["listZaloGroups"]>;
|
||||
listZaloGroupsMatchingMock: Mock<ZaloJsModule["listZaloGroupsMatching"]>;
|
||||
logoutZaloProfileMock: Mock<ZaloJsModule["logoutZaloProfile"]>;
|
||||
resolveZaloAllowFromEntriesMock: Mock<ZaloJsModule["resolveZaloAllowFromEntries"]>;
|
||||
resolveZaloGroupContextMock: Mock<ZaloJsModule["resolveZaloGroupContext"]>;
|
||||
resolveZaloGroupsByEntriesMock: Mock<ZaloJsModule["resolveZaloGroupsByEntries"]>;
|
||||
startZaloListenerMock: Mock<ZaloJsModule["startZaloListener"]>;
|
||||
startZaloQrLoginMock: Mock<ZaloJsModule["startZaloQrLogin"]>;
|
||||
waitForZaloQrLoginMock: Mock<ZaloJsModule["waitForZaloQrLogin"]>;
|
||||
};
|
||||
|
||||
const zaloJsMocks = vi.hoisted(
|
||||
(): ZaloJsMocks => ({
|
||||
checkZaloAuthenticatedMock: vi.fn(async () => false),
|
||||
getZaloUserInfoMock: vi.fn(async () => null),
|
||||
listZaloFriendsMock: vi.fn(async () => []),
|
||||
listZaloFriendsMatchingMock: vi.fn(async () => []),
|
||||
listZaloGroupMembersMock: vi.fn(async () => []),
|
||||
listZaloGroupsMock: vi.fn(async () => []),
|
||||
listZaloGroupsMatchingMock: vi.fn(async () => []),
|
||||
logoutZaloProfileMock: vi.fn(async () => ({
|
||||
cleared: true,
|
||||
loggedOut: true,
|
||||
message: "Logged out and cleared local session.",
|
||||
})),
|
||||
resolveZaloAllowFromEntriesMock: vi.fn(async ({ entries }: { entries: string[] }) =>
|
||||
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
|
||||
),
|
||||
resolveZaloGroupContextMock: vi.fn(async (_profile, groupId) => ({
|
||||
groupId,
|
||||
name: undefined,
|
||||
members: [],
|
||||
})),
|
||||
resolveZaloGroupsByEntriesMock: vi.fn(async ({ entries }: { entries: string[] }) =>
|
||||
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
|
||||
),
|
||||
startZaloListenerMock: vi.fn(async () => ({ stop: vi.fn() })),
|
||||
startZaloQrLoginMock: vi.fn(async () => ({
|
||||
message: "qr pending",
|
||||
qrDataUrl: undefined,
|
||||
})),
|
||||
waitForZaloQrLoginMock: vi.fn(async () => ({
|
||||
connected: false,
|
||||
message: "login pending",
|
||||
})),
|
||||
}),
|
||||
);
|
||||
|
||||
export const checkZaloAuthenticatedMock = zaloJsMocks.checkZaloAuthenticatedMock;
|
||||
export const getZaloUserInfoMock = zaloJsMocks.getZaloUserInfoMock;
|
||||
@@ -38,7 +66,8 @@ export const logoutZaloProfileMock = zaloJsMocks.logoutZaloProfileMock;
|
||||
export const resolveZaloAllowFromEntriesMock = zaloJsMocks.resolveZaloAllowFromEntriesMock;
|
||||
export const resolveZaloGroupContextMock = zaloJsMocks.resolveZaloGroupContextMock;
|
||||
export const resolveZaloGroupsByEntriesMock = zaloJsMocks.resolveZaloGroupsByEntriesMock;
|
||||
export const startZaloListenerMock = zaloJsMocks.startZaloListenerMock;
|
||||
export const startZaloListenerMock: Mock<ZaloJsModule["startZaloListener"]> =
|
||||
zaloJsMocks.startZaloListenerMock;
|
||||
export const startZaloQrLoginMock = zaloJsMocks.startZaloQrLoginMock;
|
||||
export const waitForZaloQrLoginMock = zaloJsMocks.waitForZaloQrLoginMock;
|
||||
|
||||
|
||||
@@ -1,46 +1,65 @@
|
||||
import fs from "node:fs/promises";
|
||||
import type { Mock } from "vitest";
|
||||
import { beforeEach, vi } from "vitest";
|
||||
import { buildAnthropicCliBackend } from "../../extensions/anthropic/test-api.js";
|
||||
import { buildGoogleGeminiCliBackend } from "../../extensions/google/test-api.js";
|
||||
import { buildOpenAICodexCliBackend } from "../../extensions/openai/test-api.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { requestHeartbeatNow } from "../infra/heartbeat-wake.js";
|
||||
import type { enqueueSystemEvent } from "../infra/system-events.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import type { getProcessSupervisor } from "../process/supervisor/index.js";
|
||||
import { setCliRunnerExecuteTestDeps } from "./cli-runner/execute.js";
|
||||
import { setCliRunnerPrepareTestDeps } from "./cli-runner/prepare.js";
|
||||
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
|
||||
import type { WorkspaceBootstrapFile } from "./workspace.js";
|
||||
|
||||
export const supervisorSpawnMock = vi.fn();
|
||||
export const enqueueSystemEventMock = vi.fn();
|
||||
export const requestHeartbeatNowMock = vi.fn();
|
||||
type ProcessSupervisor = ReturnType<typeof getProcessSupervisor>;
|
||||
type SupervisorSpawnFn = ProcessSupervisor["spawn"];
|
||||
type EnqueueSystemEventFn = typeof enqueueSystemEvent;
|
||||
type RequestHeartbeatNowFn = typeof requestHeartbeatNow;
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type BootstrapContext = {
|
||||
bootstrapFiles: WorkspaceBootstrapFile[];
|
||||
contextFiles: EmbeddedContextFile[];
|
||||
};
|
||||
type ResolveBootstrapContextForRunMock = Mock<() => Promise<BootstrapContext>>;
|
||||
|
||||
export const supervisorSpawnMock: UnknownMock = vi.fn();
|
||||
export const enqueueSystemEventMock: UnknownMock = vi.fn();
|
||||
export const requestHeartbeatNowMock: UnknownMock = vi.fn();
|
||||
export const SMALL_PNG_BASE64 =
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/woAAn8B9FD5fHAAAAAASUVORK5CYII=";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
type BootstrapContext = {
|
||||
bootstrapFiles: WorkspaceBootstrapFile[];
|
||||
contextFiles: EmbeddedContextFile[];
|
||||
};
|
||||
|
||||
return {
|
||||
resolveBootstrapContextForRunMock: vi.fn<() => Promise<BootstrapContext>>(async () => ({
|
||||
bootstrapFiles: [],
|
||||
contextFiles: [],
|
||||
})),
|
||||
};
|
||||
});
|
||||
const hoisted = vi.hoisted(
|
||||
(): {
|
||||
resolveBootstrapContextForRunMock: ResolveBootstrapContextForRunMock;
|
||||
} => {
|
||||
return {
|
||||
resolveBootstrapContextForRunMock: vi.fn<() => Promise<BootstrapContext>>(async () => ({
|
||||
bootstrapFiles: [],
|
||||
contextFiles: [],
|
||||
})),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
setCliRunnerExecuteTestDeps({
|
||||
getProcessSupervisor: () => ({
|
||||
spawn: (...args: unknown[]) => supervisorSpawnMock(...args),
|
||||
spawn: (params: Parameters<SupervisorSpawnFn>[0]) =>
|
||||
supervisorSpawnMock(params) as ReturnType<SupervisorSpawnFn>,
|
||||
cancel: vi.fn(),
|
||||
cancelScope: vi.fn(),
|
||||
reconcileOrphans: vi.fn(),
|
||||
getRecord: vi.fn(),
|
||||
}),
|
||||
enqueueSystemEvent: (...args: unknown[]) => enqueueSystemEventMock(...args),
|
||||
requestHeartbeatNow: (...args: unknown[]) => requestHeartbeatNowMock(...args),
|
||||
enqueueSystemEvent: (
|
||||
text: Parameters<EnqueueSystemEventFn>[0],
|
||||
options: Parameters<EnqueueSystemEventFn>[1],
|
||||
) => enqueueSystemEventMock(text, options) as ReturnType<EnqueueSystemEventFn>,
|
||||
requestHeartbeatNow: (options?: Parameters<RequestHeartbeatNowFn>[0]) =>
|
||||
requestHeartbeatNowMock(options) as ReturnType<RequestHeartbeatNowFn>,
|
||||
});
|
||||
|
||||
setCliRunnerPrepareTestDeps({
|
||||
@@ -71,7 +90,19 @@ type TestCliBackendConfig = {
|
||||
clearEnv?: string[];
|
||||
};
|
||||
|
||||
export function createManagedRun(exit: MockRunExit, pid = 1234) {
|
||||
type ManagedRunMock = {
|
||||
runId: string;
|
||||
pid: number;
|
||||
startedAtMs: number;
|
||||
stdin: undefined;
|
||||
wait: Mock<() => Promise<MockRunExit>>;
|
||||
cancel: Mock<() => void>;
|
||||
};
|
||||
|
||||
export function createManagedRun(
|
||||
exit: MockRunExit,
|
||||
pid = 1234,
|
||||
): ManagedRunMock & Awaited<ReturnType<SupervisorSpawnFn>> {
|
||||
return {
|
||||
runId: "run-supervisor",
|
||||
pid,
|
||||
|
||||
@@ -3,7 +3,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { Api, Model } from "@mariozechner/pi-ai";
|
||||
import { expect, vi } from "vitest";
|
||||
import { expect, vi, type Mock } from "vitest";
|
||||
import type {
|
||||
AssembleResult,
|
||||
BootstrapResult,
|
||||
@@ -22,12 +22,37 @@ type AcquireSessionWriteLockFn =
|
||||
typeof import("../../session-write-lock.js").acquireSessionWriteLock;
|
||||
|
||||
type SubscriptionMock = ReturnType<SubscribeEmbeddedPiSessionFn>;
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type BootstrapContext = {
|
||||
bootstrapFiles: WorkspaceBootstrapFile[];
|
||||
contextFiles: EmbeddedContextFile[];
|
||||
};
|
||||
type SessionManagerMocks = {
|
||||
getLeafEntry: UnknownMock;
|
||||
branch: UnknownMock;
|
||||
resetLeaf: UnknownMock;
|
||||
buildSessionContext: Mock<() => { messages: AgentMessage[] }>;
|
||||
appendCustomEntry: UnknownMock;
|
||||
};
|
||||
type AttemptSpawnWorkspaceHoisted = {
|
||||
spawnSubagentDirectMock: UnknownMock;
|
||||
createAgentSessionMock: UnknownMock;
|
||||
sessionManagerOpenMock: UnknownMock;
|
||||
resolveSandboxContextMock: UnknownMock;
|
||||
subscribeEmbeddedPiSessionMock: Mock<SubscribeEmbeddedPiSessionFn>;
|
||||
acquireSessionWriteLockMock: Mock<AcquireSessionWriteLockFn>;
|
||||
installToolResultContextGuardMock: UnknownMock;
|
||||
flushPendingToolResultsAfterIdleMock: AsyncUnknownMock;
|
||||
releaseWsSessionMock: UnknownMock;
|
||||
resolveBootstrapContextForRunMock: Mock<() => Promise<BootstrapContext>>;
|
||||
getGlobalHookRunnerMock: Mock<() => unknown>;
|
||||
initializeGlobalHookRunnerMock: UnknownMock;
|
||||
runContextEngineMaintenanceMock: AsyncUnknownMock;
|
||||
sessionManager: SessionManagerMocks;
|
||||
};
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
type BootstrapContext = {
|
||||
bootstrapFiles: WorkspaceBootstrapFile[];
|
||||
contextFiles: EmbeddedContextFile[];
|
||||
};
|
||||
const hoisted = vi.hoisted((): AttemptSpawnWorkspaceHoisted => {
|
||||
const spawnSubagentDirectMock = vi.fn();
|
||||
const createAgentSessionMock = vi.fn();
|
||||
const sessionManagerOpenMock = vi.fn();
|
||||
@@ -90,7 +115,7 @@ const hoisted = vi.hoisted(() => {
|
||||
};
|
||||
});
|
||||
|
||||
export function getHoisted() {
|
||||
export function getHoisted(): AttemptSpawnWorkspaceHoisted {
|
||||
return hoisted;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ export function streamWithPayloadPatch(
|
||||
context: Parameters<StreamFn>[1],
|
||||
options: Parameters<StreamFn>[2],
|
||||
patchPayload: (payload: Record<string, unknown>) => void,
|
||||
) {
|
||||
): ReturnType<StreamFn> {
|
||||
const originalOnPayload = options?.onPayload;
|
||||
return underlying(model, context, {
|
||||
...options,
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeEach, expect, vi } from "vitest";
|
||||
import { beforeEach, expect, vi, type Mock } from "vitest";
|
||||
|
||||
let actualOpenBoundaryFile:
|
||||
| ((
|
||||
...args: Parameters<typeof import("../../infra/boundary-file-read.js").openBoundaryFile>
|
||||
) => ReturnType<typeof import("../../infra/boundary-file-read.js").openBoundaryFile>)
|
||||
| undefined;
|
||||
type ExecDockerRawFn = typeof import("./docker.js").execDockerRaw;
|
||||
type OpenBoundaryFileFn = typeof import("../../infra/boundary-file-read.js").openBoundaryFile;
|
||||
type ExecDockerArgs = Parameters<ExecDockerRawFn>[0];
|
||||
type ExecDockerRawMock = Mock<ExecDockerRawFn>;
|
||||
type OpenBoundaryFileMock = Mock<OpenBoundaryFileFn>;
|
||||
type FsBridgeHoisted = {
|
||||
execDockerRaw: ExecDockerRawMock;
|
||||
openBoundaryFile: OpenBoundaryFileMock;
|
||||
};
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
execDockerRaw: vi.fn(),
|
||||
openBoundaryFile: vi.fn(),
|
||||
}));
|
||||
let actualOpenBoundaryFile: OpenBoundaryFileFn | undefined;
|
||||
|
||||
const hoisted = vi.hoisted(
|
||||
(): FsBridgeHoisted => ({
|
||||
execDockerRaw: vi.fn(),
|
||||
openBoundaryFile: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock("./docker.js", () => ({
|
||||
execDockerRaw: (...args: unknown[]) => hoisted.execDockerRaw(...args),
|
||||
execDockerRaw: (args: ExecDockerArgs, opts?: Parameters<ExecDockerRawFn>[1]) =>
|
||||
hoisted.execDockerRaw(args, opts),
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/boundary-file-read.js", async (importOriginal) => {
|
||||
@@ -23,7 +32,8 @@ vi.mock("../../infra/boundary-file-read.js", async (importOriginal) => {
|
||||
actualOpenBoundaryFile = actual.openBoundaryFile;
|
||||
return {
|
||||
...actual,
|
||||
openBoundaryFile: (...args: unknown[]) => hoisted.openBoundaryFile(...args),
|
||||
openBoundaryFile: (params: Parameters<OpenBoundaryFileFn>[0]) =>
|
||||
hoisted.openBoundaryFile(params),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -35,14 +45,16 @@ let createSandboxFsBridgeImpl: typeof import("./fs-bridge.js").createSandboxFsBr
|
||||
async function loadFreshFsBridgeModuleForTest() {
|
||||
vi.resetModules();
|
||||
vi.doMock("./docker.js", () => ({
|
||||
execDockerRaw: (...args: unknown[]) => hoisted.execDockerRaw(...args),
|
||||
execDockerRaw: (args: ExecDockerArgs, opts?: Parameters<ExecDockerRawFn>[1]) =>
|
||||
hoisted.execDockerRaw(args, opts),
|
||||
}));
|
||||
vi.doMock("../../infra/boundary-file-read.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../infra/boundary-file-read.js")>();
|
||||
actualOpenBoundaryFile = actual.openBoundaryFile;
|
||||
return {
|
||||
...actual,
|
||||
openBoundaryFile: (...args: unknown[]) => hoisted.openBoundaryFile(...args),
|
||||
openBoundaryFile: (params: Parameters<OpenBoundaryFileFn>[0]) =>
|
||||
hoisted.openBoundaryFile(params),
|
||||
};
|
||||
});
|
||||
({ createSandboxFsBridge: createSandboxFsBridgeImpl } = await import("./fs-bridge.js"));
|
||||
@@ -57,8 +69,8 @@ export function createSandboxFsBridge(
|
||||
return createSandboxFsBridgeImpl(...args);
|
||||
}
|
||||
|
||||
export const mockedExecDockerRaw = hoisted.execDockerRaw;
|
||||
export const mockedOpenBoundaryFile = hoisted.openBoundaryFile;
|
||||
export const mockedExecDockerRaw: ExecDockerRawMock = hoisted.execDockerRaw;
|
||||
export const mockedOpenBoundaryFile: OpenBoundaryFileMock = hoisted.openBoundaryFile;
|
||||
const DOCKER_SCRIPT_INDEX = 5;
|
||||
const DOCKER_FIRST_SCRIPT_ARG_INDEX = 7;
|
||||
|
||||
|
||||
@@ -1,38 +1,52 @@
|
||||
import { Command } from "commander";
|
||||
import type { Mock } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createCliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
export const loadConfig = vi.fn<() => OpenClawConfig>(() => ({}) as OpenClawConfig);
|
||||
export const readConfigFileSnapshot = vi.fn();
|
||||
export const writeConfigFile = vi.fn<(config: OpenClawConfig) => Promise<void>>(
|
||||
async () => undefined,
|
||||
);
|
||||
export const replaceConfigFile = vi.fn(
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type LoadConfigFn = (typeof import("../config/config.js"))["loadConfig"];
|
||||
type ParseClawHubPluginSpecFn = (typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"];
|
||||
type InstallPluginFromMarketplaceFn =
|
||||
(typeof import("../plugins/marketplace.js"))["installPluginFromMarketplace"];
|
||||
type ListMarketplacePluginsFn =
|
||||
(typeof import("../plugins/marketplace.js"))["listMarketplacePlugins"];
|
||||
type ResolveMarketplaceInstallShortcutFn =
|
||||
(typeof import("../plugins/marketplace.js"))["resolveMarketplaceInstallShortcut"];
|
||||
|
||||
function invokeMock<TArgs extends unknown[], TResult>(mock: unknown, ...args: TArgs): TResult {
|
||||
return (mock as (...args: TArgs) => TResult)(...args);
|
||||
}
|
||||
|
||||
export const loadConfig: Mock<LoadConfigFn> = vi.fn<LoadConfigFn>(() => ({}) as OpenClawConfig);
|
||||
export const readConfigFileSnapshot: AsyncUnknownMock = vi.fn();
|
||||
export const writeConfigFile: AsyncUnknownMock = vi.fn(async () => undefined);
|
||||
export const replaceConfigFile: AsyncUnknownMock = vi.fn(
|
||||
async (params: { nextConfig: OpenClawConfig }) => await writeConfigFile(params.nextConfig),
|
||||
);
|
||||
export const resolveStateDir = vi.fn(() => "/tmp/openclaw-state");
|
||||
export const installPluginFromMarketplace = vi.fn();
|
||||
export const listMarketplacePlugins = vi.fn();
|
||||
export const resolveMarketplaceInstallShortcut = vi.fn();
|
||||
export const enablePluginInConfig = vi.fn();
|
||||
export const recordPluginInstall = vi.fn();
|
||||
export const clearPluginManifestRegistryCache = vi.fn();
|
||||
export const buildPluginSnapshotReport = vi.fn();
|
||||
export const buildPluginDiagnosticsReport = vi.fn();
|
||||
export const buildPluginCompatibilityNotices = vi.fn();
|
||||
export const applyExclusiveSlotSelection = vi.fn();
|
||||
export const uninstallPlugin = vi.fn();
|
||||
export const updateNpmInstalledPlugins = vi.fn();
|
||||
export const updateNpmInstalledHookPacks = vi.fn();
|
||||
export const promptYesNo = vi.fn();
|
||||
export const installPluginFromNpmSpec = vi.fn();
|
||||
export const installPluginFromPath = vi.fn();
|
||||
export const installPluginFromClawHub = vi.fn();
|
||||
export const parseClawHubPluginSpec = vi.fn();
|
||||
export const installHooksFromNpmSpec = vi.fn();
|
||||
export const installHooksFromPath = vi.fn();
|
||||
export const recordHookInstall = vi.fn();
|
||||
) as AsyncUnknownMock;
|
||||
export const resolveStateDir: Mock<() => string> = vi.fn(() => "/tmp/openclaw-state");
|
||||
export const installPluginFromMarketplace: Mock<InstallPluginFromMarketplaceFn> = vi.fn();
|
||||
export const listMarketplacePlugins: Mock<ListMarketplacePluginsFn> = vi.fn();
|
||||
export const resolveMarketplaceInstallShortcut: Mock<ResolveMarketplaceInstallShortcutFn> = vi.fn();
|
||||
export const enablePluginInConfig: UnknownMock = vi.fn();
|
||||
export const recordPluginInstall: UnknownMock = vi.fn();
|
||||
export const clearPluginManifestRegistryCache: UnknownMock = vi.fn();
|
||||
export const buildPluginSnapshotReport: UnknownMock = vi.fn();
|
||||
export const buildPluginDiagnosticsReport: UnknownMock = vi.fn();
|
||||
export const buildPluginCompatibilityNotices: UnknownMock = vi.fn();
|
||||
export const applyExclusiveSlotSelection: UnknownMock = vi.fn();
|
||||
export const uninstallPlugin: AsyncUnknownMock = vi.fn();
|
||||
export const updateNpmInstalledPlugins: AsyncUnknownMock = vi.fn();
|
||||
export const updateNpmInstalledHookPacks: AsyncUnknownMock = vi.fn();
|
||||
export const promptYesNo: AsyncUnknownMock = vi.fn();
|
||||
export const installPluginFromNpmSpec: AsyncUnknownMock = vi.fn();
|
||||
export const installPluginFromPath: AsyncUnknownMock = vi.fn();
|
||||
export const installPluginFromClawHub: AsyncUnknownMock = vi.fn();
|
||||
export const parseClawHubPluginSpec: Mock<ParseClawHubPluginSpecFn> = vi.fn();
|
||||
export const installHooksFromNpmSpec: AsyncUnknownMock = vi.fn();
|
||||
export const installHooksFromPath: AsyncUnknownMock = vi.fn();
|
||||
export const recordHookInstall: UnknownMock = vi.fn();
|
||||
|
||||
const { defaultRuntime, runtimeLogs, runtimeErrors, resetRuntimeCapture } =
|
||||
createCliRuntimeCapture();
|
||||
@@ -45,9 +59,28 @@ vi.mock("../runtime.js", () => ({
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
loadConfig: () => loadConfig(),
|
||||
readConfigFileSnapshot: (...args: unknown[]) => readConfigFileSnapshot(...args),
|
||||
writeConfigFile: (config: OpenClawConfig) => writeConfigFile(config),
|
||||
replaceConfigFile: (params: { nextConfig: OpenClawConfig }) => replaceConfigFile(params),
|
||||
readConfigFileSnapshot: ((
|
||||
...args: Parameters<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>,
|
||||
ReturnType<(typeof import("../config/config.js"))["readConfigFileSnapshot"]>
|
||||
>(
|
||||
readConfigFileSnapshot,
|
||||
...args,
|
||||
)) as (typeof import("../config/config.js"))["readConfigFileSnapshot"],
|
||||
writeConfigFile: ((config: OpenClawConfig) =>
|
||||
invokeMock<
|
||||
[OpenClawConfig],
|
||||
ReturnType<(typeof import("../config/config.js"))["writeConfigFile"]>
|
||||
>(writeConfigFile, config)) as (typeof import("../config/config.js"))["writeConfigFile"],
|
||||
replaceConfigFile: ((
|
||||
params: Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0],
|
||||
) =>
|
||||
invokeMock<
|
||||
[Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0]],
|
||||
ReturnType<(typeof import("../config/config.js"))["replaceConfigFile"]>
|
||||
>(replaceConfigFile, params)) as (typeof import("../config/config.js"))["replaceConfigFile"],
|
||||
}));
|
||||
|
||||
vi.mock("../config/paths.js", () => ({
|
||||
@@ -55,18 +88,34 @@ vi.mock("../config/paths.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/marketplace.js", () => ({
|
||||
installPluginFromMarketplace: (...args: unknown[]) => installPluginFromMarketplace(...args),
|
||||
listMarketplacePlugins: (...args: unknown[]) => listMarketplacePlugins(...args),
|
||||
resolveMarketplaceInstallShortcut: (...args: unknown[]) =>
|
||||
resolveMarketplaceInstallShortcut(...args),
|
||||
installPluginFromMarketplace: ((...args: Parameters<InstallPluginFromMarketplaceFn>) =>
|
||||
installPluginFromMarketplace(...args)) as InstallPluginFromMarketplaceFn,
|
||||
listMarketplacePlugins: ((...args: Parameters<ListMarketplacePluginsFn>) =>
|
||||
listMarketplacePlugins(...args)) as ListMarketplacePluginsFn,
|
||||
resolveMarketplaceInstallShortcut: ((...args: Parameters<ResolveMarketplaceInstallShortcutFn>) =>
|
||||
resolveMarketplaceInstallShortcut(...args)) as ResolveMarketplaceInstallShortcutFn,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/enable.js", () => ({
|
||||
enablePluginInConfig: (...args: unknown[]) => enablePluginInConfig(...args),
|
||||
enablePluginInConfig: ((cfg: OpenClawConfig, pluginId: string) =>
|
||||
invokeMock<[OpenClawConfig, string], unknown>(
|
||||
enablePluginInConfig,
|
||||
cfg,
|
||||
pluginId,
|
||||
)) as (typeof import("../plugins/enable.js"))["enablePluginInConfig"],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/installs.js", () => ({
|
||||
recordPluginInstall: (...args: unknown[]) => recordPluginInstall(...args),
|
||||
recordPluginInstall: ((
|
||||
...args: Parameters<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>,
|
||||
ReturnType<(typeof import("../plugins/installs.js"))["recordPluginInstall"]>
|
||||
>(
|
||||
recordPluginInstall,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/installs.js"))["recordPluginInstall"],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/manifest-registry.js", () => ({
|
||||
@@ -74,17 +123,59 @@ vi.mock("../plugins/manifest-registry.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/status.js", () => ({
|
||||
buildPluginSnapshotReport: (...args: unknown[]) => buildPluginSnapshotReport(...args),
|
||||
buildPluginDiagnosticsReport: (...args: unknown[]) => buildPluginDiagnosticsReport(...args),
|
||||
buildPluginCompatibilityNotices: (...args: unknown[]) => buildPluginCompatibilityNotices(...args),
|
||||
buildPluginSnapshotReport: ((
|
||||
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>,
|
||||
ReturnType<(typeof import("../plugins/status.js"))["buildPluginSnapshotReport"]>
|
||||
>(
|
||||
buildPluginSnapshotReport,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/status.js"))["buildPluginSnapshotReport"],
|
||||
buildPluginDiagnosticsReport: ((
|
||||
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>,
|
||||
ReturnType<(typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"]>
|
||||
>(
|
||||
buildPluginDiagnosticsReport,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/status.js"))["buildPluginDiagnosticsReport"],
|
||||
buildPluginCompatibilityNotices: ((
|
||||
...args: Parameters<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>,
|
||||
ReturnType<(typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"]>
|
||||
>(
|
||||
buildPluginCompatibilityNotices,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/status.js"))["buildPluginCompatibilityNotices"],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/slots.js", () => ({
|
||||
applyExclusiveSlotSelection: (...args: unknown[]) => applyExclusiveSlotSelection(...args),
|
||||
applyExclusiveSlotSelection: ((
|
||||
params: Parameters<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>[0],
|
||||
) =>
|
||||
invokeMock<
|
||||
[Parameters<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>[0]],
|
||||
ReturnType<(typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"]>
|
||||
>(
|
||||
applyExclusiveSlotSelection,
|
||||
params,
|
||||
)) as (typeof import("../plugins/slots.js"))["applyExclusiveSlotSelection"],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/uninstall.js", () => ({
|
||||
uninstallPlugin: (...args: unknown[]) => uninstallPlugin(...args),
|
||||
uninstallPlugin: ((
|
||||
...args: Parameters<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>,
|
||||
ReturnType<(typeof import("../plugins/uninstall.js"))["uninstallPlugin"]>
|
||||
>(uninstallPlugin, ...args)) as (typeof import("../plugins/uninstall.js"))["uninstallPlugin"],
|
||||
resolveUninstallDirectoryTarget: ({
|
||||
installRecord,
|
||||
}: {
|
||||
@@ -93,33 +184,97 @@ vi.mock("../plugins/uninstall.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/update.js", () => ({
|
||||
updateNpmInstalledPlugins: (...args: unknown[]) => updateNpmInstalledPlugins(...args),
|
||||
updateNpmInstalledPlugins: ((
|
||||
...args: Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>,
|
||||
ReturnType<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>
|
||||
>(
|
||||
updateNpmInstalledPlugins,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"],
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/update.js", () => ({
|
||||
updateNpmInstalledHookPacks: (...args: unknown[]) => updateNpmInstalledHookPacks(...args),
|
||||
updateNpmInstalledHookPacks: ((
|
||||
...args: Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>,
|
||||
ReturnType<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>
|
||||
>(
|
||||
updateNpmInstalledHookPacks,
|
||||
...args,
|
||||
)) as (typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"],
|
||||
}));
|
||||
|
||||
vi.mock("./prompt.js", () => ({
|
||||
promptYesNo: (...args: unknown[]) => promptYesNo(...args),
|
||||
promptYesNo: ((...args: Parameters<(typeof import("./prompt.js"))["promptYesNo"]>) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("./prompt.js"))["promptYesNo"]>,
|
||||
ReturnType<(typeof import("./prompt.js"))["promptYesNo"]>
|
||||
>(promptYesNo, ...args)) as (typeof import("./prompt.js"))["promptYesNo"],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/install.js", () => ({
|
||||
PLUGIN_INSTALL_ERROR_CODE: {
|
||||
NPM_PACKAGE_NOT_FOUND: "npm_package_not_found",
|
||||
},
|
||||
installPluginFromNpmSpec: (...args: unknown[]) => installPluginFromNpmSpec(...args),
|
||||
installPluginFromPath: (...args: unknown[]) => installPluginFromPath(...args),
|
||||
installPluginFromNpmSpec: ((
|
||||
...args: Parameters<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>,
|
||||
ReturnType<(typeof import("../plugins/install.js"))["installPluginFromNpmSpec"]>
|
||||
>(
|
||||
installPluginFromNpmSpec,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/install.js"))["installPluginFromNpmSpec"],
|
||||
installPluginFromPath: ((
|
||||
...args: Parameters<(typeof import("../plugins/install.js"))["installPluginFromPath"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/install.js"))["installPluginFromPath"]>,
|
||||
ReturnType<(typeof import("../plugins/install.js"))["installPluginFromPath"]>
|
||||
>(
|
||||
installPluginFromPath,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/install.js"))["installPluginFromPath"],
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/install.js", () => ({
|
||||
installHooksFromNpmSpec: (...args: unknown[]) => installHooksFromNpmSpec(...args),
|
||||
installHooksFromPath: (...args: unknown[]) => installHooksFromPath(...args),
|
||||
installHooksFromNpmSpec: ((
|
||||
...args: Parameters<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>,
|
||||
ReturnType<(typeof import("../hooks/install.js"))["installHooksFromNpmSpec"]>
|
||||
>(
|
||||
installHooksFromNpmSpec,
|
||||
...args,
|
||||
)) as (typeof import("../hooks/install.js"))["installHooksFromNpmSpec"],
|
||||
installHooksFromPath: ((
|
||||
...args: Parameters<(typeof import("../hooks/install.js"))["installHooksFromPath"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../hooks/install.js"))["installHooksFromPath"]>,
|
||||
ReturnType<(typeof import("../hooks/install.js"))["installHooksFromPath"]>
|
||||
>(
|
||||
installHooksFromPath,
|
||||
...args,
|
||||
)) as (typeof import("../hooks/install.js"))["installHooksFromPath"],
|
||||
resolveHookInstallDir: (hookId: string) => `/tmp/hooks/${hookId}`,
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/installs.js", () => ({
|
||||
recordHookInstall: (...args: unknown[]) => recordHookInstall(...args),
|
||||
recordHookInstall: ((
|
||||
...args: Parameters<(typeof import("../hooks/installs.js"))["recordHookInstall"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../hooks/installs.js"))["recordHookInstall"]>,
|
||||
ReturnType<(typeof import("../hooks/installs.js"))["recordHookInstall"]>
|
||||
>(recordHookInstall, ...args)) as (typeof import("../hooks/installs.js"))["recordHookInstall"],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/clawhub.js", () => ({
|
||||
@@ -127,13 +282,31 @@ vi.mock("../plugins/clawhub.js", () => ({
|
||||
PACKAGE_NOT_FOUND: "package_not_found",
|
||||
VERSION_NOT_FOUND: "version_not_found",
|
||||
},
|
||||
installPluginFromClawHub: (...args: unknown[]) => installPluginFromClawHub(...args),
|
||||
installPluginFromClawHub: ((
|
||||
...args: Parameters<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>,
|
||||
ReturnType<(typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"]>
|
||||
>(
|
||||
installPluginFromClawHub,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"],
|
||||
formatClawHubSpecifier: ({ name, version }: { name: string; version?: string }) =>
|
||||
`clawhub:${name}${version ? `@${version}` : ""}`,
|
||||
}));
|
||||
|
||||
vi.mock("../infra/clawhub.js", () => ({
|
||||
parseClawHubPluginSpec: (...args: unknown[]) => parseClawHubPluginSpec(...args),
|
||||
parseClawHubPluginSpec: ((
|
||||
...args: Parameters<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>
|
||||
) =>
|
||||
invokeMock<
|
||||
Parameters<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>,
|
||||
ReturnType<(typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"]>
|
||||
>(
|
||||
parseClawHubPluginSpec,
|
||||
...args,
|
||||
)) as (typeof import("../infra/clawhub.js"))["parseClawHubPluginSpec"],
|
||||
}));
|
||||
|
||||
const { registerPluginsCli } = await import("./plugins-cli.js");
|
||||
@@ -197,7 +370,8 @@ export function resetPluginsCliTestState() {
|
||||
});
|
||||
writeConfigFile.mockResolvedValue(undefined);
|
||||
replaceConfigFile.mockImplementation(
|
||||
async (params: { nextConfig: OpenClawConfig }) => await writeConfigFile(params.nextConfig),
|
||||
(async (params: { nextConfig: OpenClawConfig }) =>
|
||||
await writeConfigFile(params.nextConfig)) as (...args: unknown[]) => Promise<unknown>,
|
||||
);
|
||||
resolveStateDir.mockReturnValue("/tmp/openclaw-state");
|
||||
resolveMarketplaceInstallShortcut.mockResolvedValue(null);
|
||||
@@ -205,8 +379,12 @@ export function resetPluginsCliTestState() {
|
||||
ok: false,
|
||||
error: "marketplace install failed",
|
||||
});
|
||||
enablePluginInConfig.mockImplementation((cfg: OpenClawConfig) => ({ config: cfg }));
|
||||
recordPluginInstall.mockImplementation((cfg: OpenClawConfig) => cfg);
|
||||
enablePluginInConfig.mockImplementation(((cfg: OpenClawConfig) => ({ config: cfg })) as (
|
||||
...args: unknown[]
|
||||
) => unknown);
|
||||
recordPluginInstall.mockImplementation(
|
||||
((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown,
|
||||
);
|
||||
const defaultPluginReport = {
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
@@ -214,10 +392,10 @@ export function resetPluginsCliTestState() {
|
||||
buildPluginSnapshotReport.mockReturnValue(defaultPluginReport);
|
||||
buildPluginDiagnosticsReport.mockReturnValue(defaultPluginReport);
|
||||
buildPluginCompatibilityNotices.mockReturnValue([]);
|
||||
applyExclusiveSlotSelection.mockImplementation(({ config }: { config: OpenClawConfig }) => ({
|
||||
applyExclusiveSlotSelection.mockImplementation((({ config }: { config: OpenClawConfig }) => ({
|
||||
config,
|
||||
warnings: [],
|
||||
}));
|
||||
})) as (...args: unknown[]) => unknown);
|
||||
uninstallPlugin.mockResolvedValue({
|
||||
ok: true,
|
||||
config: {} as OpenClawConfig,
|
||||
@@ -260,5 +438,7 @@ export function resetPluginsCliTestState() {
|
||||
ok: false,
|
||||
error: "hook npm install disabled in test",
|
||||
});
|
||||
recordHookInstall.mockImplementation((cfg: OpenClawConfig) => cfg);
|
||||
recordHookInstall.mockImplementation(
|
||||
((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ describe("plugins cli install", () => {
|
||||
ok: true,
|
||||
pluginId: "alpha",
|
||||
targetDir: cliInstallPath("alpha"),
|
||||
extensions: ["index.js"],
|
||||
version: "1.2.3",
|
||||
marketplaceName: "Claude",
|
||||
marketplaceSource: "local/repo",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Mock } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { mergeMockedModule } from "../test-utils/vitest-module-mocks.js";
|
||||
@@ -7,9 +8,11 @@ type ReplaceConfigFileResult = Awaited<
|
||||
ReturnType<(typeof import("../config/config.js"))["replaceConfigFile"]>
|
||||
>;
|
||||
|
||||
export const readConfigFileSnapshotMock = vi.fn();
|
||||
export const writeConfigFileMock = vi.fn().mockResolvedValue(undefined);
|
||||
export const replaceConfigFileMock = vi.fn(
|
||||
export const readConfigFileSnapshotMock: Mock<(...args: unknown[]) => Promise<unknown>> = vi.fn();
|
||||
export const writeConfigFileMock: Mock<(...args: unknown[]) => Promise<unknown>> = vi
|
||||
.fn()
|
||||
.mockResolvedValue(undefined);
|
||||
export const replaceConfigFileMock: Mock<(...args: unknown[]) => Promise<unknown>> = vi.fn(
|
||||
async (params: { nextConfig: OpenClawConfig }): Promise<ReplaceConfigFileResult> => {
|
||||
await writeConfigFileMock(params.nextConfig);
|
||||
return {
|
||||
@@ -19,17 +22,18 @@ export const replaceConfigFileMock = vi.fn(
|
||||
nextConfig: params.nextConfig,
|
||||
};
|
||||
},
|
||||
);
|
||||
) as Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
|
||||
vi.mock("../config/config.js", async (importOriginal) => {
|
||||
return await mergeMockedModule(
|
||||
await importOriginal<typeof import("../config/config.js")>(),
|
||||
() => ({
|
||||
readConfigFileSnapshot: readConfigFileSnapshotMock,
|
||||
writeConfigFile: writeConfigFileMock,
|
||||
replaceConfigFile: replaceConfigFileMock,
|
||||
}),
|
||||
);
|
||||
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||
return await mergeMockedModule(actual, () => ({
|
||||
readConfigFileSnapshot: (...args: Parameters<typeof actual.readConfigFileSnapshot>) =>
|
||||
readConfigFileSnapshotMock(...args) as ReturnType<typeof actual.readConfigFileSnapshot>,
|
||||
writeConfigFile: (...args: Parameters<typeof actual.writeConfigFile>) =>
|
||||
writeConfigFileMock(...args) as ReturnType<typeof actual.writeConfigFile>,
|
||||
replaceConfigFile: (...args: Parameters<typeof actual.replaceConfigFile>) =>
|
||||
replaceConfigFileMock(...args) as ReturnType<typeof actual.replaceConfigFile>,
|
||||
}));
|
||||
});
|
||||
|
||||
export const runtime = createTestRuntime();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Mock } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
|
||||
export const terminalNoteMock = vi.fn();
|
||||
export const terminalNoteMock: Mock<(...args: unknown[]) => unknown> = vi.fn();
|
||||
|
||||
vi.mock("../terminal/note.js", () => ({
|
||||
note: (...args: unknown[]) => terminalNoteMock(...args),
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
import type { Mock } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
|
||||
export function createStatusScanSharedMocks(configPathLabel: string) {
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type ResolveConfigPathMock = Mock<() => string>;
|
||||
|
||||
export type StatusScanSharedMocks = {
|
||||
resolveConfigPath: ResolveConfigPathMock;
|
||||
hasPotentialConfiguredChannels: UnknownMock;
|
||||
readBestEffortConfig: UnknownMock;
|
||||
resolveCommandSecretRefsViaGateway: UnknownMock;
|
||||
getUpdateCheckResult: UnknownMock;
|
||||
getAgentLocalStatuses: UnknownMock;
|
||||
getStatusSummary: UnknownMock;
|
||||
getMemorySearchManager: UnknownMock;
|
||||
buildGatewayConnectionDetails: UnknownMock;
|
||||
probeGateway: UnknownMock;
|
||||
resolveGatewayProbeAuthResolution: UnknownMock;
|
||||
ensurePluginRegistryLoaded: UnknownMock;
|
||||
buildPluginCompatibilityNotices: Mock<() => unknown[]>;
|
||||
};
|
||||
|
||||
export function createStatusScanSharedMocks(configPathLabel: string): StatusScanSharedMocks {
|
||||
return {
|
||||
resolveConfigPath: vi.fn(() => `/tmp/openclaw-${configPathLabel}-missing-${process.pid}.json`),
|
||||
hasPotentialConfiguredChannels: vi.fn(),
|
||||
@@ -19,37 +39,54 @@ export function createStatusScanSharedMocks(configPathLabel: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export type StatusScanSharedMocks = ReturnType<typeof createStatusScanSharedMocks>;
|
||||
type StatusOsSummaryModuleMock = {
|
||||
resolveOsSummary: Mock<() => { label: string }>;
|
||||
};
|
||||
|
||||
export function createStatusOsSummaryModuleMock() {
|
||||
export function createStatusOsSummaryModuleMock(): StatusOsSummaryModuleMock {
|
||||
return {
|
||||
resolveOsSummary: vi.fn(() => ({ label: "test-os" })),
|
||||
};
|
||||
}
|
||||
|
||||
type StatusScanDepsRuntimeModuleMock = {
|
||||
getTailnetHostname: UnknownMock;
|
||||
getMemorySearchManager: StatusScanSharedMocks["getMemorySearchManager"];
|
||||
};
|
||||
|
||||
export function createStatusScanDepsRuntimeModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "getMemorySearchManager">,
|
||||
) {
|
||||
): StatusScanDepsRuntimeModuleMock {
|
||||
return {
|
||||
getTailnetHostname: vi.fn(),
|
||||
getMemorySearchManager: mocks.getMemorySearchManager,
|
||||
};
|
||||
}
|
||||
|
||||
type StatusGatewayProbeModuleMock = {
|
||||
pickGatewaySelfPresence: Mock<() => null>;
|
||||
resolveGatewayProbeAuthResolution: StatusScanSharedMocks["resolveGatewayProbeAuthResolution"];
|
||||
};
|
||||
|
||||
export function createStatusGatewayProbeModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "resolveGatewayProbeAuthResolution">,
|
||||
) {
|
||||
): StatusGatewayProbeModuleMock {
|
||||
return {
|
||||
pickGatewaySelfPresence: vi.fn(() => null),
|
||||
resolveGatewayProbeAuthResolution: mocks.resolveGatewayProbeAuthResolution,
|
||||
};
|
||||
}
|
||||
|
||||
type StatusGatewayCallModuleMock = {
|
||||
buildGatewayConnectionDetails: StatusScanSharedMocks["buildGatewayConnectionDetails"];
|
||||
callGateway?: unknown;
|
||||
};
|
||||
|
||||
export function createStatusGatewayCallModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "buildGatewayConnectionDetails"> & {
|
||||
callGateway?: unknown;
|
||||
},
|
||||
) {
|
||||
): StatusGatewayCallModuleMock {
|
||||
return {
|
||||
buildGatewayConnectionDetails: mocks.buildGatewayConnectionDetails,
|
||||
...(mocks.callGateway ? { callGateway: mocks.callGateway } : {}),
|
||||
@@ -58,7 +95,7 @@ export function createStatusGatewayCallModuleMock(
|
||||
|
||||
export function createStatusPluginRegistryModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "ensurePluginRegistryLoaded">,
|
||||
) {
|
||||
): { ensurePluginRegistryLoaded: StatusScanSharedMocks["ensurePluginRegistryLoaded"] } {
|
||||
return {
|
||||
ensurePluginRegistryLoaded: mocks.ensurePluginRegistryLoaded,
|
||||
};
|
||||
@@ -66,7 +103,7 @@ export function createStatusPluginRegistryModuleMock(
|
||||
|
||||
export function createStatusPluginStatusModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "buildPluginCompatibilityNotices">,
|
||||
) {
|
||||
): { buildPluginCompatibilityNotices: StatusScanSharedMocks["buildPluginCompatibilityNotices"] } {
|
||||
return {
|
||||
buildPluginCompatibilityNotices: mocks.buildPluginCompatibilityNotices,
|
||||
};
|
||||
@@ -74,7 +111,7 @@ export function createStatusPluginStatusModuleMock(
|
||||
|
||||
export function createStatusUpdateModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "getUpdateCheckResult">,
|
||||
) {
|
||||
): { getUpdateCheckResult: StatusScanSharedMocks["getUpdateCheckResult"] } {
|
||||
return {
|
||||
getUpdateCheckResult: mocks.getUpdateCheckResult,
|
||||
};
|
||||
@@ -82,7 +119,7 @@ export function createStatusUpdateModuleMock(
|
||||
|
||||
export function createStatusAgentLocalModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "getAgentLocalStatuses">,
|
||||
) {
|
||||
): { getAgentLocalStatuses: StatusScanSharedMocks["getAgentLocalStatuses"] } {
|
||||
return {
|
||||
getAgentLocalStatuses: mocks.getAgentLocalStatuses,
|
||||
};
|
||||
@@ -90,23 +127,23 @@ export function createStatusAgentLocalModuleMock(
|
||||
|
||||
export function createStatusSummaryModuleMock(
|
||||
mocks: Pick<StatusScanSharedMocks, "getStatusSummary">,
|
||||
) {
|
||||
): { getStatusSummary: StatusScanSharedMocks["getStatusSummary"] } {
|
||||
return {
|
||||
getStatusSummary: mocks.getStatusSummary,
|
||||
};
|
||||
}
|
||||
|
||||
export function createStatusExecModuleMock() {
|
||||
export function createStatusExecModuleMock(): { runExec: UnknownMock } {
|
||||
return {
|
||||
runExec: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
type StatusScanModuleTestMocks = StatusScanSharedMocks & {
|
||||
buildChannelsTable?: ReturnType<typeof vi.fn>;
|
||||
callGateway?: ReturnType<typeof vi.fn>;
|
||||
getStatusCommandSecretTargetIds?: ReturnType<typeof vi.fn>;
|
||||
resolveMemorySearchConfig?: ReturnType<typeof vi.fn>;
|
||||
buildChannelsTable?: UnknownMock;
|
||||
callGateway?: UnknownMock;
|
||||
getStatusCommandSecretTargetIds?: UnknownMock;
|
||||
resolveMemorySearchConfig?: UnknownMock;
|
||||
};
|
||||
|
||||
export async function loadStatusScanModuleForTest(
|
||||
|
||||
@@ -33,6 +33,9 @@ type GetReplyFromConfigFn = (
|
||||
opts?: GetReplyOptions,
|
||||
configOverride?: OpenClawConfig,
|
||||
) => Promise<ReplyPayload | ReplyPayload[] | undefined>;
|
||||
type CronIsolatedRunFn = (...args: unknown[]) => Promise<{ status: string; summary: string }>;
|
||||
type AgentCommandFn = (...args: unknown[]) => Promise<void>;
|
||||
type SendWhatsAppFn = (...args: unknown[]) => Promise<{ messageId: string; toJid: string }>;
|
||||
|
||||
const createStubOutboundAdapter = (channelId: ChannelPlugin["id"]): ChannelOutboundAdapter => ({
|
||||
deliveryMode: "direct",
|
||||
@@ -233,8 +236,8 @@ const hoisted = vi.hoisted(() => {
|
||||
reasoning?: boolean;
|
||||
}>;
|
||||
};
|
||||
cronIsolatedRun: ReturnType<typeof vi.fn>;
|
||||
agentCommand: ReturnType<typeof vi.fn>;
|
||||
cronIsolatedRun: Mock<CronIsolatedRunFn>;
|
||||
agentCommand: Mock<AgentCommandFn>;
|
||||
testIsNixMode: { value: boolean };
|
||||
sessionStoreSaveDelayMs: { value: number };
|
||||
embeddedRunMock: {
|
||||
@@ -244,8 +247,8 @@ const hoisted = vi.hoisted(() => {
|
||||
waitResults: Map<string, boolean>;
|
||||
};
|
||||
testTailscaleWhois: { value: TailscaleWhoisIdentity | null };
|
||||
getReplyFromConfig: ReturnType<typeof vi.fn<GetReplyFromConfigFn>>;
|
||||
sendWhatsAppMock: ReturnType<typeof vi.fn>;
|
||||
getReplyFromConfig: Mock<GetReplyFromConfigFn>;
|
||||
sendWhatsAppMock: Mock<SendWhatsAppFn>;
|
||||
testState: {
|
||||
agentConfig: Record<string, unknown> | undefined;
|
||||
agentsConfig: Record<string, unknown> | undefined;
|
||||
@@ -346,13 +349,13 @@ export const setTestConfigRoot = (root: string) => {
|
||||
export const testTailnetIPv4 = hoisted.testTailnetIPv4;
|
||||
export const testTailscaleWhois = hoisted.testTailscaleWhois;
|
||||
export const piSdkMock = hoisted.piSdkMock;
|
||||
export const cronIsolatedRun = hoisted.cronIsolatedRun;
|
||||
export const agentCommand = hoisted.agentCommand;
|
||||
export const cronIsolatedRun: Mock<CronIsolatedRunFn> = hoisted.cronIsolatedRun;
|
||||
export const agentCommand: Mock<AgentCommandFn> = hoisted.agentCommand;
|
||||
export const getReplyFromConfig: Mock<GetReplyFromConfigFn> = hoisted.getReplyFromConfig;
|
||||
export const mockGetReplyFromConfigOnce = (impl: GetReplyFromConfigFn) => {
|
||||
getReplyFromConfig.mockImplementationOnce(impl);
|
||||
};
|
||||
export const sendWhatsAppMock = hoisted.sendWhatsAppMock;
|
||||
export const sendWhatsAppMock: Mock<SendWhatsAppFn> = hoisted.sendWhatsAppMock;
|
||||
|
||||
export const testState = hoisted.testState;
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ export function logVerboseConsole(message: string) {
|
||||
console.log(theme.muted(message));
|
||||
}
|
||||
|
||||
export const success = theme.success;
|
||||
export const warn = theme.warn;
|
||||
export const info = theme.info;
|
||||
export const danger = theme.error;
|
||||
type ThemeFormatter = (value: string) => string;
|
||||
|
||||
export const success: ThemeFormatter = theme.success;
|
||||
export const warn: ThemeFormatter = theme.warn;
|
||||
export const info: ThemeFormatter = theme.info;
|
||||
export const danger: ThemeFormatter = theme.error;
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
|
||||
import { createLazyRuntimeMethodBinder, createLazyRuntimeModule } from "../shared/lazy-runtime.js";
|
||||
|
||||
type ProviderAuthLoginRuntime = typeof import("./provider-auth-login.runtime.js");
|
||||
|
||||
const loadProviderAuthLoginRuntime = createLazyRuntimeModule(
|
||||
() => import("./provider-auth-login.runtime.js"),
|
||||
);
|
||||
const bindProviderAuthLoginRuntime = createLazyRuntimeMethodBinder(loadProviderAuthLoginRuntime);
|
||||
|
||||
export const githubCopilotLoginCommand = bindProviderAuthLoginRuntime(
|
||||
(runtime) => runtime.githubCopilotLoginCommand,
|
||||
);
|
||||
export const loginChutes = bindProviderAuthLoginRuntime((runtime) => runtime.loginChutes);
|
||||
export const loginOpenAICodexOAuth = bindProviderAuthLoginRuntime(
|
||||
(runtime) => runtime.loginOpenAICodexOAuth,
|
||||
export const githubCopilotLoginCommand: ProviderAuthLoginRuntime["githubCopilotLoginCommand"] =
|
||||
bindProviderAuthLoginRuntime((runtime) => runtime.githubCopilotLoginCommand);
|
||||
export const loginChutes: ProviderAuthLoginRuntime["loginChutes"] = bindProviderAuthLoginRuntime(
|
||||
(runtime) => runtime.loginChutes,
|
||||
);
|
||||
export const loginOpenAICodexOAuth: ProviderAuthLoginRuntime["loginOpenAICodexOAuth"] =
|
||||
bindProviderAuthLoginRuntime((runtime) => runtime.loginOpenAICodexOAuth);
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import { vi } from "vitest";
|
||||
import { vi, type Mock } from "vitest";
|
||||
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
|
||||
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
|
||||
import { createRuntimeEnv } from "./runtime-env.js";
|
||||
|
||||
export type { WizardPrompter } from "../../../src/wizard/prompts.js";
|
||||
type UnknownMock = Mock<(...args: unknown[]) => unknown>;
|
||||
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
type QueuedWizardPrompter = {
|
||||
intro: AsyncUnknownMock;
|
||||
outro: AsyncUnknownMock;
|
||||
note: AsyncUnknownMock;
|
||||
select: AsyncUnknownMock;
|
||||
multiselect: AsyncUnknownMock;
|
||||
text: AsyncUnknownMock;
|
||||
confirm: AsyncUnknownMock;
|
||||
progress: Mock<() => { update: UnknownMock; stop: UnknownMock }>;
|
||||
prompter: WizardPrompter;
|
||||
};
|
||||
|
||||
export async function selectFirstWizardOption<T>(params: {
|
||||
options: Array<{ value: T }>;
|
||||
@@ -34,7 +47,7 @@ export function createQueuedWizardPrompter(params?: {
|
||||
selectValues?: string[];
|
||||
textValues?: string[];
|
||||
confirmValues?: boolean[];
|
||||
}) {
|
||||
}): QueuedWizardPrompter {
|
||||
const selectValues = [...(params?.selectValues ?? [])];
|
||||
const textValues = [...(params?.textValues ?? [])];
|
||||
const confirmValues = [...(params?.confirmValues ?? [])];
|
||||
|
||||
Reference in New Issue
Block a user