mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-24 16:32:29 +00:00
test: harden channel suite isolation
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
|
||||
import { fetchDiscord } from "./api.js";
|
||||
import { jsonResponse } from "./test-http-helpers.js";
|
||||
|
||||
describe("fetchDiscord", () => {
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("formats rate limit payloads without raw JSON", async () => {
|
||||
const fetcher = withFetchPreconnect(async () =>
|
||||
jsonResponse(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { PluginRuntime } from "../../../src/plugins/runtime/types.js";
|
||||
import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js";
|
||||
import type { ResolvedDiscordAccount } from "./accounts.js";
|
||||
import { discordPlugin } from "./channel.js";
|
||||
import type { OpenClawConfig } from "./runtime-api.js";
|
||||
import { setDiscordRuntime } from "./runtime.js";
|
||||
let discordPlugin: typeof import("./channel.js").discordPlugin;
|
||||
let setDiscordRuntime: typeof import("./runtime.js").setDiscordRuntime;
|
||||
|
||||
const probeDiscordMock = vi.hoisted(() => vi.fn());
|
||||
const monitorDiscordProviderMock = vi.hoisted(() => vi.fn());
|
||||
@@ -75,6 +75,13 @@ afterEach(() => {
|
||||
auditDiscordChannelPermissionsMock.mockReset();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.useRealTimers();
|
||||
vi.resetModules();
|
||||
({ discordPlugin } = await import("./channel.js"));
|
||||
({ setDiscordRuntime } = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
describe("discordPlugin outbound", () => {
|
||||
it("forwards mediaLocalRoots to sendMessageDiscord", async () => {
|
||||
const sendMessageDiscord = vi.fn(async () => ({ messageId: "m1" }));
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import { MessageFlags } from "discord-api-types/v10";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
clearDiscordComponentEntries,
|
||||
registerDiscordComponentEntries,
|
||||
resolveDiscordComponentEntry,
|
||||
resolveDiscordModalEntry,
|
||||
} from "./components-registry.js";
|
||||
import {
|
||||
buildDiscordComponentMessage,
|
||||
buildDiscordComponentMessageFlags,
|
||||
readDiscordComponentSpec,
|
||||
} from "./components.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let clearDiscordComponentEntries: typeof import("./components-registry.js").clearDiscordComponentEntries;
|
||||
let registerDiscordComponentEntries: typeof import("./components-registry.js").registerDiscordComponentEntries;
|
||||
let resolveDiscordComponentEntry: typeof import("./components-registry.js").resolveDiscordComponentEntry;
|
||||
let resolveDiscordModalEntry: typeof import("./components-registry.js").resolveDiscordModalEntry;
|
||||
let buildDiscordComponentMessage: typeof import("./components.js").buildDiscordComponentMessage;
|
||||
let buildDiscordComponentMessageFlags: typeof import("./components.js").buildDiscordComponentMessageFlags;
|
||||
let readDiscordComponentSpec: typeof import("./components.js").readDiscordComponentSpec;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({
|
||||
clearDiscordComponentEntries,
|
||||
registerDiscordComponentEntries,
|
||||
resolveDiscordComponentEntry,
|
||||
resolveDiscordModalEntry,
|
||||
} = await import("./components-registry.js"));
|
||||
({ buildDiscordComponentMessage, buildDiscordComponentMessageFlags, readDiscordComponentSpec } =
|
||||
await import("./components.js"));
|
||||
});
|
||||
|
||||
describe("discord components", () => {
|
||||
it("builds v2 containers with modal trigger", () => {
|
||||
|
||||
@@ -61,6 +61,11 @@ function createAutoThreadMentionContext() {
|
||||
return { guildInfo, channelConfig };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
readAllowFromStoreMock.mockReset().mockResolvedValue([]);
|
||||
});
|
||||
|
||||
describe("registerDiscordListener", () => {
|
||||
class FakeListener {}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Client } from "@buape/carbon";
|
||||
import { ChannelType, MessageType } from "@buape/carbon";
|
||||
import { MessageType } from "@buape/carbon";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
dispatchMock,
|
||||
@@ -9,13 +9,19 @@ import {
|
||||
updateLastRouteMock,
|
||||
upsertPairingRequestMock,
|
||||
} from "./monitor.tool-result.test-harness.js";
|
||||
import { createDiscordMessageHandler } from "./monitor/message-handler.js";
|
||||
import { __resetDiscordChannelInfoCacheForTest } from "./monitor/message-utils.js";
|
||||
import { createNoopThreadBindingManager } from "./monitor/thread-bindings.js";
|
||||
|
||||
type Config = ReturnType<typeof import("../../../src/config/config.js").loadConfig>;
|
||||
let ChannelType: typeof import("@buape/carbon").ChannelType;
|
||||
let createDiscordMessageHandler: typeof import("./monitor/message-handler.js").createDiscordMessageHandler;
|
||||
let __resetDiscordChannelInfoCacheForTest: typeof import("./monitor/message-utils.js").__resetDiscordChannelInfoCacheForTest;
|
||||
let createNoopThreadBindingManager: typeof import("./monitor/thread-bindings.js").createNoopThreadBindingManager;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ ChannelType } = await import("@buape/carbon"));
|
||||
({ createDiscordMessageHandler } = await import("./monitor/message-handler.js"));
|
||||
({ __resetDiscordChannelInfoCacheForTest } = await import("./monitor/message-utils.js"));
|
||||
({ createNoopThreadBindingManager } = await import("./monitor/thread-bindings.js"));
|
||||
__resetDiscordChannelInfoCacheForTest();
|
||||
sendMock.mockClear().mockResolvedValue(undefined);
|
||||
updateLastRouteMock.mockClear();
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildDiscordComponentCustomId, buildDiscordModalCustomId } from "../components.js";
|
||||
import {
|
||||
createDiscordComponentButton,
|
||||
createDiscordComponentChannelSelect,
|
||||
createDiscordComponentMentionableSelect,
|
||||
createDiscordComponentModal,
|
||||
createDiscordComponentRoleSelect,
|
||||
createDiscordComponentStringSelect,
|
||||
createDiscordComponentUserSelect,
|
||||
} from "./agent-components.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let buildDiscordComponentCustomId: typeof import("../components.js").buildDiscordComponentCustomId;
|
||||
let buildDiscordModalCustomId: typeof import("../components.js").buildDiscordModalCustomId;
|
||||
let createDiscordComponentButton: typeof import("./agent-components.js").createDiscordComponentButton;
|
||||
let createDiscordComponentChannelSelect: typeof import("./agent-components.js").createDiscordComponentChannelSelect;
|
||||
let createDiscordComponentMentionableSelect: typeof import("./agent-components.js").createDiscordComponentMentionableSelect;
|
||||
let createDiscordComponentModal: typeof import("./agent-components.js").createDiscordComponentModal;
|
||||
let createDiscordComponentRoleSelect: typeof import("./agent-components.js").createDiscordComponentRoleSelect;
|
||||
let createDiscordComponentStringSelect: typeof import("./agent-components.js").createDiscordComponentStringSelect;
|
||||
let createDiscordComponentUserSelect: typeof import("./agent-components.js").createDiscordComponentUserSelect;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ buildDiscordComponentCustomId, buildDiscordModalCustomId } = await import("../components.js"));
|
||||
({
|
||||
createDiscordComponentButton,
|
||||
createDiscordComponentChannelSelect,
|
||||
createDiscordComponentMentionableSelect,
|
||||
createDiscordComponentModal,
|
||||
createDiscordComponentRoleSelect,
|
||||
createDiscordComponentStringSelect,
|
||||
createDiscordComponentUserSelect,
|
||||
} = await import("./agent-components.js"));
|
||||
});
|
||||
|
||||
type WildcardComponent = {
|
||||
customId: string;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { DiscordMessageListener } from "./listeners.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let DiscordMessageListener: typeof import("./listeners.js").DiscordMessageListener;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ DiscordMessageListener } = await import("./listeners.js"));
|
||||
});
|
||||
|
||||
function createLogger() {
|
||||
return {
|
||||
|
||||
@@ -10,11 +10,6 @@ import {
|
||||
__testing as sessionBindingTesting,
|
||||
registerSessionBindingAdapter,
|
||||
} from "../../../../src/infra/outbound/session-binding-service.js";
|
||||
import {
|
||||
preflightDiscordMessage,
|
||||
resolvePreflightMentionRequirement,
|
||||
shouldIgnoreBoundThreadWebhookMessage,
|
||||
} from "./message-handler.preflight.js";
|
||||
import {
|
||||
createDiscordMessage,
|
||||
createDiscordPreflightArgs,
|
||||
@@ -25,10 +20,22 @@ import {
|
||||
type DiscordConfig,
|
||||
type DiscordMessageEvent,
|
||||
} from "./message-handler.preflight.test-helpers.js";
|
||||
import {
|
||||
__testing as threadBindingTesting,
|
||||
createThreadBindingManager,
|
||||
} from "./thread-bindings.js";
|
||||
let preflightDiscordMessage: typeof import("./message-handler.preflight.js").preflightDiscordMessage;
|
||||
let resolvePreflightMentionRequirement: typeof import("./message-handler.preflight.js").resolvePreflightMentionRequirement;
|
||||
let shouldIgnoreBoundThreadWebhookMessage: typeof import("./message-handler.preflight.js").shouldIgnoreBoundThreadWebhookMessage;
|
||||
let threadBindingTesting: typeof import("./thread-bindings.js").__testing;
|
||||
let createThreadBindingManager: typeof import("./thread-bindings.js").createThreadBindingManager;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({
|
||||
preflightDiscordMessage,
|
||||
resolvePreflightMentionRequirement,
|
||||
shouldIgnoreBoundThreadWebhookMessage,
|
||||
} = await import("./message-handler.preflight.js"));
|
||||
({ __testing: threadBindingTesting, createThreadBindingManager } =
|
||||
await import("./thread-bindings.js"));
|
||||
});
|
||||
|
||||
function createThreadBinding(
|
||||
overrides?: Partial<
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createDiscordMessageHandler,
|
||||
preflightDiscordMessageMock,
|
||||
processDiscordMessageMock,
|
||||
} from "./message-handler.module-test-helpers.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createDiscordHandlerParams,
|
||||
createDiscordPreflightContext,
|
||||
} from "./message-handler.test-helpers.js";
|
||||
let createDiscordMessageHandler: typeof import("./message-handler.module-test-helpers.js").createDiscordMessageHandler;
|
||||
let preflightDiscordMessageMock: typeof import("./message-handler.module-test-helpers.js").preflightDiscordMessageMock;
|
||||
let processDiscordMessageMock: typeof import("./message-handler.module-test-helpers.js").processDiscordMessageMock;
|
||||
|
||||
const eventualReplyDeliveredMock = vi.hoisted(() => vi.fn());
|
||||
type SetStatusFn = (patch: Record<string, unknown>) => void;
|
||||
@@ -86,6 +84,12 @@ async function createLifecycleStopScenario(params: {
|
||||
}
|
||||
|
||||
describe("createDiscordMessageHandler queue behavior", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ createDiscordMessageHandler, preflightDiscordMessageMock, processDiscordMessageMock } =
|
||||
await import("./message-handler.module-test-helpers.js"));
|
||||
});
|
||||
|
||||
it("resets busy counters when the handler is created", () => {
|
||||
preflightDiscordMessageMock.mockReset();
|
||||
processDiscordMessageMock.mockReset();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { listNativeCommandSpecs } from "../../../../src/auto-reply/commands-registry.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig, loadConfig } from "../../../../src/config/config.js";
|
||||
import { createDiscordNativeCommand } from "./native-command.js";
|
||||
import { createNoopThreadBindingManager } from "./thread-bindings.js";
|
||||
let listNativeCommandSpecs: typeof import("../../../../src/auto-reply/commands-registry.js").listNativeCommandSpecs;
|
||||
let createDiscordNativeCommand: typeof import("./native-command.js").createDiscordNativeCommand;
|
||||
let createNoopThreadBindingManager: typeof import("./thread-bindings.js").createNoopThreadBindingManager;
|
||||
|
||||
function createNativeCommand(name: string): ReturnType<typeof createDiscordNativeCommand> {
|
||||
function createNativeCommand(
|
||||
name: string,
|
||||
): ReturnType<typeof import("./native-command.js").createDiscordNativeCommand> {
|
||||
const command = listNativeCommandSpecs({ provider: "discord" }).find(
|
||||
(entry) => entry.name === name,
|
||||
);
|
||||
@@ -24,17 +26,19 @@ function createNativeCommand(name: string): ReturnType<typeof createDiscordNativ
|
||||
});
|
||||
}
|
||||
|
||||
type CommandOption = NonNullable<ReturnType<typeof createDiscordNativeCommand>["options"]>[number];
|
||||
type CommandOption = NonNullable<
|
||||
ReturnType<typeof import("./native-command.js").createDiscordNativeCommand>["options"]
|
||||
>[number];
|
||||
|
||||
function findOption(
|
||||
command: ReturnType<typeof createDiscordNativeCommand>,
|
||||
command: ReturnType<typeof import("./native-command.js").createDiscordNativeCommand>,
|
||||
name: string,
|
||||
): CommandOption | undefined {
|
||||
return command.options?.find((entry) => entry.name === name);
|
||||
}
|
||||
|
||||
function requireOption(
|
||||
command: ReturnType<typeof createDiscordNativeCommand>,
|
||||
command: ReturnType<typeof import("./native-command.js").createDiscordNativeCommand>,
|
||||
name: string,
|
||||
): CommandOption {
|
||||
const option = findOption(command, name);
|
||||
@@ -60,6 +64,13 @@ function readChoices(option: CommandOption | undefined): unknown[] | undefined {
|
||||
}
|
||||
|
||||
describe("createDiscordNativeCommand option wiring", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ listNativeCommandSpecs } = await import("../../../../src/auto-reply/commands-registry.js"));
|
||||
({ createDiscordNativeCommand } = await import("./native-command.js"));
|
||||
({ createNoopThreadBindingManager } = await import("./thread-bindings.js"));
|
||||
});
|
||||
|
||||
it("uses autocomplete for /acp action so inline action values are accepted", async () => {
|
||||
const command = createNativeCommand("acp");
|
||||
const action = requireOption(command, "action");
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { __testing } from "./provider.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let __testing: typeof import("./provider.js").__testing;
|
||||
|
||||
describe("resolveThreadBindingsEnabled", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ __testing } = await import("./provider.js"));
|
||||
});
|
||||
|
||||
it("defaults to enabled when unset", () => {
|
||||
expect(
|
||||
__testing.resolveThreadBindingsEnabled({
|
||||
|
||||
@@ -34,11 +34,14 @@ vi.mock("../send.js", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
const { maybeSendBindingMessage, resolveChannelIdForBinding } =
|
||||
await import("./thread-bindings.discord-api.js");
|
||||
let maybeSendBindingMessage: typeof import("./thread-bindings.discord-api.js").maybeSendBindingMessage;
|
||||
let resolveChannelIdForBinding: typeof import("./thread-bindings.discord-api.js").resolveChannelIdForBinding;
|
||||
|
||||
describe("resolveChannelIdForBinding", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ maybeSendBindingMessage, resolveChannelIdForBinding } =
|
||||
await import("./thread-bindings.discord-api.js"));
|
||||
hoisted.restGet.mockClear();
|
||||
hoisted.createDiscordRestClient.mockClear();
|
||||
hoisted.sendMessageDiscord.mockClear().mockResolvedValue({});
|
||||
@@ -124,7 +127,10 @@ describe("resolveChannelIdForBinding", () => {
|
||||
});
|
||||
|
||||
describe("maybeSendBindingMessage", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ maybeSendBindingMessage, resolveChannelIdForBinding } =
|
||||
await import("./thread-bindings.discord-api.js"));
|
||||
hoisted.sendMessageDiscord.mockClear().mockResolvedValue({});
|
||||
hoisted.sendWebhookMessageDiscord.mockClear().mockResolvedValue({});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
const { closeDiscordThreadSessions } = await import("./thread-session-close.js");
|
||||
let closeDiscordThreadSessions: typeof import("./thread-session-close.js").closeDiscordThreadSessions;
|
||||
|
||||
function setupStore(store: Record<string, { updatedAt: number }>) {
|
||||
hoisted.updateSessionStore.mockImplementation(
|
||||
@@ -30,7 +30,9 @@ const MATCHED_KEY = `agent:main:discord:channel:${THREAD_ID}`;
|
||||
const UNMATCHED_KEY = `agent:main:discord:channel:${OTHER_ID}`;
|
||||
|
||||
describe("closeDiscordThreadSessions", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ closeDiscordThreadSessions } = await import("./thread-session-close.js"));
|
||||
hoisted.updateSessionStore.mockClear();
|
||||
hoisted.resolveStorePath.mockClear();
|
||||
hoisted.resolveStorePath.mockReturnValue("/tmp/openclaw-sessions.json");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { normalizeDiscordOutboundTarget } from "./normalize.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
const sendMessageDiscordMock = vi.fn();
|
||||
@@ -37,7 +36,14 @@ vi.mock("./monitor/thread-bindings.js", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
const { discordOutbound } = await import("./outbound-adapter.js");
|
||||
let normalizeDiscordOutboundTarget: typeof import("./normalize.js").normalizeDiscordOutboundTarget;
|
||||
let discordOutbound: typeof import("./outbound-adapter.js").discordOutbound;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ normalizeDiscordOutboundTarget } = await import("./normalize.js"));
|
||||
({ discordOutbound } = await import("./outbound-adapter.js"));
|
||||
});
|
||||
|
||||
const DEFAULT_DISCORD_SEND_RESULT = {
|
||||
channel: "discord",
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { MsgContext } from "../../../../src/auto-reply/templating.js";
|
||||
import { expectChannelInboundContextContract as expectInboundContextContract } from "../../../../src/channels/plugins/contracts/suites.js";
|
||||
import {
|
||||
createBaseSignalEventHandlerDeps,
|
||||
createSignalReceiveEvent,
|
||||
} from "./event-handler.test-harness.js";
|
||||
let expectInboundContextContract: typeof import("../../../../src/channels/plugins/contracts/suites.js").expectChannelInboundContextContract;
|
||||
let createBaseSignalEventHandlerDeps: typeof import("./event-handler.test-harness.js").createBaseSignalEventHandlerDeps;
|
||||
let createSignalReceiveEvent: typeof import("./event-handler.test-harness.js").createSignalReceiveEvent;
|
||||
|
||||
const { sendTypingMock, sendReadReceiptMock, dispatchInboundMessageMock, capture } = vi.hoisted(
|
||||
() => {
|
||||
@@ -52,7 +50,12 @@ let createSignalEventHandler: typeof import("./event-handler.js").createSignalEv
|
||||
|
||||
describe("signal createSignalEventHandler inbound context", () => {
|
||||
beforeEach(async () => {
|
||||
vi.useRealTimers();
|
||||
vi.resetModules();
|
||||
({ expectChannelInboundContextContract: expectInboundContextContract } =
|
||||
await import("../../../../src/channels/plugins/contracts/suites.js"));
|
||||
({ createBaseSignalEventHandlerDeps, createSignalReceiveEvent } =
|
||||
await import("./event-handler.test-harness.js"));
|
||||
({ createSignalEventHandler } = await import("./event-handler.js"));
|
||||
capture.ctx = undefined;
|
||||
sendTypingMock.mockReset().mockResolvedValue(true);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
stopSlackMonitor,
|
||||
} from "./monitor.test-helpers.js";
|
||||
|
||||
const { monitorSlackProvider } = await import("./monitor.js");
|
||||
let monitorSlackProvider: typeof import("./monitor.js").monitorSlackProvider;
|
||||
|
||||
const slackTestState = getSlackTestState();
|
||||
|
||||
@@ -69,6 +69,12 @@ async function runMissingThreadScenario(params: {
|
||||
|
||||
beforeEach(() => {
|
||||
resetInboundDedupe();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ monitorSlackProvider } = await import("./monitor.js"));
|
||||
resetInboundDedupe();
|
||||
resetSlackTestState({
|
||||
messages: { responsePrefix: "PFX" },
|
||||
channels: {
|
||||
|
||||
@@ -50,7 +50,7 @@ vi.mock("./message-handler/dispatch.js", () => ({
|
||||
dispatchPreparedSlackMessageMock(prepared),
|
||||
}));
|
||||
|
||||
import { createSlackMessageHandler } from "./message-handler.js";
|
||||
let createSlackMessageHandler: typeof import("./message-handler.js").createSlackMessageHandler;
|
||||
|
||||
function createMarkMessageSeen() {
|
||||
const seen = new Set<string>();
|
||||
@@ -117,7 +117,9 @@ async function createInFlightMessageScenario(ts: string) {
|
||||
}
|
||||
|
||||
describe("createSlackMessageHandler app_mention race handling", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ createSlackMessageHandler } = await import("./message-handler.js"));
|
||||
prepareSlackMessageMock.mockReset();
|
||||
dispatchPreparedSlackMessageMock.mockReset();
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ vi.mock("../send.js", () => ({
|
||||
sendMessageSlack: (...args: unknown[]) => sendMock(...args),
|
||||
}));
|
||||
|
||||
import { deliverReplies } from "./replies.js";
|
||||
let deliverReplies: typeof import("./replies.js").deliverReplies;
|
||||
|
||||
function baseParams(overrides?: Record<string, unknown>) {
|
||||
return {
|
||||
@@ -20,7 +20,9 @@ function baseParams(overrides?: Record<string, unknown>) {
|
||||
}
|
||||
|
||||
describe("deliverReplies identity passthrough", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ deliverReplies } = await import("./replies.js"));
|
||||
sendMock.mockReset();
|
||||
});
|
||||
it("passes identity to sendMessageSlack for text replies", async () => {
|
||||
|
||||
@@ -194,7 +194,9 @@ async function loadRegisterSlackMonitorSlashCommands(): Promise<RegisterFn> {
|
||||
|
||||
const { dispatchMock } = getSlackSlashMocks();
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
registerSlackMonitorSlashCommandsPromise = undefined;
|
||||
resetSlackSlashMocks();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { API_CONSTANTS } from "grammy";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_TELEGRAM_UPDATE_TYPES, resolveTelegramAllowedUpdates } from "./allowed-updates.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
let API_CONSTANTS: typeof import("grammy").API_CONSTANTS;
|
||||
let DEFAULT_TELEGRAM_UPDATE_TYPES: typeof import("./allowed-updates.js").DEFAULT_TELEGRAM_UPDATE_TYPES;
|
||||
let resolveTelegramAllowedUpdates: typeof import("./allowed-updates.js").resolveTelegramAllowedUpdates;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ API_CONSTANTS } = await import("grammy"));
|
||||
({ DEFAULT_TELEGRAM_UPDATE_TYPES, resolveTelegramAllowedUpdates } =
|
||||
await import("./allowed-updates.js"));
|
||||
});
|
||||
|
||||
describe("resolveTelegramAllowedUpdates", () => {
|
||||
it("includes the default update types plus reaction and channel post support", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let collectTelegramUnmentionedGroupIds: typeof import("./audit.js").collectTelegramUnmentionedGroupIds;
|
||||
let auditTelegramGroupMembership: typeof import("./audit.js").auditTelegramGroupMembership;
|
||||
@@ -31,12 +31,10 @@ async function auditSingleGroup() {
|
||||
}
|
||||
|
||||
describe("telegram audit", () => {
|
||||
beforeAll(async () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ collectTelegramUnmentionedGroupIds, auditTelegramGroupMembership } =
|
||||
await import("./audit.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fetchWithTimeoutMock.mockReset();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
clearRuntimeConfigSnapshot,
|
||||
setRuntimeConfigSnapshot,
|
||||
} from "../../../src/config/config.js";
|
||||
import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let buildTelegramMessageContextForTest: typeof import("./bot-message-context.test-harness.js").buildTelegramMessageContextForTest;
|
||||
let clearRuntimeConfigSnapshot: typeof import("../../../src/config/config.js").clearRuntimeConfigSnapshot;
|
||||
let setRuntimeConfigSnapshot: typeof import("../../../src/config/config.js").setRuntimeConfigSnapshot;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ buildTelegramMessageContextForTest } = await import("./bot-message-context.test-harness.js"));
|
||||
({ clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } =
|
||||
await import("../../../src/config/config.js"));
|
||||
clearRuntimeConfigSnapshot();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
});
|
||||
|
||||
describe("buildTelegramMessageContext dm thread sessions", () => {
|
||||
const buildContext = async (message: Record<string, unknown>) =>
|
||||
@@ -110,10 +121,6 @@ describe("buildTelegramMessageContext group sessions without forum", () => {
|
||||
});
|
||||
|
||||
describe("buildTelegramMessageContext direct peer routing", () => {
|
||||
afterEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
});
|
||||
|
||||
it("isolates dm sessions by sender id when chat id differs", async () => {
|
||||
const runtimeCfg = {
|
||||
agents: { defaults: { model: "anthropic/claude-opus-4-5", workspace: "/tmp/openclaw" } },
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { vi } from "vitest";
|
||||
import {
|
||||
buildTelegramMessageContext,
|
||||
type BuildTelegramMessageContextParams,
|
||||
type TelegramMediaRef,
|
||||
} from "./bot-message-context.js";
|
||||
import type { BuildTelegramMessageContextParams, TelegramMediaRef } from "./bot-message-context.js";
|
||||
|
||||
export const baseTelegramMessageContextConfig = {
|
||||
agents: { defaults: { model: "anthropic/claude-opus-4-5", workspace: "/tmp/openclaw" } },
|
||||
@@ -24,7 +20,10 @@ type BuildTelegramMessageContextForTestParams = {
|
||||
|
||||
export async function buildTelegramMessageContextForTest(
|
||||
params: BuildTelegramMessageContextForTestParams,
|
||||
): Promise<Awaited<ReturnType<typeof buildTelegramMessageContext>>> {
|
||||
): Promise<
|
||||
Awaited<ReturnType<typeof import("./bot-message-context.js").buildTelegramMessageContext>>
|
||||
> {
|
||||
const { buildTelegramMessageContext } = await import("./bot-message-context.js");
|
||||
return await buildTelegramMessageContext({
|
||||
primaryCtx: {
|
||||
message: {
|
||||
|
||||
@@ -33,7 +33,6 @@ vi.mock("./bot/delivery.replies.js", () => ({
|
||||
|
||||
let registerTelegramNativeCommands: typeof import("./bot-native-commands.js").registerTelegramNativeCommands;
|
||||
import {
|
||||
createCommandBot,
|
||||
createNativeCommandTestParams as createNativeCommandTestParamsBase,
|
||||
createPrivateCommandContext,
|
||||
deliverReplies as registeredDeliverReplies,
|
||||
@@ -80,6 +79,12 @@ function createNativeCommandTestParams(
|
||||
});
|
||||
}
|
||||
|
||||
function resolveDeliverRepliesCalls() {
|
||||
return deliveryMocks.deliverReplies.mock.calls.length > 0
|
||||
? deliveryMocks.deliverReplies.mock.calls
|
||||
: registeredDeliverReplies.mock.calls;
|
||||
}
|
||||
|
||||
describe("registerTelegramNativeCommands", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
@@ -270,7 +275,8 @@ describe("registerTelegramNativeCommands", () => {
|
||||
expect(handler).toBeTruthy();
|
||||
await handler?.(createPrivateCommandContext());
|
||||
|
||||
expect(registeredDeliverReplies).toHaveBeenCalledWith(
|
||||
const firstDeliverRepliesCall = resolveDeliverRepliesCalls().at(0) as [unknown] | undefined;
|
||||
expect(firstDeliverRepliesCall?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
mediaLocalRoots: expect.arrayContaining([
|
||||
expect.stringMatching(/[\\/]\.openclaw[\\/]workspace-work$/),
|
||||
@@ -324,7 +330,8 @@ describe("registerTelegramNativeCommands", () => {
|
||||
expect(handler).toBeTruthy();
|
||||
await handler?.(createPrivateCommandContext());
|
||||
|
||||
expect(registeredDeliverReplies).toHaveBeenCalledWith(
|
||||
const firstDeliverRepliesCall = resolveDeliverRepliesCalls().at(0) as [unknown] | undefined;
|
||||
expect(firstDeliverRepliesCall?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
silent: true,
|
||||
replies: [expect.objectContaining({ isError: true })],
|
||||
|
||||
@@ -11,18 +11,9 @@ const {
|
||||
telegramBotRuntimeForTest,
|
||||
} = harness;
|
||||
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
|
||||
const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
let createTelegramBot: (
|
||||
opts: Parameters<typeof import("./bot.js").createTelegramBot>[0],
|
||||
) => ReturnType<typeof import("./bot.js").createTelegramBot>;
|
||||
|
||||
const loadConfig = getLoadConfigMock();
|
||||
|
||||
@@ -69,6 +60,20 @@ function resolveFlushTimer(setTimeoutSpy: ReturnType<typeof vi.spyOn>) {
|
||||
}
|
||||
|
||||
describe("createTelegramBot channel_post media", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
createTelegramBot = (opts) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
});
|
||||
|
||||
it("buffers channel_post media groups and processes them together", async () => {
|
||||
setOpenChannelPostConfig();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { GetReplyOptions, MsgContext } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
|
||||
import { withEnvAsync } from "../../../test/helpers/extensions/env.js";
|
||||
import { useFrozenTime, useRealTime } from "../../../test/helpers/extensions/frozen-time.js";
|
||||
@@ -33,22 +33,11 @@ const {
|
||||
throttlerSpy,
|
||||
useSpy,
|
||||
} = harness;
|
||||
import { resolveTelegramFetch } from "./fetch.js";
|
||||
|
||||
// Import after the harness registers `vi.mock(...)` for grammY and Telegram internals.
|
||||
const {
|
||||
createTelegramBot: createTelegramBotBase,
|
||||
getTelegramSequentialKey,
|
||||
setTelegramBotRuntimeForTest,
|
||||
} = await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
let resolveTelegramFetch: typeof import("./fetch.js").resolveTelegramFetch;
|
||||
let createTelegramBot: (
|
||||
opts: Parameters<typeof import("./bot.js").createTelegramBot>[0],
|
||||
) => ReturnType<typeof import("./bot.js").createTelegramBot>;
|
||||
let getTelegramSequentialKey: typeof import("./bot.js").getTelegramSequentialKey;
|
||||
|
||||
const loadConfig = getLoadConfigMock();
|
||||
const loadWebMedia = getLoadWebMediaMock();
|
||||
@@ -92,6 +81,24 @@ describe("createTelegramBot", () => {
|
||||
afterAll(() => {
|
||||
process.env.TZ = ORIGINAL_TZ;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ resolveTelegramFetch } = await import("./fetch.js"));
|
||||
const {
|
||||
createTelegramBot: createTelegramBotBase,
|
||||
getTelegramSequentialKey: importedGetTelegramSequentialKey,
|
||||
setTelegramBotRuntimeForTest,
|
||||
} = await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
getTelegramSequentialKey = importedGetTelegramSequentialKey;
|
||||
createTelegramBot = (opts) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
});
|
||||
|
||||
// groupPolicy tests
|
||||
|
||||
|
||||
@@ -27,22 +27,13 @@ const {
|
||||
wasSentByBot,
|
||||
} = await import("./bot.create-telegram-bot.test-harness.js");
|
||||
|
||||
// Import after the harness registers `vi.mock(...)` for grammY and Telegram internals.
|
||||
const { listNativeCommandSpecs, listNativeCommandSpecsForConfig } =
|
||||
await import("../../../src/auto-reply/commands-registry.js");
|
||||
const { loadSessionStore } = await import("../../../src/config/sessions.js");
|
||||
const { normalizeTelegramCommandName } =
|
||||
await import("../../../src/config/telegram-custom-commands.js");
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
let listNativeCommandSpecs: typeof import("../../../src/auto-reply/commands-registry.js").listNativeCommandSpecs;
|
||||
let listNativeCommandSpecsForConfig: typeof import("../../../src/auto-reply/commands-registry.js").listNativeCommandSpecsForConfig;
|
||||
let loadSessionStore: typeof import("../../../src/config/sessions.js").loadSessionStore;
|
||||
let normalizeTelegramCommandName: typeof import("../../../src/config/telegram-custom-commands.js").normalizeTelegramCommandName;
|
||||
let createTelegramBot: (
|
||||
opts: Parameters<typeof import("./bot.js").createTelegramBot>[0],
|
||||
) => ReturnType<typeof import("./bot.js").createTelegramBot>;
|
||||
|
||||
const loadConfig = getLoadConfigMock();
|
||||
const readChannelAllowFromStore = getReadChannelAllowFromStoreMock();
|
||||
@@ -77,6 +68,24 @@ describe("createTelegramBot", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ listNativeCommandSpecs, listNativeCommandSpecsForConfig } =
|
||||
await import("../../../src/auto-reply/commands-registry.js"));
|
||||
({ loadSessionStore } = await import("../../../src/config/sessions.js"));
|
||||
({ normalizeTelegramCommandName } =
|
||||
await import("../../../src/config/telegram-custom-commands.js"));
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
createTelegramBot = (opts) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
});
|
||||
|
||||
it("merges custom commands with native commands", async () => {
|
||||
const config = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createWhatsAppPollFixture,
|
||||
expectWhatsAppPollSent,
|
||||
@@ -21,9 +21,14 @@ vi.mock("./runtime.js", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
import { whatsappPlugin } from "./channel.js";
|
||||
let whatsappPlugin: typeof import("./channel.js").whatsappPlugin;
|
||||
|
||||
describe("whatsappPlugin outbound sendPoll", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ whatsappPlugin } = await import("./channel.js"));
|
||||
});
|
||||
|
||||
it("threads cfg into runtime sendPollWhatsApp call", async () => {
|
||||
const { cfg, poll, to, accountId } = createWhatsAppPollFixture();
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ vi.mock("./session.js", () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { monitorWebInbox, resetWebInboundDedupe } from "./inbound.js";
|
||||
let monitorWebInbox: typeof import("./inbound.js").monitorWebInbox;
|
||||
let resetWebInboundDedupe: typeof import("./inbound.js").resetWebInboundDedupe;
|
||||
let createWaSocket: typeof import("./session.js").createWaSocket;
|
||||
|
||||
async function waitForMessage(onMessage: ReturnType<typeof vi.fn>) {
|
||||
@@ -115,12 +116,18 @@ describe("web inbound media saves with extension", () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.resetModules();
|
||||
saveMediaBufferSpy.mockClear();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
({ monitorWebInbox, resetWebInboundDedupe } = await import("./inbound.js"));
|
||||
({ createWaSocket } = await import("./session.js"));
|
||||
resetWebInboundDedupe();
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
({ createWaSocket } = await import("./session.js"));
|
||||
await fs.rm(HOME, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { normalizeMessageContent, downloadMediaMessage } = vi.hoisted(() => ({
|
||||
normalizeMessageContent: vi.fn((msg: unknown) => msg),
|
||||
@@ -10,23 +10,31 @@ vi.mock("@whiskeysockets/baileys", () => ({
|
||||
downloadMediaMessage,
|
||||
}));
|
||||
|
||||
import { downloadInboundMedia } from "./media.js";
|
||||
let downloadInboundMedia: typeof import("./media.js").downloadInboundMedia;
|
||||
|
||||
const mockSock = {
|
||||
updateMediaMessage: vi.fn(),
|
||||
logger: { child: () => ({}) },
|
||||
} as never;
|
||||
};
|
||||
|
||||
async function expectMimetype(message: Record<string, unknown>, expected: string) {
|
||||
const result = await downloadInboundMedia({ message } as never, mockSock);
|
||||
const result = await downloadInboundMedia({ message } as never, mockSock as never);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.mimetype).toBe(expected);
|
||||
}
|
||||
|
||||
describe("downloadInboundMedia", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ downloadInboundMedia } = await import("./media.js"));
|
||||
normalizeMessageContent.mockClear();
|
||||
downloadMediaMessage.mockClear();
|
||||
mockSock.updateMediaMessage.mockClear();
|
||||
});
|
||||
|
||||
it("returns undefined for messages without media", async () => {
|
||||
const msg = { message: { conversation: "hello" } } as never;
|
||||
const result = await downloadInboundMedia(msg, mockSock);
|
||||
const result = await downloadInboundMedia(msg, mockSock as never);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -59,7 +67,7 @@ describe("downloadInboundMedia", () => {
|
||||
documentMessage: { mimetype: "application/pdf", fileName: "report.pdf" },
|
||||
},
|
||||
} as never;
|
||||
const result = await downloadInboundMedia(msg, mockSock);
|
||||
const result = await downloadInboundMedia(msg, mockSock as never);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.mimetype).toBe("application/pdf");
|
||||
expect(result?.fileName).toBe("report.pdf");
|
||||
|
||||
@@ -19,15 +19,17 @@ vi.mock("./session.js", () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { loginWeb } from "./login.js";
|
||||
import type { waitForWaConnection } from "./session.js";
|
||||
|
||||
const { createWaSocket } = await import("./session.js");
|
||||
let loginWeb: typeof import("./login.js").loginWeb;
|
||||
let createWaSocket: typeof import("./session.js").createWaSocket;
|
||||
|
||||
describe("web login", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.useFakeTimers();
|
||||
vi.clearAllMocks();
|
||||
({ loginWeb } = await import("./login.js"));
|
||||
({ createWaSocket } = await import("./session.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -2,19 +2,18 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import sharp from "sharp";
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveStateDir } from "../../../src/config/paths.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "../../../src/infra/tmp-openclaw-dir.js";
|
||||
import { optimizeImageToPng } from "../../../src/media/image-ops.js";
|
||||
import { mockPinnedHostnameResolution } from "../../../src/test-helpers/ssrf.js";
|
||||
import { captureEnv } from "../../../test/helpers/extensions/env.js";
|
||||
import { sendVoiceMessageDiscord } from "../../discord/src/send.js";
|
||||
import {
|
||||
LocalMediaAccessError,
|
||||
loadWebMedia,
|
||||
loadWebMediaRaw,
|
||||
optimizeImageToJpeg,
|
||||
} from "./media.js";
|
||||
|
||||
let LocalMediaAccessError: typeof import("./media.js").LocalMediaAccessError;
|
||||
let loadWebMedia: typeof import("./media.js").loadWebMedia;
|
||||
let loadWebMediaRaw: typeof import("./media.js").loadWebMediaRaw;
|
||||
let optimizeImageToJpeg: typeof import("./media.js").optimizeImageToJpeg;
|
||||
|
||||
const convertHeicToJpegMock = vi.fn();
|
||||
|
||||
@@ -78,6 +77,8 @@ function cloneStatWithDev<T extends { dev: number | bigint }>(stat: T, dev: numb
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
({ LocalMediaAccessError, loadWebMedia, loadWebMediaRaw, optimizeImageToJpeg } =
|
||||
await import("./media.js"));
|
||||
fixtureRoot = await fs.mkdtemp(
|
||||
path.join(resolvePreferredOpenClawTmpDir(), "openclaw-media-test-"),
|
||||
);
|
||||
@@ -136,6 +137,12 @@ afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ LocalMediaAccessError, loadWebMedia, loadWebMediaRaw, optimizeImageToJpeg } =
|
||||
await import("./media.js"));
|
||||
});
|
||||
|
||||
describe("web media loading", () => {
|
||||
beforeAll(() => {
|
||||
// Ensure state dir is stable and not influenced by other tests that stub OPENCLAW_STATE_DIR.
|
||||
|
||||
@@ -158,7 +158,7 @@ describe("web monitor inbox", () => {
|
||||
() => {
|
||||
expect(sock.sendMessage).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
{ timeout: 5_000, interval: 5 },
|
||||
);
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
expectPairingPromptSent(sock, "999@s.whatsapp.net", "+999");
|
||||
@@ -246,7 +246,7 @@ describe("web monitor inbox", () => {
|
||||
},
|
||||
]);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
{ timeout: 5_000, interval: 5 },
|
||||
);
|
||||
|
||||
// Verify it WAS NOT passed to onMessage
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import fsSync from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import "./monitor-inbox.test-harness.js";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { setLoggerOverride } from "../../../src/logging.js";
|
||||
import { monitorWebInbox } from "./inbound.js";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
getAuthDir,
|
||||
@@ -13,10 +7,31 @@ import {
|
||||
installWebMonitorInboxUnitTestHooks,
|
||||
mockLoadConfig,
|
||||
} from "./monitor-inbox.test-harness.js";
|
||||
let monitorWebInbox: typeof import("./inbound.js").monitorWebInbox;
|
||||
const inboundLoggerInfoMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/text-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/text-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
getChildLogger: () => ({
|
||||
info: inboundLoggerInfoMock,
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe("web monitor inbox", () => {
|
||||
installWebMonitorInboxUnitTestHooks();
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
inboundLoggerInfoMock.mockReset();
|
||||
({ monitorWebInbox } = await import("./inbound.js"));
|
||||
});
|
||||
|
||||
async function openMonitor(onMessage = vi.fn()) {
|
||||
return await monitorWebInbox({
|
||||
verbose: false,
|
||||
@@ -120,10 +135,7 @@ describe("web monitor inbox", () => {
|
||||
expect(sock.ws.close).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("logs inbound bodies to file", async () => {
|
||||
const logPath = path.join(os.tmpdir(), `openclaw-log-test-${crypto.randomUUID()}.log`);
|
||||
setLoggerOverride({ level: "trace", file: logPath });
|
||||
|
||||
it("logs inbound bodies through the inbound child logger", async () => {
|
||||
const { listener } = await runSingleUpsertAndCapture({
|
||||
type: "notify",
|
||||
messages: [
|
||||
@@ -136,15 +148,13 @@ describe("web monitor inbox", () => {
|
||||
],
|
||||
});
|
||||
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(fsSync.existsSync(logPath)).toBe(true);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
expect(inboundLoggerInfoMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: "ping",
|
||||
from: "+999",
|
||||
}),
|
||||
"inbound message",
|
||||
);
|
||||
const content = fsSync.readFileSync(logPath, "utf-8");
|
||||
expect(content).toMatch(/web-inbound/);
|
||||
expect(content).toMatch(/ping/);
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
|
||||
@@ -149,7 +149,8 @@ export async function waitForMessageCalls(onMessage: ReturnType<typeof vi.fn>, c
|
||||
() => {
|
||||
expect(onMessage).toHaveBeenCalledTimes(count);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
// Channel-suite workers can be saturated under no-isolate CI runs.
|
||||
{ timeout: 5_000, interval: 5 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -215,6 +216,7 @@ export function installWebMonitorInboxUnitTestHooks(opts?: { authDir?: boolean }
|
||||
const createAuthDir = opts?.authDir ?? true;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.useRealTimers();
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
sessionState.sock = createMockSock();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
@@ -15,9 +15,14 @@ vi.mock("./send.js", () => ({
|
||||
sendReactionWhatsApp: hoisted.sendReactionWhatsApp,
|
||||
}));
|
||||
|
||||
import { whatsappOutbound } from "./outbound-adapter.js";
|
||||
let whatsappOutbound: typeof import("./outbound-adapter.js").whatsappOutbound;
|
||||
|
||||
describe("whatsappOutbound sendPoll", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ whatsappOutbound } = await import("./outbound-adapter.js"));
|
||||
});
|
||||
|
||||
it("threads cfg through poll send options", async () => {
|
||||
const cfg = { marker: "resolved-cfg" } as OpenClawConfig;
|
||||
const poll = {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createServer } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { WebSocketServer } from "ws";
|
||||
import {
|
||||
decorateOpenClawProfile,
|
||||
@@ -112,6 +112,10 @@ describe("browser chrome profile decoration", () => {
|
||||
fixtureRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "openclaw-chrome-suite-"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (fixtureRoot) {
|
||||
await fsp.rm(fixtureRoot, { recursive: true, force: true });
|
||||
@@ -206,6 +210,10 @@ describe("browser chrome helpers", () => {
|
||||
return vi.spyOn(fs, "existsSync").mockImplementation((p) => match(String(p)));
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
installPwToolsCoreTestHooks,
|
||||
setPwToolsCoreCurrentPage,
|
||||
@@ -6,9 +6,14 @@ import {
|
||||
} from "./pw-tools-core.test-harness.js";
|
||||
|
||||
installPwToolsCoreTestHooks();
|
||||
const mod = await import("./pw-tools-core.js");
|
||||
let mod: typeof import("./pw-tools-core.js");
|
||||
|
||||
describe("pw-tools-core", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
mod = await import("./pw-tools-core.js");
|
||||
});
|
||||
|
||||
it("clamps timeoutMs for scrollIntoView", async () => {
|
||||
const scrollIntoViewIfNeeded = vi.fn(async () => {});
|
||||
setPwToolsCoreCurrentRefLocator({ scrollIntoViewIfNeeded });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let page: { evaluate: ReturnType<typeof vi.fn> } | null = null;
|
||||
|
||||
@@ -34,11 +34,9 @@ vi.mock("./pw-tools-core.snapshot.js", () => ({
|
||||
let batchViaPlaywright: typeof import("./pw-tools-core.interactions.js").batchViaPlaywright;
|
||||
|
||||
describe("batchViaPlaywright", () => {
|
||||
beforeAll(async () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ batchViaPlaywright } = await import("./pw-tools-core.interactions.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
page = {
|
||||
evaluate: vi.fn(async () => "ok"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let page: Record<string, unknown> | null = null;
|
||||
let locator: Record<string, unknown> | null = null;
|
||||
@@ -54,11 +54,9 @@ function seedSingleLocatorPage(): { setInputFiles: ReturnType<typeof vi.fn> } {
|
||||
}
|
||||
|
||||
describe("setInputFilesViaPlaywright", () => {
|
||||
beforeAll(async () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ setInputFilesViaPlaywright } = await import("./pw-tools-core.interactions.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
page = null;
|
||||
locator = null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DEFAULT_UPLOAD_DIR } from "./paths.js";
|
||||
import {
|
||||
installPwToolsCoreTestHooks,
|
||||
@@ -9,9 +9,14 @@ import {
|
||||
} from "./pw-tools-core.test-harness.js";
|
||||
|
||||
installPwToolsCoreTestHooks();
|
||||
const mod = await import("./pw-tools-core.js");
|
||||
let mod: typeof import("./pw-tools-core.js");
|
||||
|
||||
describe("pw-tools-core", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
mod = await import("./pw-tools-core.js");
|
||||
});
|
||||
|
||||
it("last file-chooser arm wins", async () => {
|
||||
const firstPath = path.join(DEFAULT_UPLOAD_DIR, `vitest-arm-1-${crypto.randomUUID()}.txt`);
|
||||
const secondPath = path.join(DEFAULT_UPLOAD_DIR, `vitest-arm-2-${crypto.randomUUID()}.txt`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DEFAULT_UPLOAD_DIR } from "./paths.js";
|
||||
import {
|
||||
getPwToolsCoreSessionMocks,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
|
||||
installPwToolsCoreTestHooks();
|
||||
const sessionMocks = getPwToolsCoreSessionMocks();
|
||||
const mod = await import("./pw-tools-core.js");
|
||||
let mod: typeof import("./pw-tools-core.js");
|
||||
|
||||
function createFileChooserPageMocks() {
|
||||
const fileChooser = { setFiles: vi.fn(async () => {}) };
|
||||
@@ -26,6 +26,11 @@ function createFileChooserPageMocks() {
|
||||
}
|
||||
|
||||
describe("pw-tools-core", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
mod = await import("./pw-tools-core.js");
|
||||
});
|
||||
|
||||
it("screenshots an element selector", async () => {
|
||||
const elementScreenshot = vi.fn(async () => Buffer.from("E"));
|
||||
const page = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveTargetIdAfterNavigate } from "./agent.snapshot.js";
|
||||
|
||||
type Tab = { targetId: string; url: string };
|
||||
@@ -8,6 +8,10 @@ function staticListTabs(tabs: Tab[]): () => Promise<Tab[]> {
|
||||
}
|
||||
|
||||
describe("resolveTargetIdAfterNavigate", () => {
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("returns original targetId when old target still exists (no swap)", async () => {
|
||||
const result = await resolveTargetIdAfterNavigate({
|
||||
oldTargetId: "old-123",
|
||||
|
||||
@@ -401,6 +401,7 @@ export async function cleanupBrowserControlServerTestContext(): Promise<void> {
|
||||
|
||||
export function installBrowserControlServerHooks() {
|
||||
beforeEach(async () => {
|
||||
vi.useRealTimers();
|
||||
cdpMocks.createTargetViaCdp.mockImplementation(async () => {
|
||||
if (state.createTargetId) {
|
||||
return { targetId: state.createTargetId };
|
||||
|
||||
Reference in New Issue
Block a user