import 'dart:convert'; import 'package:halkyon_llm/src/sse.dart'; import 'package:test/test.dart'; Future> _events(List chunks) async => [ await for (final event in parseSse( Stream.fromIterable(chunks.map(utf8.encode)), )) (event.type, event.data), ]; void main() { group('parseSse, as the HTML Standard reads an event stream', () { test('an event, typed, its data after one space', () async { expect(await _events(['event: ping\ndata: {"a":1}\n\n']), [ ('ping', '{"a":1}'), ]); }); test('untyped events are messages', () async { expect(await _events(['data: x\n\n']), [('message', 'x')]); }); test('data lines are joined by line feeds', () async { expect(await _events(['data: a\ndata: b\ndata:c\n\n']), [ ('message', 'a\nb\nc'), ]); }); test('only one space after the colon is taken off', () async { expect(await _events(['data: two\n\n']), [('message', ' two')]); }); test( 'lines end in CR LF, LF or CR, also when a chunk cuts CR LF', () async { expect( await _events(['data: a\r', '\n\r\ndata: b\r\rdata: c\n\n']), [('message', 'a'), ('message', 'b'), ('message', 'c')], ); }, ); test('comments, id and retry are skipped', () async { expect( await _events([': keep-alive\nid: 7\nretry: 100\ndata: x\n\n']), [('message', 'x')], ); }); test('a field without a colon has an empty value', () async { expect(await _events(['data\n\n']), [('message', '')]); }); test('an event without data is not dispatched', () async { expect(await _events(['event: ping\n\ndata: x\n\n']), [('message', 'x')]); }); test('an event the stream ends in the middle of is dropped', () async { expect(await _events(['data: a\n\ndata: b\n']), [('message', 'a')]); }); test('a byte order mark is skipped', () async { expect(await _events(['\uFEFFdata: x\n\n']), [('message', 'x')]); }); test('characters cut in two by chunks are put together', () async { final bytes = utf8.encode('data: 犬 (いぬ)\n\n'); final events = await parseSse( Stream.fromIterable([ for (final byte in bytes) [byte], ]), ).toList(); expect(events.single.data, '犬 (いぬ)'); }); test('bytes that are not UTF-8 fail as a FormatException', () { expect( parseSse(Stream.value([0x64, 0x61, 0x74, 0x61, 0x3A, 0xFF, 0x0A, 0x0A])) .toList(), throwsFormatException, ); }); }); }