import { describe, expect, it } from 'bun:test' import { readLines } from './stream.ts' function streamOf(chunks: string[]): ReadableStream { const encoder = new TextEncoder() return new ReadableStream({ start(controller) { for (const chunk of chunks) controller.enqueue(encoder.encode(chunk)) controller.close() }, }) } async function collect(chunks: string[]): Promise { const lines: string[] = [] for await (const line of readLines(streamOf(chunks))) lines.push(line) return lines } describe('按行读流', () => { it('一块里有好几行就拆开', async () => { expect(await collect(['a\nb\nc\n'])).toEqual(['a', 'b', 'c']) }) it('半行留在缓冲里,等下一块补齐', async () => { // 分块边界和行边界无关,这是这个模块存在的全部理由 expect(await collect(['{"up":1,"do', 'wn":2}\n{"up":3}\n'])).toEqual([ '{"up":1,"down":2}', '{"up":3}', ]) }) it('流结束时缓冲里剩的半行也交出去', async () => { expect(await collect(['a\nb'])).toEqual(['a', 'b']) }) it('空行丢弃,\\r\\n 换行的 \\r 去掉', async () => { expect(await collect(['a\r\n\n\nb\n'])).toEqual(['a', 'b']) }) it('多字节字符跨块也不会读坏', async () => { const bytes = new TextEncoder().encode('香港01\n') const stream = new ReadableStream({ start(controller) { // 故意从「港」字中间切开 controller.enqueue(bytes.slice(0, 4)) controller.enqueue(bytes.slice(4)) controller.close() }, }) const lines: string[] = [] for await (const line of readLines(stream)) lines.push(line) expect(lines).toEqual(['香港01']) }) })