import { describe, it, expect } from "vitest"; import { splitForDiscord } from "../../src/discord/split"; const DISCORD_HARD_LIMIT = 2000; describe("splitForDiscord", () => { // (a) Short message below limit → returned as single unchanged chunk it("returns a single chunk for short messages", () => { const text = "Hello, world!"; const result = splitForDiscord(text); expect(result).toHaveLength(1); expect(result[0]).toBe(text); }); // (b) Text exactly at the limit → single chunk it("returns a single chunk when text is exactly at the limit", () => { const text = "a".repeat(1900); const result = splitForDiscord(text, 1900); expect(result).toHaveLength(1); expect(result[0]).toBe(text); }); // (c) Long plain text → split at line boundaries it("splits long plain text at line boundaries", () => { const line = "x".repeat(100); // 25 lines = 25*100 + 24 newlines = 2524 chars → must split with limit 1900 const text = Array.from({ length: 25 }, () => line).join("\n"); const result = splitForDiscord(text, 1900); expect(result.length).toBeGreaterThan(1); // Reassembled text must equal original expect(result.join("\n")).toBe(text); }); // (d) Code block spanning a split → fence is closed and reopened with lang tag it("closes and reopens fenced code blocks across chunk boundaries", () => { const preamble = "Here is some code:\n"; const fenceOpen = "```typescript\n"; // Fill enough code lines to force a split inside the fence const codeLine = "const x = 1; // " + "y".repeat(80) + "\n"; const codeLines = Array.from({ length: 20 }, () => codeLine).join(""); const fenceClose = "```\n"; const epilogue = "End of message."; const text = preamble + fenceOpen + codeLines + fenceClose + epilogue; const result = splitForDiscord(text, 1900); expect(result.length).toBeGreaterThan(1); // Every chunk that starts mid-fence must open with ```typescript // Every chunk that ends mid-fence must close with ``` for (let i = 0; i < result.length - 1; i++) { const chunk = result[i]; // Count fence toggles to determine if we end inside a fence const fenceToggles = (chunk.match(/^```/gm) ?? []).length; if (fenceToggles % 2 === 1) { // Odd number means we opened but didn't close — the splitter must close it expect(chunk.trimEnd()).toMatch(/```$/); // And the next chunk must reopen with the same language expect(result[i + 1]).toMatch(/^```typescript/); } } }); // (e) No chunk exceeds 2000 characters it("never produces a chunk exceeding 2000 characters", () => { const line = "z".repeat(150); const text = Array.from({ length: 50 }, () => line).join("\n"); const result = splitForDiscord(text, 1900); for (const chunk of result) { expect(chunk.length).toBeLessThanOrEqual(DISCORD_HARD_LIMIT); } }); // (f) Empty input → no crash, returns consistent value it("handles empty input without crashing", () => { expect(() => splitForDiscord("")).not.toThrow(); const result = splitForDiscord(""); expect(Array.isArray(result)).toBe(true); }); // Additional: fence language tag is preserved correctly it("preserves the fence language tag when reopening after a split", () => { const limit = 200; const header = "Intro\n"; const fenceOpen = "```python\n"; // Enough lines to exceed 200 chars inside the fence const codeLine = "print('hello')\n"; const codeLines = Array.from({ length: 20 }, () => codeLine).join(""); const fenceClose = "```"; const text = header + fenceOpen + codeLines + fenceClose; const result = splitForDiscord(text, limit); // Find the first chunk that ends with ``` (closed fence) const closingChunk = result.find( (c, i) => i < result.length - 1 && c.trimEnd().endsWith("```") ); expect(closingChunk).toBeDefined(); // The chunk following a mid-fence close should start with ```python const idx = result.indexOf(closingChunk!); expect(result[idx + 1]).toMatch(/^```python/); }); // Regression: fence closure "\n```" must not push a chunk beyond the 2000 hard limit it("does not exceed 2000 chars even when fence closure is appended at limit boundary", () => { const fenceOpen = "```typescript\n"; // Line long enough that current is near 2000 when fence closure is added const longLine = "x".repeat(1984); const nextLine = "y".repeat(10); const fenceClose = "\n```"; const text = fenceOpen + longLine + "\n" + nextLine + fenceClose; const result = splitForDiscord(text, 2000); for (const chunk of result) { expect(chunk.length).toBeLessThanOrEqual(DISCORD_HARD_LIMIT); } }); });