import { describe, expect, it } from
"vitest";
import { createSlackSendTestClient, installSlackBlockTestMocks } from
"./blocks.test-helpers.js";
installSlackBlockTestMocks();
const { sendMessageSlack } = await
import(
"./send.js");
const SLACK_TEST_CFG = { channels: { slack: { botToken:
"xoxb-test" } } };
describe(
"sendMessageSlack NO_REPLY guard", () => {
it(
"suppresses NO_REPLY text before any Slack API call", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack(
"channel:C123",
"NO_REPLY", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
});
expect(client.chat.postMessage).not.toHaveBeenCalled();
expect(result.messageId).toBe(
"suppressed");
});
it(
"suppresses NO_REPLY with surrounding whitespace", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack(
"channel:C123",
" NO_REPLY ", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
});
expect(client.chat.postMessage).not.toHaveBeenCalled();
expect(result.messageId).toBe(
"suppressed");
});
it(
"does not suppress substantive text containing NO_REPLY", async () => {
const client = createSlackSendTestClient();
await sendMessageSlack(
"channel:C123",
"This is not a NO_REPLY situation", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
});
expect(client.chat.postMessage).toHaveBeenCalled();
});
it(
"does not suppress NO_REPLY when blocks are attached", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack(
"channel:C123",
"NO_REPLY", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [{ type:
"section", text: { type:
"mrkdwn", text:
"content" } }],
});
expect(client.chat.postMessage).toHaveBeenCalled();
expect(result.messageId).toBe(
"171234.567");
});
});
describe(
"sendMessageSlack chunking", () => {
it(
"keeps 4205-character text in a single Slack post by default", async () => {
const client = createSlackSendTestClient();
const message =
"a".repeat(
4205);
await sendMessageSlack(
"channel:C123", message, {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
});
expect(client.chat.postMessage).toHaveBeenCalledTimes(
1);
expect(client.chat.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel:
"C123",
text: message,
}),
);
});
it(
"splits oversized fallback text through the normal Slack sender", async () => {
const client = createSlackSendTestClient();
const message =
"a".repeat(
8500);
await sendMessageSlack(
"channel:C123", message, {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
});
const postedTexts = client.chat.postMessage.mock.calls.map((call) => call[
0].text);
expect(postedTexts).toHaveLength(
2);
expect(postedTexts.every((text) =>
typeof text ===
"string" && text.length <=
8000)).toBe(
true);
expect(postedTexts.join(
"")).toBe(message);
});
});
describe(
"sendMessageSlack blocks", () => {
it(
"posts blocks with fallback text when message is empty", async () => {
const client = createSlackSendTestClient();
const result = await sendMessageSlack(
"channel:C123",
"", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [{ type:
"divider" }],
});
expect(client.conversations.open).not.toHaveBeenCalled();
expect(client.chat.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel:
"C123",
text:
"Shared a Block Kit message",
blocks: [{ type:
"divider" }],
}),
);
expect(result).toEqual({ messageId:
"171234.567", channelId:
"C123" });
});
it(
"derives fallback text from image blocks", async () => {
const client = createSlackSendTestClient();
await sendMessageSlack(
"channel:C123",
"", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [{ type:
"image", image_url:
"https://example.com/a.png", alt_text: "Build chart" }],
});
expect(client.chat.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
text:
"Build chart",
}),
);
});
it(
"derives fallback text from video blocks", async () => {
const client = createSlackSendTestClient();
await sendMessageSlack(
"channel:C123",
"", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [
{
type:
"video",
title: { type:
"plain_text", text:
"Release demo" },
video_url:
"https://example.com/demo.mp4",
thumbnail_url:
"https://example.com/thumb.jpg",
alt_text:
"demo",
},
],
});
expect(client.chat.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
text:
"Release demo",
}),
);
});
it(
"derives fallback text from file blocks", async () => {
const client = createSlackSendTestClient();
await sendMessageSlack(
"channel:C123",
"", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [{ type:
"file", source:
"remote", external_id:
"F123" }],
});
expect(client.chat.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
text:
"Shared a file",
}),
);
});
it(
"rejects blocks combined with mediaUrl", async () => {
const client = createSlackSendTestClient();
await expect(
sendMessageSlack(
"channel:C123",
"hi", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
mediaUrl:
"https://example.com/image.png",
blocks: [{ type:
"divider" }],
}),
).rejects.toThrow(/does not support blocks with mediaUrl/i);
expect(client.chat.postMessage).not.toHaveBeenCalled();
});
it(
"rejects empty blocks arrays from runtime callers", async () => {
const client = createSlackSendTestClient();
await expect(
sendMessageSlack(
"channel:C123",
"hi", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [],
}),
).rejects.toThrow(/must contain at least one block/i);
expect(client.chat.postMessage).not.toHaveBeenCalled();
});
it(
"rejects blocks arrays above Slack max count", async () => {
const client = createSlackSendTestClient();
const blocks = Array.from({ length:
51 }, () => ({ type:
"divider" }));
await expect(
sendMessageSlack(
"channel:C123",
"hi", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks,
}),
).rejects.toThrow(/cannot exceed
50 items/i);
expect(client.chat.postMessage).not.toHaveBeenCalled();
});
it(
"rejects blocks missing type from runtime callers", async () => {
const client = createSlackSendTestClient();
await expect(
sendMessageSlack(
"channel:C123",
"hi", {
token:
"xoxb-test",
cfg: SLACK_TEST_CFG,
client,
blocks: [{} as { type: string }],
}),
).rejects.toThrow(/non-empty string type/i);
expect(client.chat.postMessage).not.toHaveBeenCalled();
});
});