import { describe, it, expect } from 'vitest'; import { parseNdjsonStream } from './ndjson.js'; function createReadableStream(chunks: string[]): ReadableStream { const encoder = new TextEncoder(); let index = 0; return new ReadableStream({ pull(controller) { if (index < chunks.length) { controller.enqueue(encoder.encode(chunks[index])); index++; } else { controller.close(); } }, }); } describe('parseNdjsonStream', () => { it('parses complete NDJSON lines', async () => { const stream = createReadableStream(['{"a":1}\n{"a":2}\n{"a":3}\n']); const results: unknown[] = []; for await (const chunk of parseNdjsonStream(stream)) { results.push(chunk); } expect(results).toEqual([{ a: 1 }, { a: 2 }, { a: 3 }]); }); it('handles partial lines split across chunks', async () => { const stream = createReadableStream(['{"a":', '1}\n{"a":2}\n']); const results: unknown[] = []; for await (const chunk of parseNdjsonStream(stream)) { results.push(chunk); } expect(results).toEqual([{ a: 1 }, { a: 2 }]); }); it('skips empty lines', async () => { const stream = createReadableStream(['{"a":1}\n\n\n{"a":2}\n']); const results: unknown[] = []; for await (const chunk of parseNdjsonStream(stream)) { results.push(chunk); } expect(results).toEqual([{ a: 1 }, { a: 2 }]); }); it('skips malformed JSON lines', async () => { const stream = createReadableStream(['{"a":1}\nnot json\n{"a":2}\n']); const results: unknown[] = []; for await (const chunk of parseNdjsonStream(stream)) { results.push(chunk); } expect(results).toEqual([{ a: 1 }, { a: 2 }]); }); it('processes remaining buffer after stream ends', async () => { const stream = createReadableStream(['{"a":1}\n{"a":2}']); const results: unknown[] = []; for await (const chunk of parseNdjsonStream(stream)) { results.push(chunk); } expect(results).toEqual([{ a: 1 }, { a: 2 }]); }); it('handles empty stream', async () => { const stream = createReadableStream([]); const results: unknown[] = []; for await (const chunk of parseNdjsonStream(stream)) { results.push(chunk); } expect(results).toEqual([]); }); });