mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 00:18:26 +00:00
refactor: dedupe channel outbound and monitor tests
This commit is contained in:
@@ -25,10 +25,18 @@ vi.mock("../auto-reply/dispatch.js", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../pairing/pairing-store.js", () => ({
|
||||
readChannelAllowFromStore: (...args: unknown[]) => readAllowFromStoreMock(...args),
|
||||
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
|
||||
}));
|
||||
function createPairingStoreMocks() {
|
||||
return {
|
||||
readChannelAllowFromStore(...args: unknown[]) {
|
||||
return readAllowFromStoreMock(...args);
|
||||
},
|
||||
upsertChannelPairingRequest(...args: unknown[]) {
|
||||
return upsertPairingRequestMock(...args);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
vi.mock("../pairing/pairing-store.js", () => createPairingStoreMocks());
|
||||
|
||||
vi.mock("../config/sessions.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/sessions.js")>();
|
||||
|
||||
@@ -43,8 +43,12 @@ type DiscordReactionEvent = Parameters<MessageReactionAddListener["handle"]>[0];
|
||||
|
||||
type DiscordReactionListenerParams = {
|
||||
cfg: LoadedConfig;
|
||||
accountId: string;
|
||||
runtime: RuntimeEnv;
|
||||
logger: Logger;
|
||||
onEvent?: () => void;
|
||||
} & DiscordReactionRoutingParams;
|
||||
|
||||
type DiscordReactionRoutingParams = {
|
||||
botUserId?: string;
|
||||
dmEnabled: boolean;
|
||||
groupDmEnabled: boolean;
|
||||
@@ -54,8 +58,6 @@ type DiscordReactionListenerParams = {
|
||||
groupPolicy: "open" | "allowlist" | "disabled";
|
||||
allowNameMatching: boolean;
|
||||
guildEntries?: Record<string, import("./allow-list.js").DiscordGuildEntryResolved>;
|
||||
logger: Logger;
|
||||
onEvent?: () => void;
|
||||
};
|
||||
|
||||
const DISCORD_SLOW_LISTENER_THRESHOLD_MS = 30_000;
|
||||
@@ -315,23 +317,15 @@ async function authorizeDiscordReactionIngress(
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
async function handleDiscordReactionEvent(params: {
|
||||
data: DiscordReactionEvent;
|
||||
client: Client;
|
||||
action: "added" | "removed";
|
||||
cfg: LoadedConfig;
|
||||
accountId: string;
|
||||
botUserId?: string;
|
||||
dmEnabled: boolean;
|
||||
groupDmEnabled: boolean;
|
||||
groupDmChannels: string[];
|
||||
dmPolicy: "open" | "pairing" | "allowlist" | "disabled";
|
||||
allowFrom: string[];
|
||||
groupPolicy: "open" | "allowlist" | "disabled";
|
||||
allowNameMatching: boolean;
|
||||
guildEntries?: Record<string, import("./allow-list.js").DiscordGuildEntryResolved>;
|
||||
logger: Logger;
|
||||
}) {
|
||||
async function handleDiscordReactionEvent(
|
||||
params: {
|
||||
data: DiscordReactionEvent;
|
||||
client: Client;
|
||||
action: "added" | "removed";
|
||||
cfg: LoadedConfig;
|
||||
logger: Logger;
|
||||
} & DiscordReactionRoutingParams,
|
||||
) {
|
||||
try {
|
||||
const { data, client, action, botUserId, guildEntries } = params;
|
||||
if (!("user" in data)) {
|
||||
|
||||
@@ -120,6 +120,19 @@ const { processDiscordMessage } = await import("./message-handler.process.js");
|
||||
|
||||
const createBaseContext = createBaseDiscordMessageContext;
|
||||
|
||||
function mockDispatchSingleBlockReply(payload: { text: string; isReasoning?: boolean }) {
|
||||
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
|
||||
await params?.dispatcher.sendBlockReply(payload);
|
||||
return { queuedFinal: false, counts: { final: 0, tool: 0, block: 1 } };
|
||||
});
|
||||
}
|
||||
|
||||
async function processStreamOffDiscordMessage() {
|
||||
const ctx = await createBaseContext({ discordConfig: { streamMode: "off" } });
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
await processDiscordMessage(ctx as any);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
sendMocks.reactMessageDiscord.mockClear();
|
||||
@@ -463,15 +476,8 @@ describe("processDiscordMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
it("suppresses reasoning payload delivery to Discord", async () => {
|
||||
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
|
||||
await params?.dispatcher.sendBlockReply({ text: "thinking...", isReasoning: true });
|
||||
return { queuedFinal: false, counts: { final: 0, tool: 0, block: 1 } };
|
||||
});
|
||||
|
||||
const ctx = await createBaseContext({ discordConfig: { streamMode: "off" } });
|
||||
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
await processDiscordMessage(ctx as any);
|
||||
mockDispatchSingleBlockReply({ text: "thinking...", isReasoning: true });
|
||||
await processStreamOffDiscordMessage();
|
||||
|
||||
expect(deliverDiscordReply).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -495,15 +501,8 @@ describe("processDiscordMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
it("delivers non-reasoning block payloads to Discord", async () => {
|
||||
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
|
||||
await params?.dispatcher.sendBlockReply({ text: "hello from block stream" });
|
||||
return { queuedFinal: false, counts: { final: 0, tool: 0, block: 1 } };
|
||||
});
|
||||
|
||||
const ctx = await createBaseContext({ discordConfig: { streamMode: "off" } });
|
||||
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
await processDiscordMessage(ctx as any);
|
||||
mockDispatchSingleBlockReply({ text: "hello from block stream" });
|
||||
await processStreamOffDiscordMessage();
|
||||
|
||||
expect(deliverDiscordReply).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -210,8 +210,10 @@ function createBoundThreadBindingManager(params: {
|
||||
targetSessionKey: string;
|
||||
agentId: string;
|
||||
}): ThreadBindingManager {
|
||||
const baseManager = createNoopThreadBindingManager(params.accountId);
|
||||
const now = Date.now();
|
||||
return {
|
||||
accountId: params.accountId,
|
||||
...baseManager,
|
||||
getIdleTimeoutMs: () => 24 * 60 * 60 * 1000,
|
||||
getMaxAgeMs: () => 0,
|
||||
getByThreadId: (threadId: string) =>
|
||||
@@ -224,20 +226,12 @@ function createBoundThreadBindingManager(params: {
|
||||
targetSessionKey: params.targetSessionKey,
|
||||
agentId: params.agentId,
|
||||
boundBy: "system",
|
||||
boundAt: Date.now(),
|
||||
lastActivityAt: Date.now(),
|
||||
boundAt: now,
|
||||
lastActivityAt: now,
|
||||
idleTimeoutMs: 24 * 60 * 60 * 1000,
|
||||
maxAgeMs: 0,
|
||||
}
|
||||
: undefined,
|
||||
getBySessionKey: () => undefined,
|
||||
listBySessionKey: () => [],
|
||||
listBindings: () => [],
|
||||
touchThread: () => null,
|
||||
bindTarget: async () => null,
|
||||
unbindThread: () => null,
|
||||
unbindBySessionKey: () => [],
|
||||
stop: () => {},
|
||||
: baseManager.getByThreadId(threadId),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,14 @@ describe("monitorDiscordProvider", () => {
|
||||
},
|
||||
}) as OpenClawConfig;
|
||||
|
||||
const getConstructedEventQueue = (): { listenerTimeout?: number } | undefined => {
|
||||
expect(clientConstructorOptionsMock).toHaveBeenCalledTimes(1);
|
||||
const opts = clientConstructorOptionsMock.mock.calls[0]?.[0] as {
|
||||
eventQueue?: { listenerTimeout?: number };
|
||||
};
|
||||
return opts.eventQueue;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
clientConstructorOptionsMock.mockClear();
|
||||
clientFetchUserMock.mockClear().mockResolvedValue({ id: "bot-1" });
|
||||
@@ -349,12 +357,9 @@ describe("monitorDiscordProvider", () => {
|
||||
runtime: baseRuntime(),
|
||||
});
|
||||
|
||||
expect(clientConstructorOptionsMock).toHaveBeenCalledTimes(1);
|
||||
const opts = clientConstructorOptionsMock.mock.calls[0]?.[0] as {
|
||||
eventQueue?: { listenerTimeout?: number };
|
||||
};
|
||||
expect(opts.eventQueue).toBeDefined();
|
||||
expect(opts.eventQueue?.listenerTimeout).toBe(120_000);
|
||||
const eventQueue = getConstructedEventQueue();
|
||||
expect(eventQueue).toBeDefined();
|
||||
expect(eventQueue?.listenerTimeout).toBe(120_000);
|
||||
});
|
||||
|
||||
it("forwards custom eventQueue config from discord config to Carbon Client", async () => {
|
||||
@@ -377,10 +382,7 @@ describe("monitorDiscordProvider", () => {
|
||||
runtime: baseRuntime(),
|
||||
});
|
||||
|
||||
expect(clientConstructorOptionsMock).toHaveBeenCalledTimes(1);
|
||||
const opts = clientConstructorOptionsMock.mock.calls[0]?.[0] as {
|
||||
eventQueue?: { listenerTimeout?: number };
|
||||
};
|
||||
expect(opts.eventQueue?.listenerTimeout).toBe(300_000);
|
||||
const eventQueue = getConstructedEventQueue();
|
||||
expect(eventQueue?.listenerTimeout).toBe(300_000);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user