mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 09:21:35 +07:00
test: harden ci isolated mocks
This commit is contained in:
@@ -4,50 +4,42 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
const mockStore: Record<string, Record<string, unknown>> = {};
|
||||
|
||||
vi.mock("../config/sessions.js", () => ({
|
||||
loadSessionStore: vi.fn((storePath: string) => mockStore[storePath] ?? {}),
|
||||
resolveAgentMainSessionKey: vi.fn(({ agentId }: { agentId: string }) => `agent:${agentId}:main`),
|
||||
resolveStorePath: vi.fn((_store: unknown, _opts: unknown) => "/mock/store.json"),
|
||||
}));
|
||||
|
||||
vi.mock("../infra/outbound/channel-selection.js", () => ({
|
||||
resolveMessageChannelSelection: vi.fn(async () => ({ channel: "telegram" })),
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: vi.fn(() => ({
|
||||
meta: { label: "Telegram" },
|
||||
config: {},
|
||||
messaging: {
|
||||
parseExplicitTarget: ({ raw }: { raw: string }) => {
|
||||
const target = parseTelegramTarget(raw);
|
||||
return {
|
||||
to: target.chatId,
|
||||
threadId: target.messageThreadId,
|
||||
chatType: target.chatType === "unknown" ? undefined : target.chatType,
|
||||
};
|
||||
},
|
||||
},
|
||||
outbound: {
|
||||
resolveTarget: ({ to }: { to?: string }) =>
|
||||
to ? { ok: true, to } : { ok: false, error: new Error("missing") },
|
||||
},
|
||||
})),
|
||||
normalizeChannelId: vi.fn((id: string) => id),
|
||||
}));
|
||||
|
||||
const mockedModuleIds = [
|
||||
"../channels/plugins/index.js",
|
||||
"../config/sessions.js",
|
||||
"../infra/outbound/channel-selection.js",
|
||||
];
|
||||
|
||||
let resolveDeliveryTarget: typeof import("./isolated-agent/delivery-target.js").resolveDeliveryTarget;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
vi.resetModules();
|
||||
vi.doMock("../config/sessions.js", () => ({
|
||||
loadSessionStore: vi.fn((storePath: string) => mockStore[storePath] ?? {}),
|
||||
resolveAgentMainSessionKey: vi.fn(
|
||||
({ agentId }: { agentId: string }) => `agent:${agentId}:main`,
|
||||
),
|
||||
resolveStorePath: vi.fn((_store: unknown, _opts: unknown) => "/mock/store.json"),
|
||||
}));
|
||||
vi.doMock("../infra/outbound/channel-selection.js", () => ({
|
||||
resolveMessageChannelSelection: vi.fn(async () => ({ channel: "telegram" })),
|
||||
}));
|
||||
vi.doMock("../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: vi.fn(() => ({
|
||||
meta: { label: "Telegram" },
|
||||
config: {},
|
||||
messaging: {
|
||||
parseExplicitTarget: ({ raw }: { raw: string }) => {
|
||||
const target = parseTelegramTarget(raw);
|
||||
return {
|
||||
to: target.chatId,
|
||||
threadId: target.messageThreadId,
|
||||
chatType: target.chatType === "unknown" ? undefined : target.chatType,
|
||||
};
|
||||
},
|
||||
},
|
||||
outbound: {
|
||||
resolveTarget: ({ to }: { to?: string }) =>
|
||||
to ? { ok: true, to } : { ok: false, error: new Error("missing") },
|
||||
},
|
||||
})),
|
||||
normalizeChannelId: vi.fn((id: string) => id),
|
||||
}));
|
||||
({ resolveDeliveryTarget } = await import("./isolated-agent/delivery-target.js"));
|
||||
vi.clearAllMocks();
|
||||
for (const key of Object.keys(mockStore)) {
|
||||
delete mockStore[key];
|
||||
}
|
||||
@@ -55,9 +47,6 @@ beforeEach(async () => {
|
||||
|
||||
afterAll(() => {
|
||||
vi.restoreAllMocks();
|
||||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
|
||||
@@ -5,18 +5,25 @@ import { getActivePluginRegistry, getActivePluginRegistryKey } from "../plugins/
|
||||
import type { ImageGenerationProviderPlugin } from "../plugins/types.js";
|
||||
|
||||
const BUILTIN_IMAGE_GENERATION_PROVIDERS: readonly ImageGenerationProviderPlugin[] = [];
|
||||
const UNSAFE_PROVIDER_IDS = new Set(["__proto__", "constructor", "prototype"]);
|
||||
|
||||
function normalizeImageGenerationProviderId(id: string | undefined): string | undefined {
|
||||
const normalized = normalizeProviderId(id ?? "");
|
||||
return normalized || undefined;
|
||||
}
|
||||
|
||||
function isSafeImageGenerationProviderId(id: string | undefined): id is string {
|
||||
return Boolean(id && !UNSAFE_PROVIDER_IDS.has(id));
|
||||
}
|
||||
|
||||
function resolvePluginImageGenerationProviders(
|
||||
cfg?: OpenClawConfig,
|
||||
): ImageGenerationProviderPlugin[] {
|
||||
const active = getActivePluginRegistry();
|
||||
const registry =
|
||||
getActivePluginRegistryKey() || !cfg ? active : loadOpenClawPlugins({ config: cfg });
|
||||
(active?.imageGenerationProviders?.length ?? 0) > 0 || getActivePluginRegistryKey() || !cfg
|
||||
? active
|
||||
: loadOpenClawPlugins({ config: cfg });
|
||||
return registry?.imageGenerationProviders?.map((entry) => entry.provider) ?? [];
|
||||
}
|
||||
|
||||
@@ -28,14 +35,14 @@ function buildProviderMaps(cfg?: OpenClawConfig): {
|
||||
const aliases = new Map<string, ImageGenerationProviderPlugin>();
|
||||
const register = (provider: ImageGenerationProviderPlugin) => {
|
||||
const id = normalizeImageGenerationProviderId(provider.id);
|
||||
if (!id) {
|
||||
if (!isSafeImageGenerationProviderId(id)) {
|
||||
return;
|
||||
}
|
||||
canonical.set(id, provider);
|
||||
aliases.set(id, provider);
|
||||
for (const alias of provider.aliases ?? []) {
|
||||
const normalizedAlias = normalizeImageGenerationProviderId(alias);
|
||||
if (normalizedAlias) {
|
||||
if (isSafeImageGenerationProviderId(normalizedAlias)) {
|
||||
aliases.set(normalizedAlias, provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,11 @@ import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||
|
||||
let runMessageAction: typeof import("./message-action-runner.js").runMessageAction;
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
executePollAction: vi.fn(),
|
||||
}));
|
||||
|
||||
let runMessageAction: typeof import("./message-action-runner.js").runMessageAction;
|
||||
|
||||
vi.mock("./outbound-send-service.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./outbound-send-service.js")>(
|
||||
"./outbound-send-service.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
executePollAction: mocks.executePollAction,
|
||||
};
|
||||
});
|
||||
|
||||
const telegramConfig = {
|
||||
channels: {
|
||||
telegram: {
|
||||
@@ -43,6 +32,12 @@ const telegramPollTestPlugin: ChannelPlugin = {
|
||||
resolveAccount: () => ({ botToken: "telegram-test" }),
|
||||
isConfigured: () => true,
|
||||
},
|
||||
outbound: {
|
||||
deliveryMode: "gateway",
|
||||
sendPoll: async () => ({
|
||||
messageId: "poll-test",
|
||||
}),
|
||||
},
|
||||
messaging: {
|
||||
targetResolver: {
|
||||
looksLikeId: () => true,
|
||||
@@ -99,7 +94,15 @@ async function runPollAction(params: {
|
||||
describe("runMessageAction poll handling", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ runMessageAction } = await import("./message-action-runner.js"));
|
||||
vi.doMock("./outbound-send-service.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./outbound-send-service.js")>(
|
||||
"./outbound-send-service.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
executePollAction: mocks.executePollAction,
|
||||
};
|
||||
});
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
@@ -115,6 +118,7 @@ describe("runMessageAction poll handling", () => {
|
||||
payload: { ok: true, corePoll: input.resolveCorePoll() },
|
||||
pollResult: { ok: true },
|
||||
}));
|
||||
({ runMessageAction } = await import("./message-action-runner.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -7,43 +7,6 @@ const mocks = vi.hoisted(() => ({
|
||||
loadOpenClawPlugins: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined,
|
||||
getChannelPlugin: mocks.getChannelPlugin,
|
||||
listChannelPlugins: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: () => "main",
|
||||
resolveSessionAgentId: ({
|
||||
sessionKey,
|
||||
}: {
|
||||
sessionKey?: string;
|
||||
config?: unknown;
|
||||
agentId?: string;
|
||||
}) => {
|
||||
const match = sessionKey?.match(/^agent:([^:]+)/i);
|
||||
return match?.[1] ?? "main";
|
||||
},
|
||||
resolveAgentWorkspaceDir: () => "/tmp/openclaw-test-workspace",
|
||||
}));
|
||||
|
||||
vi.mock("../../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable: ({ config }: { config: unknown }) => ({ config, changes: [] }),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: mocks.loadOpenClawPlugins,
|
||||
}));
|
||||
|
||||
vi.mock("./targets.js", () => ({
|
||||
resolveOutboundTarget: mocks.resolveOutboundTarget,
|
||||
}));
|
||||
|
||||
vi.mock("./deliver.js", () => ({
|
||||
deliverOutboundPayloads: mocks.deliverOutboundPayloads,
|
||||
}));
|
||||
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||
|
||||
@@ -52,7 +15,37 @@ let sendMessage: typeof import("./message.js").sendMessage;
|
||||
describe("sendMessage", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ sendMessage } = await import("./message.js"));
|
||||
vi.doMock("../../channels/plugins/index.js", () => ({
|
||||
normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined,
|
||||
getChannelPlugin: mocks.getChannelPlugin,
|
||||
listChannelPlugins: () => [],
|
||||
}));
|
||||
vi.doMock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: () => "main",
|
||||
resolveSessionAgentId: ({
|
||||
sessionKey,
|
||||
}: {
|
||||
sessionKey?: string;
|
||||
config?: unknown;
|
||||
agentId?: string;
|
||||
}) => {
|
||||
const match = sessionKey?.match(/^agent:([^:]+)/i);
|
||||
return match?.[1] ?? "main";
|
||||
},
|
||||
resolveAgentWorkspaceDir: () => "/tmp/openclaw-test-workspace",
|
||||
}));
|
||||
vi.doMock("../../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable: ({ config }: { config: unknown }) => ({ config, changes: [] }),
|
||||
}));
|
||||
vi.doMock("../../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: mocks.loadOpenClawPlugins,
|
||||
}));
|
||||
vi.doMock("./targets.js", () => ({
|
||||
resolveOutboundTarget: mocks.resolveOutboundTarget,
|
||||
}));
|
||||
vi.doMock("./deliver.js", () => ({
|
||||
deliverOutboundPayloads: mocks.deliverOutboundPayloads,
|
||||
}));
|
||||
setActivePluginRegistry(createTestRegistry([]));
|
||||
mocks.getChannelPlugin.mockClear();
|
||||
mocks.resolveOutboundTarget.mockClear();
|
||||
@@ -64,6 +57,8 @@ describe("sendMessage", () => {
|
||||
});
|
||||
mocks.resolveOutboundTarget.mockImplementation(({ to }: { to: string }) => ({ ok: true, to }));
|
||||
mocks.deliverOutboundPayloads.mockResolvedValue([{ channel: "mattermost", messageId: "m1" }]);
|
||||
|
||||
({ sendMessage } = await import("./message.js"));
|
||||
});
|
||||
|
||||
it("passes explicit agentId to outbound delivery for scoped media roots", async () => {
|
||||
|
||||
@@ -3,6 +3,11 @@ import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
||||
let applyCrossContextDecoration: typeof import("./outbound-policy.js").applyCrossContextDecoration;
|
||||
let buildCrossContextDecoration: typeof import("./outbound-policy.js").buildCrossContextDecoration;
|
||||
let enforceCrossContextPolicy: typeof import("./outbound-policy.js").enforceCrossContextPolicy;
|
||||
let shouldApplyCrossContextMarker: typeof import("./outbound-policy.js").shouldApplyCrossContextMarker;
|
||||
|
||||
class TestDiscordUiContainer extends Container {}
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -47,19 +52,6 @@ const mocks = vi.hoisted(() => ({
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./channel-adapters.js", () => ({
|
||||
getChannelMessageAdapter: mocks.getChannelMessageAdapter,
|
||||
}));
|
||||
|
||||
vi.mock("./target-normalization.js", () => ({
|
||||
normalizeTargetForProvider: mocks.normalizeTargetForProvider,
|
||||
}));
|
||||
|
||||
vi.mock("./target-resolver.js", () => ({
|
||||
formatTargetDisplay: mocks.formatTargetDisplay,
|
||||
lookupDirectoryDisplay: mocks.lookupDirectoryDisplay,
|
||||
}));
|
||||
|
||||
const slackConfig = {
|
||||
channels: {
|
||||
slack: {
|
||||
@@ -75,15 +67,20 @@ const discordConfig = {
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
let applyCrossContextDecoration: typeof import("./outbound-policy.js").applyCrossContextDecoration;
|
||||
let buildCrossContextDecoration: typeof import("./outbound-policy.js").buildCrossContextDecoration;
|
||||
let enforceCrossContextPolicy: typeof import("./outbound-policy.js").enforceCrossContextPolicy;
|
||||
let shouldApplyCrossContextMarker: typeof import("./outbound-policy.js").shouldApplyCrossContextMarker;
|
||||
|
||||
describe("outbound policy helpers", () => {
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
vi.doMock("./channel-adapters.js", () => ({
|
||||
getChannelMessageAdapter: mocks.getChannelMessageAdapter,
|
||||
}));
|
||||
vi.doMock("./target-normalization.js", () => ({
|
||||
normalizeTargetForProvider: mocks.normalizeTargetForProvider,
|
||||
}));
|
||||
vi.doMock("./target-resolver.js", () => ({
|
||||
formatTargetDisplay: mocks.formatTargetDisplay,
|
||||
lookupDirectoryDisplay: mocks.lookupDirectoryDisplay,
|
||||
}));
|
||||
({
|
||||
applyCrossContextDecoration,
|
||||
buildCrossContextDecoration,
|
||||
|
||||
@@ -16,15 +16,25 @@ vi.mock("../config/paths.js", () => ({
|
||||
resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args),
|
||||
}));
|
||||
|
||||
import {
|
||||
__testing,
|
||||
cleanStaleGatewayProcessesSync,
|
||||
findGatewayPidsOnPortSync,
|
||||
} from "./restart-stale-pids.js";
|
||||
let __testing: typeof import("./restart-stale-pids.js").__testing;
|
||||
let cleanStaleGatewayProcessesSync: typeof import("./restart-stale-pids.js").cleanStaleGatewayProcessesSync;
|
||||
let findGatewayPidsOnPortSync: typeof import("./restart-stale-pids.js").findGatewayPidsOnPortSync;
|
||||
|
||||
let currentTimeMs = 0;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("node:child_process", () => ({
|
||||
spawnSync: (...args: unknown[]) => spawnSyncMock(...args),
|
||||
}));
|
||||
vi.doMock("./ports-lsof.js", () => ({
|
||||
resolveLsofCommandSync: (...args: unknown[]) => resolveLsofCommandSyncMock(...args),
|
||||
}));
|
||||
vi.doMock("../config/paths.js", () => ({
|
||||
resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args),
|
||||
}));
|
||||
({ __testing, cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } =
|
||||
await import("./restart-stale-pids.js"));
|
||||
spawnSyncMock.mockReset();
|
||||
resolveLsofCommandSyncMock.mockReset();
|
||||
resolveGatewayPortMock.mockReset();
|
||||
|
||||
@@ -29,43 +29,40 @@ const {
|
||||
discoverModelsMock,
|
||||
} = hoisted;
|
||||
|
||||
vi.mock("@mariozechner/pi-ai", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@mariozechner/pi-ai")>();
|
||||
return {
|
||||
...actual,
|
||||
complete: completeMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/minimax-vlm.js", () => ({
|
||||
isMinimaxVlmProvider: (provider: string) =>
|
||||
provider === "minimax" || provider === "minimax-portal",
|
||||
isMinimaxVlmModel: (provider: string, modelId: string) =>
|
||||
(provider === "minimax" || provider === "minimax-portal") && modelId === "MiniMax-VL-01",
|
||||
minimaxUnderstandImage: minimaxUnderstandImageMock,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/models-config.js", () => ({
|
||||
ensureOpenClawModelsJson: ensureOpenClawModelsJsonMock,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/model-auth.js", () => ({
|
||||
getApiKeyForModel: getApiKeyForModelMock,
|
||||
resolveApiKeyForProvider: resolveApiKeyForProviderMock,
|
||||
requireApiKey: requireApiKeyMock,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/pi-model-discovery-runtime.js", () => ({
|
||||
discoverAuthStorage: () => ({
|
||||
setRuntimeApiKey: setRuntimeApiKeyMock,
|
||||
}),
|
||||
discoverModels: discoverModelsMock,
|
||||
}));
|
||||
|
||||
const { describeImageWithModel } = await import("./image.js");
|
||||
let describeImageWithModel: typeof import("./image.js").describeImageWithModel;
|
||||
|
||||
describe("describeImageWithModel", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("@mariozechner/pi-ai", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@mariozechner/pi-ai")>();
|
||||
return {
|
||||
...actual,
|
||||
complete: completeMock,
|
||||
};
|
||||
});
|
||||
vi.doMock("../agents/minimax-vlm.js", () => ({
|
||||
isMinimaxVlmProvider: (provider: string) =>
|
||||
provider === "minimax" || provider === "minimax-portal",
|
||||
isMinimaxVlmModel: (provider: string, modelId: string) =>
|
||||
(provider === "minimax" || provider === "minimax-portal") && modelId === "MiniMax-VL-01",
|
||||
minimaxUnderstandImage: minimaxUnderstandImageMock,
|
||||
}));
|
||||
vi.doMock("../agents/models-config.js", () => ({
|
||||
ensureOpenClawModelsJson: ensureOpenClawModelsJsonMock,
|
||||
}));
|
||||
vi.doMock("../agents/model-auth.js", () => ({
|
||||
getApiKeyForModel: getApiKeyForModelMock,
|
||||
resolveApiKeyForProvider: resolveApiKeyForProviderMock,
|
||||
requireApiKey: requireApiKeyMock,
|
||||
}));
|
||||
vi.doMock("../agents/pi-model-discovery-runtime.js", () => ({
|
||||
discoverAuthStorage: () => ({
|
||||
setRuntimeApiKey: setRuntimeApiKeyMock,
|
||||
}),
|
||||
discoverModels: discoverModelsMock,
|
||||
}));
|
||||
({ describeImageWithModel } = await import("./image.js"));
|
||||
vi.clearAllMocks();
|
||||
minimaxUnderstandImageMock.mockResolvedValue("portal ok");
|
||||
discoverModelsMock.mockReturnValue({
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { MsgContext } from "../auto-reply/templating.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
buildProviderRegistry,
|
||||
createMediaAttachmentCache,
|
||||
normalizeMediaAttachments,
|
||||
runCapability,
|
||||
} from "./runner.js";
|
||||
|
||||
let buildProviderRegistry: typeof import("./runner.js").buildProviderRegistry;
|
||||
let createMediaAttachmentCache: typeof import("./runner.js").createMediaAttachmentCache;
|
||||
let normalizeMediaAttachments: typeof import("./runner.js").normalizeMediaAttachments;
|
||||
let runCapability: typeof import("./runner.js").runCapability;
|
||||
|
||||
const catalog = [
|
||||
{
|
||||
@@ -30,7 +29,23 @@ vi.mock("../agents/model-catalog.js", async () => {
|
||||
});
|
||||
|
||||
describe("runCapability image skip", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("../agents/model-catalog.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../agents/model-catalog.js")>(
|
||||
"../agents/model-catalog.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
loadModelCatalog,
|
||||
};
|
||||
});
|
||||
({
|
||||
buildProviderRegistry,
|
||||
createMediaAttachmentCache,
|
||||
normalizeMediaAttachments,
|
||||
runCapability,
|
||||
} = await import("./runner.js"));
|
||||
loadModelCatalog.mockClear();
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ describe("postJsonWithRetry", () => {
|
||||
let postJsonWithRetry: typeof import("./batch-http.js").postJsonWithRetry;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
vi.resetModules();
|
||||
({ postJsonWithRetry } = await import("./batch-http.js"));
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { postJson } from "./post-json.js";
|
||||
|
||||
vi.mock("./post-json.js", () => ({
|
||||
postJson: vi.fn(),
|
||||
}));
|
||||
const postJsonMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
type EmbeddingsRemoteFetchModule = typeof import("./embeddings-remote-fetch.js");
|
||||
|
||||
let fetchRemoteEmbeddingVectors: EmbeddingsRemoteFetchModule["fetchRemoteEmbeddingVectors"];
|
||||
|
||||
describe("fetchRemoteEmbeddingVectors", () => {
|
||||
const postJsonMock = vi.mocked(postJson);
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("./post-json.js", () => ({
|
||||
postJson: postJsonMock,
|
||||
}));
|
||||
({ fetchRemoteEmbeddingVectors } = await import("./embeddings-remote-fetch.js"));
|
||||
vi.clearAllMocks();
|
||||
postJsonMock.mockReset();
|
||||
});
|
||||
|
||||
it("maps remote embedding response data to vectors", async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ describe("postJson", () => {
|
||||
let remoteHttpMock: ReturnType<typeof vi.mocked<typeof withRemoteHttpResponse>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
vi.resetModules();
|
||||
({ postJson } = await import("./post-json.js"));
|
||||
|
||||
@@ -111,18 +111,16 @@ vi.mock("./runtime.js", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const {
|
||||
__testing,
|
||||
buildPluginBindingApprovalCustomId,
|
||||
detachPluginConversationBinding,
|
||||
getCurrentPluginConversationBinding,
|
||||
parsePluginBindingApprovalCustomId,
|
||||
requestPluginConversationBinding,
|
||||
resolvePluginConversationBindingApproval,
|
||||
} = await import("./conversation-binding.js");
|
||||
const { registerSessionBindingAdapter, unregisterSessionBindingAdapter } =
|
||||
await import("../infra/outbound/session-binding-service.js");
|
||||
const { setActivePluginRegistry } = await import("./runtime.js");
|
||||
let __testing: typeof import("./conversation-binding.js").__testing;
|
||||
let buildPluginBindingApprovalCustomId: typeof import("./conversation-binding.js").buildPluginBindingApprovalCustomId;
|
||||
let detachPluginConversationBinding: typeof import("./conversation-binding.js").detachPluginConversationBinding;
|
||||
let getCurrentPluginConversationBinding: typeof import("./conversation-binding.js").getCurrentPluginConversationBinding;
|
||||
let parsePluginBindingApprovalCustomId: typeof import("./conversation-binding.js").parsePluginBindingApprovalCustomId;
|
||||
let requestPluginConversationBinding: typeof import("./conversation-binding.js").requestPluginConversationBinding;
|
||||
let resolvePluginConversationBindingApproval: typeof import("./conversation-binding.js").resolvePluginConversationBindingApproval;
|
||||
let registerSessionBindingAdapter: typeof import("../infra/outbound/session-binding-service.js").registerSessionBindingAdapter;
|
||||
let unregisterSessionBindingAdapter: typeof import("../infra/outbound/session-binding-service.js").unregisterSessionBindingAdapter;
|
||||
let setActivePluginRegistry: typeof import("./runtime.js").setActivePluginRegistry;
|
||||
|
||||
type PluginBindingRequest = Awaited<ReturnType<typeof requestPluginConversationBinding>>;
|
||||
type ConversationBindingModule = typeof import("./conversation-binding.js");
|
||||
@@ -187,7 +185,38 @@ function createDeferredVoid(): { promise: Promise<void>; resolve: () => void } {
|
||||
}
|
||||
|
||||
describe("plugin conversation binding approvals", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("../infra/home-dir.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../infra/home-dir.js")>();
|
||||
return {
|
||||
...actual,
|
||||
expandHomePrefix: (value: string) => {
|
||||
if (value === "~/.openclaw/plugin-binding-approvals.json") {
|
||||
return approvalsPath;
|
||||
}
|
||||
return actual.expandHomePrefix(value);
|
||||
},
|
||||
};
|
||||
});
|
||||
vi.doMock("./runtime.js", () => ({
|
||||
getActivePluginRegistry: () => pluginRuntimeState.registry,
|
||||
setActivePluginRegistry: (registry: PluginRegistry) => {
|
||||
pluginRuntimeState.registry = registry;
|
||||
},
|
||||
}));
|
||||
({
|
||||
__testing,
|
||||
buildPluginBindingApprovalCustomId,
|
||||
detachPluginConversationBinding,
|
||||
getCurrentPluginConversationBinding,
|
||||
parsePluginBindingApprovalCustomId,
|
||||
requestPluginConversationBinding,
|
||||
resolvePluginConversationBindingApproval,
|
||||
} = await import("./conversation-binding.js"));
|
||||
({ registerSessionBindingAdapter, unregisterSessionBindingAdapter } =
|
||||
await import("../infra/outbound/session-binding-service.js"));
|
||||
({ setActivePluginRegistry } = await import("./runtime.js"));
|
||||
sessionBindingState.reset();
|
||||
__testing.reset();
|
||||
setActivePluginRegistry(createEmptyPluginRegistry());
|
||||
|
||||
@@ -20,17 +20,6 @@ const resolveOwningPluginIdsForProviderMock = vi.fn<ResolveOwningPluginIdsForPro
|
||||
(_) => undefined as string[] | undefined,
|
||||
);
|
||||
|
||||
vi.mock("./providers.js", () => ({
|
||||
resolveNonBundledProviderPluginIds: (params: unknown) =>
|
||||
resolveNonBundledProviderPluginIdsMock(params as never),
|
||||
resolveOwningPluginIdsForProvider: (params: unknown) =>
|
||||
resolveOwningPluginIdsForProviderMock(params as never),
|
||||
}));
|
||||
|
||||
vi.mock("./providers.runtime.js", () => ({
|
||||
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
|
||||
}));
|
||||
|
||||
let augmentModelCatalogWithProviderPlugins: typeof import("./provider-runtime.js").augmentModelCatalogWithProviderPlugins;
|
||||
let buildProviderAuthDoctorHintWithPlugin: typeof import("./provider-runtime.js").buildProviderAuthDoctorHintWithPlugin;
|
||||
let buildProviderMissingAuthMessageWithPlugin: typeof import("./provider-runtime.js").buildProviderMissingAuthMessageWithPlugin;
|
||||
@@ -70,6 +59,15 @@ const MODEL: ProviderRuntimeModel = {
|
||||
describe("provider-runtime", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("./providers.js", () => ({
|
||||
resolveNonBundledProviderPluginIds: (params: unknown) =>
|
||||
resolveNonBundledProviderPluginIdsMock(params as never),
|
||||
resolveOwningPluginIdsForProvider: (params: unknown) =>
|
||||
resolveOwningPluginIdsForProviderMock(params as never),
|
||||
}));
|
||||
vi.doMock("./providers.runtime.js", () => ({
|
||||
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
|
||||
}));
|
||||
({
|
||||
augmentModelCatalogWithProviderPlugins,
|
||||
buildProviderAuthDoctorHintWithPlugin,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { edgeTTS } from "./tts-core.js";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let edgeTTS: typeof import("./tts-core.js").edgeTTS;
|
||||
|
||||
let mockTtsPromise = vi.fn<(text: string, filePath: string) => Promise<void>>();
|
||||
|
||||
@@ -24,10 +25,25 @@ const baseEdgeConfig = {
|
||||
};
|
||||
|
||||
describe("edgeTTS – empty audio validation", () => {
|
||||
let tempDir: string;
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("node-edge-tts", () => ({
|
||||
EdgeTTS: class {
|
||||
ttsPromise(text: string, filePath: string) {
|
||||
return mockTtsPromise(text, filePath);
|
||||
}
|
||||
},
|
||||
}));
|
||||
({ edgeTTS } = await import("./tts-core.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
if (tempDir) {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
tempDir = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
it("throws when the output file is 0 bytes", async () => {
|
||||
|
||||
@@ -3,11 +3,6 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
||||
import {
|
||||
getSpeechProvider,
|
||||
listSpeechProviders,
|
||||
normalizeSpeechProviderId,
|
||||
} from "./provider-registry.js";
|
||||
|
||||
const loadOpenClawPluginsMock = vi.fn();
|
||||
|
||||
@@ -16,6 +11,10 @@ vi.mock("../plugins/loader.js", () => ({
|
||||
loadOpenClawPluginsMock(...args),
|
||||
}));
|
||||
|
||||
let getSpeechProvider: typeof import("./provider-registry.js").getSpeechProvider;
|
||||
let listSpeechProviders: typeof import("./provider-registry.js").listSpeechProviders;
|
||||
let normalizeSpeechProviderId: typeof import("./provider-registry.js").normalizeSpeechProviderId;
|
||||
|
||||
function createSpeechProvider(id: string, aliases?: string[]): SpeechProviderPlugin {
|
||||
return {
|
||||
id,
|
||||
@@ -32,10 +31,13 @@ function createSpeechProvider(id: string, aliases?: string[]): SpeechProviderPlu
|
||||
}
|
||||
|
||||
describe("speech provider registry", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
resetPluginRuntimeStateForTest();
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
loadOpenClawPluginsMock.mockReturnValue(createEmptyPluginRegistry());
|
||||
({ getSpeechProvider, listSpeechProviders, normalizeSpeechProviderId } =
|
||||
await import("./provider-registry.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { completeSimple, type AssistantMessage } from "@mariozechner/pi-ai";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildElevenLabsSpeechProvider } from "../../extensions/elevenlabs/speech-provider.ts";
|
||||
import { buildMicrosoftSpeechProvider } from "../../extensions/microsoft/speech-provider.ts";
|
||||
import { buildOpenAISpeechProvider } from "../../extensions/openai/speech-provider.ts";
|
||||
@@ -401,7 +401,44 @@ describe("tts", () => {
|
||||
messages: { tts: {} },
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("@mariozechner/pi-ai", async (importOriginal) => {
|
||||
const original = await importOriginal<typeof import("@mariozechner/pi-ai")>();
|
||||
return {
|
||||
...original,
|
||||
completeSimple: vi.fn(),
|
||||
};
|
||||
});
|
||||
vi.doMock("@mariozechner/pi-ai/oauth", async () => {
|
||||
const actual = await vi.importActual<typeof import("@mariozechner/pi-ai/oauth")>(
|
||||
"@mariozechner/pi-ai/oauth",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
getOAuthProviders: () => [],
|
||||
getOAuthApiKey: vi.fn(async () => null),
|
||||
};
|
||||
});
|
||||
vi.doMock("../agents/pi-embedded-runner/model.js", () => ({
|
||||
resolveModel: vi.fn((provider: string, modelId: string) =>
|
||||
createResolvedModel(provider, modelId),
|
||||
),
|
||||
resolveModelAsync: vi.fn(async (provider: string, modelId: string) =>
|
||||
createResolvedModel(provider, modelId),
|
||||
),
|
||||
}));
|
||||
vi.doMock("../agents/model-auth.js", () => ({
|
||||
getApiKeyForModel: vi.fn(async () => ({
|
||||
apiKey: "test-api-key",
|
||||
source: "test",
|
||||
mode: "api-key",
|
||||
})),
|
||||
requireApiKey: vi.fn((auth: { apiKey?: string }) => auth.apiKey ?? ""),
|
||||
}));
|
||||
vi.doMock("../agents/custom-api-registry.js", () => ({
|
||||
ensureCustomApiRegistered: vi.fn(),
|
||||
}));
|
||||
({ completeSimple: completeSimpleForTest } = await import("@mariozechner/pi-ai"));
|
||||
({ getApiKeyForModel: getApiKeyForModelForTest } = await import("../agents/model-auth.js"));
|
||||
({ resolveModelAsync: resolveModelAsyncForTest } =
|
||||
@@ -411,9 +448,6 @@ describe("tts", () => {
|
||||
const ttsModule = await import("./tts.js");
|
||||
summarizeTextForTest = ttsModule._test.summarizeText;
|
||||
resolveTtsConfigForTest = ttsModule.resolveTtsConfig;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(completeSimpleForTest).mockResolvedValue(
|
||||
mockAssistantMessage([{ type: "text", text: "Summary" }]),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user