mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
chore: Fix types in tests 23/N.
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { VoiceCallConfigSchema, type VoiceCallConfig } from "./config.js";
|
||||
import type { CallManager } from "./manager.js";
|
||||
import type { VoiceCallProvider } from "./providers/base.js";
|
||||
import type { CallRecord } from "./types.js";
|
||||
import { VoiceCallConfigSchema, type VoiceCallConfig } from "./config.js";
|
||||
import { VoiceCallWebhookServer } from "./webhook.js";
|
||||
|
||||
const provider: VoiceCallProvider = {
|
||||
name: "mock",
|
||||
verifyWebhook: () => ({ ok: true }),
|
||||
parseWebhookEvent: () => ({ events: [] }),
|
||||
initiateCall: async () => ({ providerCallId: "provider-call" }),
|
||||
initiateCall: async () => ({ providerCallId: "provider-call", status: "initiated" }),
|
||||
hangupCall: async () => {},
|
||||
playTts: async () => {},
|
||||
startListening: async () => {},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js";
|
||||
import {
|
||||
handleToolExecutionEnd,
|
||||
handleToolExecutionStart,
|
||||
} from "./pi-embedded-subscribe.handlers.tools.js";
|
||||
import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js";
|
||||
|
||||
// Minimal mock context factory. Only the fields needed for the media emission path.
|
||||
function createMockContext(overrides?: {
|
||||
|
||||
@@ -65,6 +65,8 @@ const { readAllowFromStoreMock, upsertPairingRequestMock } = vi.hoisted(() => ({
|
||||
|
||||
let handleLineWebhookEvents: typeof import("./bot-handlers.js").handleLineWebhookEvents;
|
||||
|
||||
const createRuntime = () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() });
|
||||
|
||||
vi.mock("../pairing/pairing-store.js", () => ({
|
||||
readChannelAllowFromStore: readAllowFromStoreMock,
|
||||
upsertChannelPairingRequest: upsertPairingRequestMock,
|
||||
@@ -105,7 +107,7 @@ describe("handleLineWebhookEvents", () => {
|
||||
tokenSource: "config",
|
||||
config: { groupPolicy: "disabled" },
|
||||
},
|
||||
runtime: { error: vi.fn() },
|
||||
runtime: createRuntime(),
|
||||
mediaMaxBytes: 1,
|
||||
processMessage,
|
||||
});
|
||||
@@ -137,7 +139,7 @@ describe("handleLineWebhookEvents", () => {
|
||||
tokenSource: "config",
|
||||
config: { groupPolicy: "allowlist" },
|
||||
},
|
||||
runtime: { error: vi.fn() },
|
||||
runtime: createRuntime(),
|
||||
mediaMaxBytes: 1,
|
||||
processMessage,
|
||||
});
|
||||
@@ -171,7 +173,7 @@ describe("handleLineWebhookEvents", () => {
|
||||
tokenSource: "config",
|
||||
config: { groupPolicy: "allowlist", groupAllowFrom: ["user-3"] },
|
||||
},
|
||||
runtime: { error: vi.fn() },
|
||||
runtime: createRuntime(),
|
||||
mediaMaxBytes: 1,
|
||||
processMessage,
|
||||
});
|
||||
@@ -203,7 +205,7 @@ describe("handleLineWebhookEvents", () => {
|
||||
tokenSource: "config",
|
||||
config: { groupPolicy: "open", groups: { "*": { enabled: false } } },
|
||||
},
|
||||
runtime: { error: vi.fn() },
|
||||
runtime: createRuntime(),
|
||||
mediaMaxBytes: 1,
|
||||
processMessage,
|
||||
});
|
||||
|
||||
@@ -8,24 +8,26 @@ const sign = (body: string, secret: string) =>
|
||||
|
||||
function createRes() {
|
||||
const headers: Record<string, string> = {};
|
||||
const res = {
|
||||
const resObj = {
|
||||
statusCode: 0,
|
||||
headersSent: false,
|
||||
setHeader: (k: string, v: string) => {
|
||||
headers[k.toLowerCase()] = v;
|
||||
},
|
||||
end: vi.fn((data?: unknown) => {
|
||||
res.headersSent = true;
|
||||
resObj.headersSent = true;
|
||||
// Keep payload available for assertions
|
||||
(res as { body?: unknown }).body = data;
|
||||
resObj.body = data;
|
||||
}),
|
||||
} as unknown as ServerResponse & { body?: unknown };
|
||||
body: undefined as unknown,
|
||||
};
|
||||
const res = resObj as unknown as ServerResponse & { body?: unknown };
|
||||
return { res, headers };
|
||||
}
|
||||
|
||||
function createPostWebhookTestHarness(rawBody: string, secret = "secret") {
|
||||
const bot = { handleWebhook: vi.fn(async () => {}) };
|
||||
const runtime = { error: vi.fn() };
|
||||
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
|
||||
const handler = createLineNodeWebhookHandler({
|
||||
channelSecret: secret,
|
||||
bot,
|
||||
@@ -38,7 +40,7 @@ function createPostWebhookTestHarness(rawBody: string, secret = "secret") {
|
||||
describe("createLineNodeWebhookHandler", () => {
|
||||
it("returns 200 for GET", async () => {
|
||||
const bot = { handleWebhook: vi.fn(async () => {}) };
|
||||
const runtime = { error: vi.fn() };
|
||||
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
|
||||
const handler = createLineNodeWebhookHandler({
|
||||
channelSecret: "secret",
|
||||
bot,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveEntriesWithActiveFallback, resolveModelEntries } from "./resolve.js";
|
||||
import type { MediaUnderstandingCapability } from "./types.js";
|
||||
|
||||
const providerRegistry = new Map([
|
||||
const providerRegistry = new Map<string, { capabilities: MediaUnderstandingCapability[] }>([
|
||||
["openai", { capabilities: ["image"] }],
|
||||
["groq", { capabilities: ["audio"] }],
|
||||
]);
|
||||
|
||||
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import { PassThrough } from "node:stream";
|
||||
import JSZip from "jszip";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createPinnedLookup } from "../infra/net/ssrf.js";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
import { saveMediaSource, setMediaStoreNetworkDepsForTest } from "./store.js";
|
||||
|
||||
@@ -24,8 +25,10 @@ describe("media store redirects", () => {
|
||||
setMediaStoreNetworkDepsForTest({
|
||||
httpRequest: (...args) => mockRequest(...args),
|
||||
httpsRequest: (...args) => mockRequest(...args),
|
||||
resolvePinnedHostname: async () => ({
|
||||
lookup: async () => [{ address: "93.184.216.34", family: 4 }],
|
||||
resolvePinnedHostname: async (hostname) => ({
|
||||
hostname,
|
||||
addresses: ["93.184.216.34"],
|
||||
lookup: createPinnedLookup({ hostname, addresses: ["93.184.216.34"] }),
|
||||
}),
|
||||
});
|
||||
});
|
||||
@@ -41,7 +44,10 @@ describe("media store redirects", () => {
|
||||
let call = 0;
|
||||
mockRequest.mockImplementation((_url, _opts, cb) => {
|
||||
call += 1;
|
||||
const res = new PassThrough();
|
||||
const res = Object.assign(new PassThrough(), {
|
||||
statusCode: 0,
|
||||
headers: {} as Record<string, string>,
|
||||
});
|
||||
const req = {
|
||||
on: (event: string, handler: (...args: unknown[]) => void) => {
|
||||
if (event === "error") {
|
||||
@@ -83,7 +89,10 @@ describe("media store redirects", () => {
|
||||
|
||||
it("sniffs xlsx from zip content when headers and url extension are missing", async () => {
|
||||
mockRequest.mockImplementationOnce((_url, _opts, cb) => {
|
||||
const res = new PassThrough();
|
||||
const res = Object.assign(new PassThrough(), {
|
||||
statusCode: 0,
|
||||
headers: {} as Record<string, string>,
|
||||
});
|
||||
const req = {
|
||||
on: (event: string, handler: (...args: unknown[]) => void) => {
|
||||
if (event === "error") {
|
||||
|
||||
@@ -104,7 +104,17 @@ describe("after_tool_call hook wiring", () => {
|
||||
expect(hookMocks.runner.runAfterToolCall).toHaveBeenCalledTimes(1);
|
||||
expect(hookMocks.runner.runBeforeToolCall).not.toHaveBeenCalled();
|
||||
|
||||
const [event, context] = hookMocks.runner.runAfterToolCall.mock.calls[0];
|
||||
const firstCall = (hookMocks.runner.runAfterToolCall as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
expect(firstCall).toBeDefined();
|
||||
const event = firstCall?.[0] as
|
||||
| { toolName?: string; params?: unknown; error?: unknown; durationMs?: unknown }
|
||||
| undefined;
|
||||
const context = firstCall?.[1] as { toolName?: string } | undefined;
|
||||
expect(event).toBeDefined();
|
||||
expect(context).toBeDefined();
|
||||
if (!event || !context) {
|
||||
throw new Error("missing hook call payload");
|
||||
}
|
||||
expect(event.toolName).toBe("read");
|
||||
expect(event.params).toEqual({ path: "/tmp/file.txt" });
|
||||
expect(event.error).toBeUndefined();
|
||||
@@ -143,7 +153,13 @@ describe("after_tool_call hook wiring", () => {
|
||||
|
||||
expect(hookMocks.runner.runAfterToolCall).toHaveBeenCalledTimes(1);
|
||||
|
||||
const [event] = hookMocks.runner.runAfterToolCall.mock.calls[0];
|
||||
const firstCall = (hookMocks.runner.runAfterToolCall as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
expect(firstCall).toBeDefined();
|
||||
const event = firstCall?.[0] as { error?: unknown } | undefined;
|
||||
expect(event).toBeDefined();
|
||||
if (!event) {
|
||||
throw new Error("missing hook call payload");
|
||||
}
|
||||
expect(event.error).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
@@ -38,18 +38,26 @@ function createMonitorRuntime() {
|
||||
}
|
||||
|
||||
function setSignalAutoStartConfig(overrides: Record<string, unknown> = {}) {
|
||||
setSignalToolResultTestConfig({
|
||||
...config,
|
||||
setSignalToolResultTestConfig(createSignalConfig(overrides));
|
||||
}
|
||||
|
||||
function createSignalConfig(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
const base = config as OpenClawConfig;
|
||||
const channels = (base.channels ?? {}) as Record<string, unknown>;
|
||||
const signal = (channels.signal ?? {}) as Record<string, unknown>;
|
||||
return {
|
||||
...base,
|
||||
channels: {
|
||||
...config.channels,
|
||||
...channels,
|
||||
signal: {
|
||||
...signal,
|
||||
autoStart: true,
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
...overrides,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function createAutoAbortController() {
|
||||
@@ -193,18 +201,9 @@ describe("monitorSignalProvider tool results", () => {
|
||||
});
|
||||
|
||||
it("replies with pairing code when dmPolicy is pairing and no allowFrom is set", async () => {
|
||||
setSignalToolResultTestConfig({
|
||||
...config,
|
||||
channels: {
|
||||
...config.channels,
|
||||
signal: {
|
||||
...config.channels?.signal,
|
||||
autoStart: false,
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
setSignalToolResultTestConfig(
|
||||
createSignalConfig({ autoStart: false, dmPolicy: "pairing", allowFrom: [] }),
|
||||
);
|
||||
await receiveSignalPayloads({
|
||||
payloads: [
|
||||
{
|
||||
@@ -277,19 +276,14 @@ describe("monitorSignalProvider tool results", () => {
|
||||
});
|
||||
|
||||
it("enqueues system events for reaction notifications", async () => {
|
||||
setSignalToolResultTestConfig({
|
||||
...config,
|
||||
channels: {
|
||||
...config.channels,
|
||||
signal: {
|
||||
...config.channels?.signal,
|
||||
autoStart: false,
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
reactionNotifications: "all",
|
||||
},
|
||||
},
|
||||
});
|
||||
setSignalToolResultTestConfig(
|
||||
createSignalConfig({
|
||||
autoStart: false,
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
reactionNotifications: "all",
|
||||
}),
|
||||
);
|
||||
await receiveSignalPayloads({
|
||||
payloads: [
|
||||
{
|
||||
@@ -312,20 +306,15 @@ describe("monitorSignalProvider tool results", () => {
|
||||
});
|
||||
|
||||
it("notifies on own reactions when target includes uuid + phone", async () => {
|
||||
setSignalToolResultTestConfig({
|
||||
...config,
|
||||
channels: {
|
||||
...config.channels,
|
||||
signal: {
|
||||
...config.channels?.signal,
|
||||
autoStart: false,
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
account: "+15550002222",
|
||||
reactionNotifications: "own",
|
||||
},
|
||||
},
|
||||
});
|
||||
setSignalToolResultTestConfig(
|
||||
createSignalConfig({
|
||||
autoStart: false,
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
account: "+15550002222",
|
||||
reactionNotifications: "own",
|
||||
}),
|
||||
);
|
||||
await receiveSignalPayloads({
|
||||
payloads: [
|
||||
{
|
||||
@@ -376,18 +365,9 @@ describe("monitorSignalProvider tool results", () => {
|
||||
});
|
||||
|
||||
it("does not resend pairing code when a request is already pending", async () => {
|
||||
setSignalToolResultTestConfig({
|
||||
...config,
|
||||
channels: {
|
||||
...config.channels,
|
||||
signal: {
|
||||
...config.channels?.signal,
|
||||
autoStart: false,
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
setSignalToolResultTestConfig(
|
||||
createSignalConfig({ autoStart: false, dmPolicy: "pairing", allowFrom: [] }),
|
||||
);
|
||||
upsertPairingRequestMock
|
||||
.mockResolvedValueOnce({ code: "PAIRCODE", created: true })
|
||||
.mockResolvedValueOnce({ code: "PAIRCODE", created: false });
|
||||
|
||||
@@ -6,12 +6,18 @@ import type {
|
||||
} from "../channels/plugins/types.js";
|
||||
import type { PluginRegistry } from "../plugins/registry.js";
|
||||
|
||||
export const createTestRegistry = (channels: PluginRegistry["channels"] = []): PluginRegistry => ({
|
||||
type TestChannelRegistration = {
|
||||
pluginId: string;
|
||||
plugin: unknown;
|
||||
source: string;
|
||||
};
|
||||
|
||||
export const createTestRegistry = (channels: TestChannelRegistration[] = []): PluginRegistry => ({
|
||||
plugins: [],
|
||||
tools: [],
|
||||
hooks: [],
|
||||
typedHooks: [],
|
||||
channels,
|
||||
channels: channels as unknown as PluginRegistry["channels"],
|
||||
providers: [],
|
||||
gatewayHandlers: {},
|
||||
httpHandlers: [],
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { completeSimple } from "@mariozechner/pi-ai";
|
||||
import { completeSimple, type AssistantMessage } from "@mariozechner/pi-ai";
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
import { getApiKeyForModel } from "../agents/model-auth.js";
|
||||
import { resolveModel } from "../agents/pi-embedded-runner/model.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { withEnv } from "../test-utils/env.js";
|
||||
import * as tts from "./tts.js";
|
||||
|
||||
@@ -54,12 +55,36 @@ const {
|
||||
resolveEdgeOutputFormat,
|
||||
} = _test;
|
||||
|
||||
const mockAssistantMessage = (content: AssistantMessage["content"]): AssistantMessage => ({
|
||||
role: "assistant",
|
||||
content,
|
||||
api: "openai-completions",
|
||||
provider: "openai",
|
||||
model: "gpt-4o-mini",
|
||||
usage: {
|
||||
input: 1,
|
||||
output: 1,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: 2,
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
total: 0,
|
||||
},
|
||||
},
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
describe("tts", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(completeSimple).mockResolvedValue({
|
||||
content: [{ type: "text", text: "Summary" }],
|
||||
});
|
||||
vi.mocked(completeSimple).mockResolvedValue(
|
||||
mockAssistantMessage([{ type: "text", text: "Summary" }]),
|
||||
);
|
||||
});
|
||||
|
||||
describe("isValidVoiceId", () => {
|
||||
@@ -165,7 +190,7 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
describe("resolveEdgeOutputFormat", () => {
|
||||
const baseCfg = {
|
||||
const baseCfg: OpenClawConfig = {
|
||||
agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } },
|
||||
messages: { tts: {} },
|
||||
};
|
||||
@@ -223,7 +248,7 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
describe("summarizeText", () => {
|
||||
const baseCfg = {
|
||||
const baseCfg: OpenClawConfig = {
|
||||
agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } },
|
||||
messages: { tts: {} },
|
||||
};
|
||||
@@ -231,9 +256,9 @@ describe("tts", () => {
|
||||
|
||||
it("summarizes text and returns result with metrics", async () => {
|
||||
const mockSummary = "This is a summarized version of the text.";
|
||||
vi.mocked(completeSimple).mockResolvedValue({
|
||||
content: [{ type: "text", text: mockSummary }],
|
||||
});
|
||||
vi.mocked(completeSimple).mockResolvedValue(
|
||||
mockAssistantMessage([{ type: "text", text: mockSummary }]),
|
||||
);
|
||||
|
||||
const longText = "A".repeat(2000);
|
||||
const result = await summarizeText({
|
||||
@@ -268,7 +293,7 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
it("uses summaryModel override when configured", async () => {
|
||||
const cfg = {
|
||||
const cfg: OpenClawConfig = {
|
||||
agents: { defaults: { model: { primary: "anthropic/claude-opus-4-5" } } },
|
||||
messages: { tts: { summaryModel: "openai/gpt-4.1-mini" } },
|
||||
};
|
||||
@@ -330,9 +355,7 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
it("throws error when no summary is returned", async () => {
|
||||
vi.mocked(completeSimple).mockResolvedValue({
|
||||
content: [],
|
||||
});
|
||||
vi.mocked(completeSimple).mockResolvedValue(mockAssistantMessage([]));
|
||||
|
||||
await expect(
|
||||
summarizeText({
|
||||
@@ -346,9 +369,9 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
it("throws error when summary content is empty", async () => {
|
||||
vi.mocked(completeSimple).mockResolvedValue({
|
||||
content: [{ type: "text", text: " " }],
|
||||
});
|
||||
vi.mocked(completeSimple).mockResolvedValue(
|
||||
mockAssistantMessage([{ type: "text", text: " " }]),
|
||||
);
|
||||
|
||||
await expect(
|
||||
summarizeText({
|
||||
@@ -363,7 +386,7 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
describe("getTtsProvider", () => {
|
||||
const baseCfg = {
|
||||
const baseCfg: OpenClawConfig = {
|
||||
agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } },
|
||||
messages: { tts: {} },
|
||||
};
|
||||
@@ -415,7 +438,7 @@ describe("tts", () => {
|
||||
});
|
||||
|
||||
describe("maybeApplyTtsToPayload", () => {
|
||||
const baseCfg = {
|
||||
const baseCfg: OpenClawConfig = {
|
||||
agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } },
|
||||
messages: {
|
||||
tts: {
|
||||
@@ -445,11 +468,11 @@ describe("tts", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const taggedCfg = {
|
||||
const taggedCfg: OpenClawConfig = {
|
||||
...baseCfg,
|
||||
messages: {
|
||||
...baseCfg.messages,
|
||||
tts: { ...baseCfg.messages.tts, auto: "tagged" },
|
||||
...baseCfg.messages!,
|
||||
tts: { ...baseCfg.messages!.tts, auto: "tagged" },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ vi.mock("./qr-image.js", () => ({
|
||||
|
||||
const { startWebLoginWithQr, waitForWebLogin } = await import("./login-qr.js");
|
||||
const { createWaSocket, waitForWaConnection, logoutWeb } = await import("./session.js");
|
||||
const createWaSocketMock = vi.mocked(createWaSocket);
|
||||
const waitForWaConnectionMock = vi.mocked(waitForWaConnection);
|
||||
const logoutWebMock = vi.mocked(logoutWeb);
|
||||
|
||||
describe("login-qr", () => {
|
||||
beforeEach(() => {
|
||||
@@ -44,7 +47,7 @@ describe("login-qr", () => {
|
||||
});
|
||||
|
||||
it("restarts login once on status 515 and completes", async () => {
|
||||
waitForWaConnection
|
||||
waitForWaConnectionMock
|
||||
.mockRejectedValueOnce({ output: { statusCode: 515 } })
|
||||
.mockResolvedValueOnce(undefined);
|
||||
|
||||
@@ -54,7 +57,7 @@ describe("login-qr", () => {
|
||||
const result = await waitForWebLogin({ timeoutMs: 5000 });
|
||||
|
||||
expect(result.connected).toBe(true);
|
||||
expect(createWaSocket).toHaveBeenCalledTimes(2);
|
||||
expect(logoutWeb).not.toHaveBeenCalled();
|
||||
expect(createWaSocketMock).toHaveBeenCalledTimes(2);
|
||||
expect(logoutWebMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -44,6 +44,9 @@ vi.mock("./session.js", () => {
|
||||
});
|
||||
|
||||
const { createWaSocket, waitForWaConnection, formatError } = await import("./session.js");
|
||||
const createWaSocketMock = vi.mocked(createWaSocket);
|
||||
const waitForWaConnectionMock = vi.mocked(waitForWaConnection);
|
||||
const formatErrorMock = vi.mocked(formatError);
|
||||
const { loginWeb } = await import("./login.js");
|
||||
|
||||
describe("loginWeb coverage", () => {
|
||||
@@ -57,27 +60,29 @@ describe("loginWeb coverage", () => {
|
||||
});
|
||||
|
||||
it("restarts once when WhatsApp requests code 515", async () => {
|
||||
waitForWaConnection
|
||||
waitForWaConnectionMock
|
||||
.mockRejectedValueOnce({ output: { statusCode: 515 } })
|
||||
.mockResolvedValueOnce(undefined);
|
||||
|
||||
const runtime = { log: vi.fn(), error: vi.fn() } as never;
|
||||
await loginWeb(false, waitForWaConnection as never, runtime);
|
||||
await loginWeb(false, waitForWaConnectionMock as never, runtime);
|
||||
|
||||
expect(createWaSocket).toHaveBeenCalledTimes(2);
|
||||
const firstSock = await createWaSocket.mock.results[0].value;
|
||||
expect(createWaSocketMock).toHaveBeenCalledTimes(2);
|
||||
const firstSock = await createWaSocketMock.mock.results[0]?.value;
|
||||
expect(firstSock.ws.close).toHaveBeenCalled();
|
||||
vi.runAllTimers();
|
||||
const secondSock = await createWaSocket.mock.results[1].value;
|
||||
const secondSock = await createWaSocketMock.mock.results[1]?.value;
|
||||
expect(secondSock.ws.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("clears creds and throws when logged out", async () => {
|
||||
waitForWaConnection.mockRejectedValueOnce({
|
||||
waitForWaConnectionMock.mockRejectedValueOnce({
|
||||
output: { statusCode: DisconnectReason.loggedOut },
|
||||
});
|
||||
|
||||
await expect(loginWeb(false, waitForWaConnection as never)).rejects.toThrow(/cache cleared/i);
|
||||
await expect(loginWeb(false, waitForWaConnectionMock as never)).rejects.toThrow(
|
||||
/cache cleared/i,
|
||||
);
|
||||
expect(rmMock).toHaveBeenCalledWith(authDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
@@ -85,10 +90,10 @@ describe("loginWeb coverage", () => {
|
||||
});
|
||||
|
||||
it("formats and rethrows generic errors", async () => {
|
||||
waitForWaConnection.mockRejectedValueOnce(new Error("boom"));
|
||||
await expect(loginWeb(false, waitForWaConnection as never)).rejects.toThrow(
|
||||
waitForWaConnectionMock.mockRejectedValueOnce(new Error("boom"));
|
||||
await expect(loginWeb(false, waitForWaConnectionMock as never)).rejects.toThrow(
|
||||
"formatted:Error: boom",
|
||||
);
|
||||
expect(formatError).toHaveBeenCalled();
|
||||
expect(formatErrorMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { baileys, getLastSocket, resetBaileysMocks, resetLoadConfigMock } from "
|
||||
|
||||
const { createWaSocket, formatError, logWebSelfId, waitForWaConnection } =
|
||||
await import("./session.js");
|
||||
const useMultiFileAuthStateMock = vi.mocked(baileys.useMultiFileAuthState);
|
||||
|
||||
function mockCredsJsonSpies(readContents: string) {
|
||||
const credsSuffix = path.join(".openclaw", "credentials", "whatsapp", "default", "creds.json");
|
||||
@@ -65,7 +66,7 @@ describe("web session", () => {
|
||||
expect(passedLogger?.level).toBe("silent");
|
||||
expect(typeof passedLogger?.trace).toBe("function");
|
||||
const sock = getLastSocket();
|
||||
const saveCreds = (await baileys.useMultiFileAuthState.mock.results[0].value).saveCreds;
|
||||
const saveCreds = (await useMultiFileAuthStateMock.mock.results[0]?.value)?.saveCreds;
|
||||
// trigger creds.update listener
|
||||
sock.ev.emit("creds.update", {});
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
@@ -145,7 +146,7 @@ describe("web session", () => {
|
||||
|
||||
await createWaSocket(false, false);
|
||||
const sock = getLastSocket();
|
||||
const saveCreds = (await baileys.useMultiFileAuthState.mock.results[0].value).saveCreds;
|
||||
const saveCreds = (await useMultiFileAuthStateMock.mock.results[0]?.value)?.saveCreds;
|
||||
|
||||
sock.ev.emit("creds.update", {});
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
@@ -170,7 +171,7 @@ describe("web session", () => {
|
||||
await gate;
|
||||
inFlight -= 1;
|
||||
});
|
||||
baileys.useMultiFileAuthState.mockResolvedValueOnce({
|
||||
useMultiFileAuthStateMock.mockResolvedValueOnce({
|
||||
state: { creds: {}, keys: {} },
|
||||
saveCreds,
|
||||
});
|
||||
@@ -184,7 +185,9 @@ describe("web session", () => {
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
expect(inFlight).toBe(1);
|
||||
|
||||
release?.();
|
||||
if (release) {
|
||||
release();
|
||||
}
|
||||
|
||||
// let both queued saves complete
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
@@ -207,7 +210,7 @@ describe("web session", () => {
|
||||
|
||||
await createWaSocket(false, false);
|
||||
const sock = getLastSocket();
|
||||
const saveCreds = (await baileys.useMultiFileAuthState.mock.results[0].value).saveCreds;
|
||||
const saveCreds = (await useMultiFileAuthStateMock.mock.results[0]?.value)?.saveCreds;
|
||||
|
||||
sock.ev.emit("creds.update", {});
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
|
||||
Reference in New Issue
Block a user