fix(slack): await draft stream flush before messageId check to prevent duplicate messages
This commit is contained in:
parent
8a05c05596
commit
3bbbf789cb
@ -137,4 +137,39 @@ describe("createSlackDraftStream", () => {
|
||||
expect(stream.messageId()).toBeUndefined();
|
||||
expect(stream.channelId()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("flush resolves in-flight send so messageId is available (race condition fix)", async () => {
|
||||
let resolveDeferred!: (value: { channelId: string; messageId: string }) => void;
|
||||
const send = vi.fn<DraftSendFn>(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveDeferred = resolve;
|
||||
}),
|
||||
);
|
||||
const { stream } = createDraftStreamHarness({ send });
|
||||
|
||||
stream.update("hello");
|
||||
// Start flush — send is in-flight but hasn't resolved
|
||||
const flushPromise = stream.flush();
|
||||
|
||||
// At this point, sendMessageSlack is in-flight but hasn't resolved
|
||||
expect(stream.messageId()).toBeUndefined();
|
||||
|
||||
// Simulate send completing
|
||||
resolveDeferred({ channelId: "C123", messageId: "999.888" });
|
||||
await flushPromise;
|
||||
|
||||
// Now messageId should be populated
|
||||
expect(stream.messageId()).toBe("999.888");
|
||||
});
|
||||
|
||||
it("flush is safe to call when no draft has been started", async () => {
|
||||
const { stream, send } = createDraftStreamHarness();
|
||||
|
||||
// Flush without any update — should be a no-op
|
||||
await stream.flush();
|
||||
|
||||
expect(send).not.toHaveBeenCalled();
|
||||
expect(stream.messageId()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -310,6 +310,11 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
|
||||
const reply = resolveSendableOutboundReplyParts(payload);
|
||||
const slackBlocks = readSlackReplyBlocks(payload);
|
||||
// Flush any in-flight draft send so messageId() is populated.
|
||||
// Without this, a race between the fire-and-forget sendMessageSlack()
|
||||
// and the deliver callback can cause canFinalizeViaPreviewEdit to
|
||||
// evaluate false, resulting in a duplicate Slack message.
|
||||
await draftStream?.flush();
|
||||
const draftMessageId = draftStream?.messageId();
|
||||
const draftChannelId = draftStream?.channelId();
|
||||
const finalText = reply.text;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user