mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 10:32:43 +00:00
refactor(line): dedupe replay webhook test fixtures
This commit is contained in:
@@ -68,6 +68,46 @@ let createLineWebhookReplayCache: typeof import("./bot-handlers.js").createLineW
|
|||||||
|
|
||||||
const createRuntime = () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() });
|
const createRuntime = () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() });
|
||||||
|
|
||||||
|
function createReplayMessageEvent(params: {
|
||||||
|
messageId: string;
|
||||||
|
groupId: string;
|
||||||
|
userId: string;
|
||||||
|
webhookEventId: string;
|
||||||
|
isRedelivery: boolean;
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
type: "message",
|
||||||
|
message: { id: params.messageId, type: "text", text: "hello" },
|
||||||
|
replyToken: "reply-token",
|
||||||
|
timestamp: Date.now(),
|
||||||
|
source: { type: "group", groupId: params.groupId, userId: params.userId },
|
||||||
|
mode: "active",
|
||||||
|
webhookEventId: params.webhookEventId,
|
||||||
|
deliveryContext: { isRedelivery: params.isRedelivery },
|
||||||
|
} as MessageEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOpenGroupReplayContext(
|
||||||
|
processMessage: ReturnType<typeof vi.fn>,
|
||||||
|
replayCache: ReturnType<typeof createLineWebhookReplayCache>,
|
||||||
|
): Parameters<typeof handleLineWebhookEvents>[1] {
|
||||||
|
return {
|
||||||
|
cfg: { channels: { line: { groupPolicy: "open" } } },
|
||||||
|
account: {
|
||||||
|
accountId: "default",
|
||||||
|
enabled: true,
|
||||||
|
channelAccessToken: "token",
|
||||||
|
channelSecret: "secret",
|
||||||
|
tokenSource: "config",
|
||||||
|
config: { groupPolicy: "open" },
|
||||||
|
},
|
||||||
|
runtime: createRuntime(),
|
||||||
|
mediaMaxBytes: 1,
|
||||||
|
processMessage,
|
||||||
|
replayCache,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
vi.mock("../pairing/pairing-store.js", () => ({
|
vi.mock("../pairing/pairing-store.js", () => ({
|
||||||
readChannelAllowFromStore: readAllowFromStoreMock,
|
readChannelAllowFromStore: readAllowFromStoreMock,
|
||||||
upsertChannelPairingRequest: upsertPairingRequestMock,
|
upsertChannelPairingRequest: upsertPairingRequestMock,
|
||||||
@@ -377,32 +417,14 @@ describe("handleLineWebhookEvents", () => {
|
|||||||
|
|
||||||
it("deduplicates replayed webhook events by webhookEventId before processing", async () => {
|
it("deduplicates replayed webhook events by webhookEventId before processing", async () => {
|
||||||
const processMessage = vi.fn();
|
const processMessage = vi.fn();
|
||||||
const event = {
|
const event = createReplayMessageEvent({
|
||||||
type: "message",
|
messageId: "m-replay",
|
||||||
message: { id: "m-replay", type: "text", text: "hello" },
|
groupId: "group-replay",
|
||||||
replyToken: "reply-token",
|
userId: "user-replay",
|
||||||
timestamp: Date.now(),
|
|
||||||
source: { type: "group", groupId: "group-replay", userId: "user-replay" },
|
|
||||||
mode: "active",
|
|
||||||
webhookEventId: "evt-replay-1",
|
webhookEventId: "evt-replay-1",
|
||||||
deliveryContext: { isRedelivery: true },
|
isRedelivery: true,
|
||||||
} as MessageEvent;
|
});
|
||||||
|
const context = createOpenGroupReplayContext(processMessage, createLineWebhookReplayCache());
|
||||||
const context: Parameters<typeof handleLineWebhookEvents>[1] = {
|
|
||||||
cfg: { channels: { line: { groupPolicy: "open" } } },
|
|
||||||
account: {
|
|
||||||
accountId: "default",
|
|
||||||
enabled: true,
|
|
||||||
channelAccessToken: "token",
|
|
||||||
channelSecret: "secret",
|
|
||||||
tokenSource: "config",
|
|
||||||
config: { groupPolicy: "open" },
|
|
||||||
},
|
|
||||||
runtime: createRuntime(),
|
|
||||||
mediaMaxBytes: 1,
|
|
||||||
processMessage,
|
|
||||||
replayCache: createLineWebhookReplayCache(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await handleLineWebhookEvents([event], context);
|
await handleLineWebhookEvents([event], context);
|
||||||
await handleLineWebhookEvents([event], context);
|
await handleLineWebhookEvents([event], context);
|
||||||
@@ -419,32 +441,14 @@ describe("handleLineWebhookEvents", () => {
|
|||||||
const processMessage = vi.fn(async () => {
|
const processMessage = vi.fn(async () => {
|
||||||
await firstDone;
|
await firstDone;
|
||||||
});
|
});
|
||||||
const event = {
|
const event = createReplayMessageEvent({
|
||||||
type: "message",
|
messageId: "m-inflight",
|
||||||
message: { id: "m-inflight", type: "text", text: "hello" },
|
groupId: "group-inflight",
|
||||||
replyToken: "reply-token",
|
userId: "user-inflight",
|
||||||
timestamp: Date.now(),
|
|
||||||
source: { type: "group", groupId: "group-inflight", userId: "user-inflight" },
|
|
||||||
mode: "active",
|
|
||||||
webhookEventId: "evt-inflight-1",
|
webhookEventId: "evt-inflight-1",
|
||||||
deliveryContext: { isRedelivery: true },
|
isRedelivery: true,
|
||||||
} as MessageEvent;
|
});
|
||||||
|
const context = createOpenGroupReplayContext(processMessage, createLineWebhookReplayCache());
|
||||||
const context: Parameters<typeof handleLineWebhookEvents>[1] = {
|
|
||||||
cfg: { channels: { line: { groupPolicy: "open" } } },
|
|
||||||
account: {
|
|
||||||
accountId: "default",
|
|
||||||
enabled: true,
|
|
||||||
channelAccessToken: "token",
|
|
||||||
channelSecret: "secret",
|
|
||||||
tokenSource: "config",
|
|
||||||
config: { groupPolicy: "open" },
|
|
||||||
},
|
|
||||||
runtime: createRuntime(),
|
|
||||||
mediaMaxBytes: 1,
|
|
||||||
processMessage,
|
|
||||||
replayCache: createLineWebhookReplayCache(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const firstRun = handleLineWebhookEvents([event], context);
|
const firstRun = handleLineWebhookEvents([event], context);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
@@ -464,32 +468,14 @@ describe("handleLineWebhookEvents", () => {
|
|||||||
const processMessage = vi.fn(async () => {
|
const processMessage = vi.fn(async () => {
|
||||||
await firstDone;
|
await firstDone;
|
||||||
});
|
});
|
||||||
const event = {
|
const event = createReplayMessageEvent({
|
||||||
type: "message",
|
messageId: "m-inflight-fail",
|
||||||
message: { id: "m-inflight-fail", type: "text", text: "hello" },
|
groupId: "group-inflight",
|
||||||
replyToken: "reply-token",
|
userId: "user-inflight",
|
||||||
timestamp: Date.now(),
|
|
||||||
source: { type: "group", groupId: "group-inflight", userId: "user-inflight" },
|
|
||||||
mode: "active",
|
|
||||||
webhookEventId: "evt-inflight-fail-1",
|
webhookEventId: "evt-inflight-fail-1",
|
||||||
deliveryContext: { isRedelivery: true },
|
isRedelivery: true,
|
||||||
} as MessageEvent;
|
});
|
||||||
|
const context = createOpenGroupReplayContext(processMessage, createLineWebhookReplayCache());
|
||||||
const context: Parameters<typeof handleLineWebhookEvents>[1] = {
|
|
||||||
cfg: { channels: { line: { groupPolicy: "open" } } },
|
|
||||||
account: {
|
|
||||||
accountId: "default",
|
|
||||||
enabled: true,
|
|
||||||
channelAccessToken: "token",
|
|
||||||
channelSecret: "secret",
|
|
||||||
tokenSource: "config",
|
|
||||||
config: { groupPolicy: "open" },
|
|
||||||
},
|
|
||||||
runtime: createRuntime(),
|
|
||||||
mediaMaxBytes: 1,
|
|
||||||
processMessage,
|
|
||||||
replayCache: createLineWebhookReplayCache(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const firstRun = handleLineWebhookEvents([event], context);
|
const firstRun = handleLineWebhookEvents([event], context);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
@@ -604,32 +590,14 @@ describe("handleLineWebhookEvents", () => {
|
|||||||
.fn()
|
.fn()
|
||||||
.mockRejectedValueOnce(new Error("transient failure"))
|
.mockRejectedValueOnce(new Error("transient failure"))
|
||||||
.mockResolvedValueOnce(undefined);
|
.mockResolvedValueOnce(undefined);
|
||||||
const event = {
|
const event = createReplayMessageEvent({
|
||||||
type: "message",
|
messageId: "m-fail-then-retry",
|
||||||
message: { id: "m-fail-then-retry", type: "text", text: "hello" },
|
groupId: "group-retry",
|
||||||
replyToken: "reply-token",
|
userId: "user-retry",
|
||||||
timestamp: Date.now(),
|
|
||||||
source: { type: "group", groupId: "group-retry", userId: "user-retry" },
|
|
||||||
mode: "active",
|
|
||||||
webhookEventId: "evt-fail-then-retry",
|
webhookEventId: "evt-fail-then-retry",
|
||||||
deliveryContext: { isRedelivery: false },
|
isRedelivery: false,
|
||||||
} as MessageEvent;
|
});
|
||||||
|
const context = createOpenGroupReplayContext(processMessage, createLineWebhookReplayCache());
|
||||||
const context: Parameters<typeof handleLineWebhookEvents>[1] = {
|
|
||||||
cfg: { channels: { line: { groupPolicy: "open" } } },
|
|
||||||
account: {
|
|
||||||
accountId: "default",
|
|
||||||
enabled: true,
|
|
||||||
channelAccessToken: "token",
|
|
||||||
channelSecret: "secret",
|
|
||||||
tokenSource: "config",
|
|
||||||
config: { groupPolicy: "open" },
|
|
||||||
},
|
|
||||||
runtime: createRuntime(),
|
|
||||||
mediaMaxBytes: 1,
|
|
||||||
processMessage,
|
|
||||||
replayCache: createLineWebhookReplayCache(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await expect(handleLineWebhookEvents([event], context)).rejects.toThrow("transient failure");
|
await expect(handleLineWebhookEvents([event], context)).rejects.toThrow("transient failure");
|
||||||
await handleLineWebhookEvents([event], context);
|
await handleLineWebhookEvents([event], context);
|
||||||
|
|||||||
Reference in New Issue
Block a user