import { expect, tap } from '@git.zone/tstest/tapbundle'; import { LineScanner } from '../ts/classes.linescanner.js'; const noopLogger = { log() {} }; tap.test('should parse a single complete line', async () => { const scanner = new LineScanner(1024 * 1024, noopLogger); const lines: string[] = []; scanner.push(Buffer.from('{"hello":"world"}\n'), (line) => lines.push(line)); expect(lines.length).toEqual(1); expect(lines[0]).toEqual('{"hello":"world"}'); }); tap.test('should parse multiple lines in one chunk', async () => { const scanner = new LineScanner(1024 * 1024, noopLogger); const lines: string[] = []; scanner.push(Buffer.from('{"a":1}\n{"b":2}\n{"c":3}\n'), (line) => lines.push(line)); expect(lines.length).toEqual(3); expect(lines[0]).toEqual('{"a":1}'); expect(lines[1]).toEqual('{"b":2}'); expect(lines[2]).toEqual('{"c":3}'); }); tap.test('should handle a line split across chunks', async () => { const scanner = new LineScanner(1024 * 1024, noopLogger); const lines: string[] = []; scanner.push(Buffer.from('{"hel'), (line) => lines.push(line)); expect(lines.length).toEqual(0); scanner.push(Buffer.from('lo":"world"}\n'), (line) => lines.push(line)); expect(lines.length).toEqual(1); expect(lines[0]).toEqual('{"hello":"world"}'); }); tap.test('should drop oversized lines', async () => { const scanner = new LineScanner(100, noopLogger); const lines: string[] = []; // Line is 200 chars + newline, exceeds maxPayloadSize of 100 const oversized = 'x'.repeat(200) + '\n'; scanner.push(Buffer.from(oversized), (line) => lines.push(line)); expect(lines.length).toEqual(0); }); tap.test('should clear buffer on OOM (no newline, exceeds max)', async () => { const scanner = new LineScanner(100, noopLogger); const lines: string[] = []; // Push 200 bytes without any newline — exceeds maxPayloadSize scanner.push(Buffer.from('x'.repeat(200)), (line) => lines.push(line)); expect(lines.length).toEqual(0); // After clearing, should work normally again scanner.push(Buffer.from('{"ok":true}\n'), (line) => lines.push(line)); expect(lines.length).toEqual(1); expect(lines[0]).toEqual('{"ok":true}'); }); tap.test('should skip empty lines', async () => { const scanner = new LineScanner(1024 * 1024, noopLogger); const lines: string[] = []; scanner.push(Buffer.from('\n\n{"a":1}\n\n'), (line) => lines.push(line)); expect(lines.length).toEqual(1); expect(lines[0]).toEqual('{"a":1}'); }); tap.test('should handle mixed complete and partial lines', async () => { const scanner = new LineScanner(1024 * 1024, noopLogger); const lines: string[] = []; // First chunk: one complete line + start of second line scanner.push(Buffer.from('{"first":1}\n{"sec'), (line) => lines.push(line)); expect(lines.length).toEqual(1); // Second chunk: end of second line + complete third line scanner.push(Buffer.from('ond":2}\n{"third":3}\n'), (line) => lines.push(line)); expect(lines.length).toEqual(3); expect(lines[1]).toEqual('{"second":2}'); expect(lines[2]).toEqual('{"third":3}'); }); tap.test('clear should reset the buffer', async () => { const scanner = new LineScanner(1024 * 1024, noopLogger); const lines: string[] = []; // Push partial data scanner.push(Buffer.from('{"partial":'), (line) => lines.push(line)); // Clear scanner.clear(); // Now push a complete new line — old partial should not affect it scanner.push(Buffer.from('{"fresh":true}\n'), (line) => lines.push(line)); expect(lines.length).toEqual(1); expect(lines[0]).toEqual('{"fresh":true}'); }); export default tap.start();