test(discord): cover durable chunk retry delivery (#82898)

* test(discord): cover durable chunk retry delivery

* test(discord): use plugin sdk test runtime

* fix(telegram): satisfy message cache strict checks

* test(discord): include durable delivery in changed lane
This commit is contained in:
Peter Steinberger
2026-05-17 06:11:58 +01:00
committed by GitHub
parent df23b0f86c
commit 6a8a6551fc
3 changed files with 108 additions and 3 deletions

View File

@@ -0,0 +1,103 @@
import { sendDurableMessageBatch } from "openclaw/plugin-sdk/channel-message";
import {
createEmptyPluginRegistry,
createTestRegistry,
resetPluginRuntimeStateForTest,
setActivePluginRegistry,
} from "openclaw/plugin-sdk/plugin-test-runtime";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import {
createDiscordOutboundHoisted,
installDiscordOutboundModuleSpies,
resetDiscordOutboundMocks,
} from "./outbound-adapter.test-harness.js";
const hoisted = createDiscordOutboundHoisted();
await installDiscordOutboundModuleSpies(hoisted);
let discordPlugin: typeof import("./channel.js").discordPlugin;
beforeAll(async () => {
({ discordPlugin } = await import("./channel.js"));
});
describe("durable Discord delivery", () => {
beforeEach(() => {
resetDiscordOutboundMocks(hoisted);
setActivePluginRegistry(
createTestRegistry([
{
pluginId: "discord",
source: "test",
plugin: discordPlugin,
},
]),
);
});
afterEach(() => {
resetPluginRuntimeStateForTest();
setActivePluginRegistry(createEmptyPluginRegistry());
});
it("fans out planned text chunks and retries a transient failure on a later chunk", async () => {
hoisted.sendMessageDiscordMock
.mockResolvedValueOnce({
messageId: "msg-chunk-1",
channelId: "ch-1",
})
.mockRejectedValueOnce(Object.assign(new Error("discord 500"), { status: 500 }))
.mockResolvedValueOnce({
messageId: "msg-chunk-2",
channelId: "ch-1",
});
const result = await sendDurableMessageBatch({
cfg: {
channels: {
discord: {
token: "test-token",
retry: { attempts: 2, minDelayMs: 0, maxDelayMs: 0, jitter: 0 },
},
},
},
channel: "discord",
to: "channel:123456",
payloads: [{ text: "first chunk\nsecond chunk" }],
formatting: {
chunkMode: "newline",
maxLinesPerMessage: 1,
textLimit: 2000,
},
skipQueue: true,
});
expect(result.status).toBe("sent");
if (result.status !== "sent") {
throw new Error("expected durable Discord send to succeed");
}
expect(
result.results.map((entry) => ({
channel: entry.channel,
messageId: entry.messageId,
})),
).toEqual([
{ channel: "discord", messageId: "msg-chunk-1" },
{ channel: "discord", messageId: "msg-chunk-2" },
]);
expect(result.receipt.platformMessageIds).toEqual(["msg-chunk-1", "msg-chunk-2"]);
expect(result.payloadOutcomes).toEqual([
{
index: 0,
status: "sent",
results: result.results,
},
]);
expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledTimes(3);
expect(hoisted.sendMessageDiscordMock.mock.calls.map((call) => call[1])).toEqual([
"first chunk",
"second chunk",
"second chunk",
]);
});
});

View File

@@ -568,7 +568,7 @@ export function createTelegramMessageCache(params?: {
}
const key = telegramMessageCacheKey({ accountId, chatId, messageId });
const cachedNode = upsertCachedMessageNode({ messages, key, node, mode });
if (node.messageId === currentObservation.node.messageId) {
if (messageId === currentObservation.node.messageId) {
recordedEntry = cachedNode;
}
trimMessages(messages, maxMessages);
@@ -697,11 +697,12 @@ function resolveSessionBoundaryNode(params: {
if (!params.messageId) {
return undefined;
}
const { messageId } = params;
const candidates = params.cache
.recentBefore({
accountId: params.accountId,
chatId: params.chatId,
messageId: params.messageId,
messageId,
...(params.threadId !== undefined ? { threadId: params.threadId } : {}),
limit: Number.MAX_SAFE_INTEGER,
})
@@ -709,7 +710,7 @@ function resolveSessionBoundaryNode(params: {
const current = params.cache.get({
accountId: params.accountId,
chatId: params.chatId,
messageId: params.messageId,
messageId,
});
if (current && isSessionBoundaryCommandNode(current)) {
candidates.push(current);

View File

@@ -943,6 +943,7 @@ describe("test-projects args", () => {
"extensions/discord/src/channel-actions.contract.test.ts",
"extensions/discord/src/channel.message-adapter.test.ts",
"extensions/discord/src/channel.test.ts",
"extensions/discord/src/durable-delivery.test.ts",
"extensions/discord/src/monitor/message-handler.bot-self-filter.test.ts",
"extensions/discord/src/monitor/message-handler.queue.test.ts",
"extensions/discord/src/monitor/provider.skill-dedupe.test.ts",