mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 07:32:44 +00:00
refactor(slack): dedupe app mention in-flight race setup
This commit is contained in:
@@ -84,6 +84,38 @@ function createSlackEvent(params: { type: "message" | "app_mention"; ts: string;
|
|||||||
return { type: params.type, channel: "C1", ts: params.ts, text: params.text } as never;
|
return { type: params.type, channel: "C1", ts: params.ts, text: params.text } as never;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendMessageEvent(handler: ReturnType<typeof createTestHandler>, ts: string) {
|
||||||
|
await handler(createSlackEvent({ type: "message", ts, text: "hello" }), { source: "message" });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMentionEvent(handler: ReturnType<typeof createTestHandler>, ts: string) {
|
||||||
|
await handler(createSlackEvent({ type: "app_mention", ts, text: "<@U_BOT> hello" }), {
|
||||||
|
source: "app_mention",
|
||||||
|
wasMentioned: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createInFlightMessageScenario(ts: string) {
|
||||||
|
let resolveMessagePrepare: ((value: unknown) => void) | undefined;
|
||||||
|
const messagePrepare = new Promise<unknown>((resolve) => {
|
||||||
|
resolveMessagePrepare = resolve;
|
||||||
|
});
|
||||||
|
prepareSlackMessageMock.mockImplementation(async ({ opts }) => {
|
||||||
|
if (opts.source === "message") {
|
||||||
|
return messagePrepare;
|
||||||
|
}
|
||||||
|
return { ctxPayload: {} };
|
||||||
|
});
|
||||||
|
|
||||||
|
const handler = createTestHandler();
|
||||||
|
const messagePending = handler(createSlackEvent({ type: "message", ts, text: "hello" }), {
|
||||||
|
source: "message",
|
||||||
|
});
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
return { handler, messagePending, resolveMessagePrepare };
|
||||||
|
}
|
||||||
|
|
||||||
describe("createSlackMessageHandler app_mention race handling", () => {
|
describe("createSlackMessageHandler app_mention race handling", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
prepareSlackMessageMock.mockReset();
|
prepareSlackMessageMock.mockReset();
|
||||||
@@ -100,103 +132,34 @@ describe("createSlackMessageHandler app_mention race handling", () => {
|
|||||||
|
|
||||||
const handler = createTestHandler();
|
const handler = createTestHandler();
|
||||||
|
|
||||||
await handler(createSlackEvent({ type: "message", ts: "1700000000.000100", text: "hello" }), {
|
await sendMessageEvent(handler, "1700000000.000100");
|
||||||
source: "message",
|
await sendMentionEvent(handler, "1700000000.000100");
|
||||||
});
|
await sendMentionEvent(handler, "1700000000.000100");
|
||||||
await handler(
|
|
||||||
createSlackEvent({
|
|
||||||
type: "app_mention",
|
|
||||||
ts: "1700000000.000100",
|
|
||||||
text: "<@U_BOT> hello",
|
|
||||||
}),
|
|
||||||
{ source: "app_mention", wasMentioned: true },
|
|
||||||
);
|
|
||||||
await handler(
|
|
||||||
createSlackEvent({
|
|
||||||
type: "app_mention",
|
|
||||||
ts: "1700000000.000100",
|
|
||||||
text: "<@U_BOT> hello",
|
|
||||||
}),
|
|
||||||
{ source: "app_mention", wasMentioned: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(prepareSlackMessageMock).toHaveBeenCalledTimes(2);
|
expect(prepareSlackMessageMock).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows app_mention while message handling is still in-flight, then keeps later duplicates deduped", async () => {
|
it("allows app_mention while message handling is still in-flight, then keeps later duplicates deduped", async () => {
|
||||||
let resolveMessagePrepare: ((value: unknown) => void) | undefined;
|
const { handler, messagePending, resolveMessagePrepare } =
|
||||||
const messagePrepare = new Promise<unknown>((resolve) => {
|
await createInFlightMessageScenario("1700000000.000150");
|
||||||
resolveMessagePrepare = resolve;
|
|
||||||
});
|
|
||||||
prepareSlackMessageMock.mockImplementation(async ({ opts }) => {
|
|
||||||
if (opts.source === "message") {
|
|
||||||
return messagePrepare;
|
|
||||||
}
|
|
||||||
return { ctxPayload: {} };
|
|
||||||
});
|
|
||||||
|
|
||||||
const handler = createTestHandler();
|
await sendMentionEvent(handler, "1700000000.000150");
|
||||||
|
|
||||||
const messagePending = handler(
|
|
||||||
createSlackEvent({ type: "message", ts: "1700000000.000150", text: "hello" }),
|
|
||||||
{ source: "message" },
|
|
||||||
);
|
|
||||||
await Promise.resolve();
|
|
||||||
|
|
||||||
await handler(
|
|
||||||
createSlackEvent({
|
|
||||||
type: "app_mention",
|
|
||||||
ts: "1700000000.000150",
|
|
||||||
text: "<@U_BOT> hello",
|
|
||||||
}),
|
|
||||||
{ source: "app_mention", wasMentioned: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
resolveMessagePrepare?.(null);
|
resolveMessagePrepare?.(null);
|
||||||
await messagePending;
|
await messagePending;
|
||||||
|
|
||||||
await handler(
|
await sendMentionEvent(handler, "1700000000.000150");
|
||||||
createSlackEvent({
|
|
||||||
type: "app_mention",
|
|
||||||
ts: "1700000000.000150",
|
|
||||||
text: "<@U_BOT> hello",
|
|
||||||
}),
|
|
||||||
{ source: "app_mention", wasMentioned: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(prepareSlackMessageMock).toHaveBeenCalledTimes(2);
|
expect(prepareSlackMessageMock).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("suppresses message dispatch when app_mention already dispatched during in-flight race", async () => {
|
it("suppresses message dispatch when app_mention already dispatched during in-flight race", async () => {
|
||||||
let resolveMessagePrepare: ((value: unknown) => void) | undefined;
|
const { handler, messagePending, resolveMessagePrepare } =
|
||||||
const messagePrepare = new Promise<unknown>((resolve) => {
|
await createInFlightMessageScenario("1700000000.000175");
|
||||||
resolveMessagePrepare = resolve;
|
|
||||||
});
|
|
||||||
prepareSlackMessageMock.mockImplementation(async ({ opts }) => {
|
|
||||||
if (opts.source === "message") {
|
|
||||||
return messagePrepare;
|
|
||||||
}
|
|
||||||
return { ctxPayload: {} };
|
|
||||||
});
|
|
||||||
|
|
||||||
const handler = createTestHandler();
|
await sendMentionEvent(handler, "1700000000.000175");
|
||||||
|
|
||||||
const messagePending = handler(
|
|
||||||
createSlackEvent({ type: "message", ts: "1700000000.000175", text: "hello" }),
|
|
||||||
{ source: "message" },
|
|
||||||
);
|
|
||||||
await Promise.resolve();
|
|
||||||
|
|
||||||
await handler(
|
|
||||||
createSlackEvent({
|
|
||||||
type: "app_mention",
|
|
||||||
ts: "1700000000.000175",
|
|
||||||
text: "<@U_BOT> hello",
|
|
||||||
}),
|
|
||||||
{ source: "app_mention", wasMentioned: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
resolveMessagePrepare?.({ ctxPayload: {} });
|
resolveMessagePrepare?.({ ctxPayload: {} });
|
||||||
await messagePending;
|
await messagePending;
|
||||||
@@ -210,17 +173,8 @@ describe("createSlackMessageHandler app_mention race handling", () => {
|
|||||||
|
|
||||||
const handler = createTestHandler();
|
const handler = createTestHandler();
|
||||||
|
|
||||||
await handler(createSlackEvent({ type: "message", ts: "1700000000.000200", text: "hello" }), {
|
await sendMessageEvent(handler, "1700000000.000200");
|
||||||
source: "message",
|
await sendMentionEvent(handler, "1700000000.000200");
|
||||||
});
|
|
||||||
await handler(
|
|
||||||
createSlackEvent({
|
|
||||||
type: "app_mention",
|
|
||||||
ts: "1700000000.000200",
|
|
||||||
text: "<@U_BOT> hello",
|
|
||||||
}),
|
|
||||||
{ source: "app_mention", wasMentioned: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(prepareSlackMessageMock).toHaveBeenCalledTimes(1);
|
expect(prepareSlackMessageMock).toHaveBeenCalledTimes(1);
|
||||||
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
expect(dispatchPreparedSlackMessageMock).toHaveBeenCalledTimes(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user