disclaw/tests/unit/send-response.test.ts
Nick Tabeling 9e03fc5207
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
feat(DIS-154): discord rich features — reactions, file responses, attachment inbox
- Status reactions on messages: 👀 on enqueue,  on success,  on any error
- Typing interval raised from 5s to 8s
- src/discord/send-response.ts: text >4000 chars sent as response.md attachment
- src/discord/attachment-inbox.ts: downloads Discord attachments into .disclaw-inbox/, cleanupInbox() removes files older than 24h
- src/router.ts: input limit guard at 4000 chars (⚠️ reaction + hint message), processAttachments hints injected into prompt
- Unit tests: send-response.test.ts, attachment-inbox.test.ts (107 tests, all green)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 09:58:57 +02:00

70 lines
2.3 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { sendResponse } from "../../src/discord/send-response";
import type { TextChannel, Message } from "discord.js";
// Minimal stubs
function makeChannel() {
return {
send: vi.fn().mockResolvedValue({ id: "msg-1" }),
} as unknown as TextChannel;
}
function makeMessage() {
return {} as Message;
}
describe("sendResponse", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("sends chunks via channel.send() for text <= 4000 chars", async () => {
const channel = makeChannel();
const text = "Hello, world!";
await sendResponse(channel, text, makeMessage());
expect(channel.send).toHaveBeenCalledTimes(1);
expect(channel.send).toHaveBeenCalledWith(text);
// No file attachment
const call = vi.mocked(channel.send).mock.calls[0][0];
expect(typeof call).toBe("string");
});
it("sends multiple chunks for text <= 4000 chars that needs splitting", async () => {
const channel = makeChannel();
// Build a text that exceeds the 1900 default split limit but stays <= 4000
const line = "a".repeat(100);
const text = Array.from({ length: 30 }, () => line).join("\n");
// 30*100 + 29 newlines = 3029 chars — within 4000 but split into chunks
expect(text.length).toBeLessThanOrEqual(4000);
await sendResponse(channel, text, makeMessage());
expect(channel.send).toHaveBeenCalledTimes(
(channel.send as ReturnType<typeof vi.fn>).mock.calls.length
);
// All calls must be strings, not objects with files
for (const [arg] of vi.mocked(channel.send).mock.calls) {
expect(typeof arg).toBe("string");
}
});
it("sends an attachment for text > 4000 chars", async () => {
const channel = makeChannel();
const text = "x".repeat(4001);
await sendResponse(channel, text, makeMessage());
expect(channel.send).toHaveBeenCalledTimes(1);
const call = vi.mocked(channel.send).mock.calls[0][0];
expect(typeof call).toBe("object");
expect((call as { files: unknown[] }).files).toBeDefined();
expect((call as { files: unknown[] }).files).toHaveLength(1);
});
it("does not call channel.send() for empty text", async () => {
const channel = makeChannel();
await sendResponse(channel, "", makeMessage());
expect(channel.send).not.toHaveBeenCalled();
});
});