import { describe, expect, it } from "vitest"; import { chunkDiscordText } from "./chunk.js"; function countLines(text: string) { return text.split("\n").length; } function hasBalancedFences(chunk: string) { let open: { markerChar: string; markerLen: number } | null = null; for (const line of chunk.split("\n")) { const match = line.match(/^( {0,3})(`{3,}|~{3,})(.*)$/); if (!match) continue; const marker = match[2]; if (!open) { open = { markerChar: marker[0], markerLen: marker.length }; continue; } if (open.markerChar === marker[0] && marker.length >= open.markerLen) { open = null; } } return open === null; } describe("chunkDiscordText", () => { it("splits tall messages even when under 2000 chars", () => { const text = Array.from({ length: 45 }, (_, i) => `line-${i + 1}`).join( "\n", ); expect(text.length).toBeLessThan(2000); const chunks = chunkDiscordText(text, { maxChars: 2000, maxLines: 20 }); expect(chunks.length).toBeGreaterThan(1); for (const chunk of chunks) { expect(countLines(chunk)).toBeLessThanOrEqual(20); } }); it("keeps fenced code blocks balanced across chunks", () => { const body = Array.from( { length: 30 }, (_, i) => `console.log(${i});`, ).join("\n"); const text = `Here is code:\n\n\`\`\`js\n${body}\n\`\`\`\n\nDone.`; const chunks = chunkDiscordText(text, { maxChars: 2000, maxLines: 10 }); expect(chunks.length).toBeGreaterThan(1); for (const chunk of chunks) { expect(hasBalancedFences(chunk)).toBe(true); expect(chunk.length).toBeLessThanOrEqual(2000); } expect(chunks[0]).toContain("```js"); expect(chunks.at(-1)).toContain("Done."); }); it("reserves space for closing fences when chunking", () => { const body = "a".repeat(120); const text = `\`\`\`txt\n${body}\n\`\`\``; const chunks = chunkDiscordText(text, { maxChars: 50, maxLines: 50 }); expect(chunks.length).toBeGreaterThan(1); for (const chunk of chunks) { expect(chunk.length).toBeLessThanOrEqual(50); expect(hasBalancedFences(chunk)).toBe(true); } }); });