- 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>
70 lines
2.3 KiB
TypeScript
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();
|
|
});
|
|
});
|