test(slack): cover first native stream thread target

This commit is contained in:
Peter Steinberger
2026-04-24 23:42:42 +01:00
parent 137f5c3a8b
commit 99cfa50451
2 changed files with 49 additions and 5 deletions

View File

@@ -30,6 +30,7 @@ class TestSlackStreamNotDeliveredError extends Error {
let mockedNativeStreaming = false;
let mockedBlockStreamingEnabled: boolean | undefined = false;
let capturedReplyOptions: { disableBlockStreaming?: boolean } | undefined;
let mockedReplyThreadTs: string | undefined = THREAD_TS;
let mockedDispatchSequence: Array<{
kind: "tool" | "block" | "final";
payload: { text: string; isError?: boolean; mediaUrl?: string; mediaUrls?: string[] };
@@ -52,7 +53,15 @@ function createDraftStreamStub() {
};
}
function createPreparedSlackMessage() {
function createPreparedSlackMessage(params?: {
message?: Partial<{
channel: string;
ts: string;
thread_ts?: string;
user: string;
}>;
replyToMode?: "off" | "first" | "all" | "batched";
}) {
return {
ctx: {
cfg: {},
@@ -77,6 +86,7 @@ function createPreparedSlackMessage() {
ts: "171234.111",
thread_ts: THREAD_TS,
user: "U123",
...params?.message,
},
route: {
agentId: "agent-1",
@@ -88,7 +98,7 @@ function createPreparedSlackMessage() {
ctxPayload: {
MessageThreadId: THREAD_TS,
},
replyToMode: "all",
replyToMode: params?.replyToMode ?? "all",
isDirectMessage: false,
isRoomish: false,
historyKey: "history-key",
@@ -248,13 +258,13 @@ vi.mock("../config.runtime.js", () => ({
vi.mock("../replies.js", () => ({
createSlackReplyDeliveryPlan: () => ({
peekThreadTs: () => THREAD_TS,
nextThreadTs: () => THREAD_TS,
peekThreadTs: () => mockedReplyThreadTs,
nextThreadTs: () => mockedReplyThreadTs,
markSent: () => {},
}),
deliverReplies: deliverRepliesMock,
readSlackReplyBlocks: () => undefined,
resolveSlackThreadTs: () => THREAD_TS,
resolveSlackThreadTs: () => mockedReplyThreadTs,
}));
vi.mock("../reply.runtime.js", () => ({
@@ -311,6 +321,7 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
mockedNativeStreaming = false;
mockedBlockStreamingEnabled = false;
capturedReplyOptions = undefined;
mockedReplyThreadTs = THREAD_TS;
mockedDispatchSequence = [{ kind: "final", payload: { text: FINAL_REPLY_TEXT } }];
createSlackDraftStreamMock.mockReturnValue(createDraftStreamStub());
@@ -347,6 +358,28 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
expect(capturedReplyOptions?.disableBlockStreaming).toBe(true);
});
it("starts native streams in the first-reply thread for top-level channel messages", async () => {
mockedNativeStreaming = true;
mockedReplyThreadTs = "171234.111";
mockedDispatchSequence = [{ kind: "final", payload: { text: FINAL_REPLY_TEXT } }];
await dispatchPreparedSlackMessage(
createPreparedSlackMessage({
message: { thread_ts: undefined },
replyToMode: "all",
}),
);
expect(startSlackStreamMock).toHaveBeenCalledWith(
expect.objectContaining({
channel: "C123",
threadTs: "171234.111",
text: FINAL_REPLY_TEXT,
}),
);
expect(deliverRepliesMock).not.toHaveBeenCalled();
});
it("keeps same-content tool and final payloads distinct after preview fallback", async () => {
mockedDispatchSequence = [
{ kind: "tool", payload: { text: SAME_TEXT } },

View File

@@ -156,6 +156,17 @@ describe("slack native streaming thread hint", () => {
).toBe("1000.2");
});
it("uses the message timestamp for top-level channel replies when replyToMode=all", () => {
expect(
resolveSlackStreamingThreadHint({
replyToMode: "all",
incomingThreadTs: undefined,
messageTs: "1000.4",
isThreadReply: false,
}),
).toBe("1000.4");
});
it("uses the existing incoming thread regardless of replyToMode", () => {
expect(
resolveSlackStreamingThreadHint({