import { expect, test } from 'vitest'; import { SSEEventParser } from '../../source/sse-session/sse-event-parser.ts'; import type { SSEvent } from '../../source/sse-session/types.ts'; import { edgeCases, priceOracleEvents, storageEvents, priceOracleEventsCombined, storageEventsCombined, edgeCasesCombined, } from './fixtures/events.fixtures.ts'; /** Shared encoder for turning fixture strings into stream bytes. */ const textEncoder = new TextEncoder(); /** * Tests that SSEEventParser parses a simple data event. */ const testSseEventParserParsesSimpleEvent = (): void => { const parser = new SSEEventParser(); const events = parser.parseEvents(textEncoder.encode('data: test\n\n')); expect(events).toEqual([{ data: 'test' }]); }; /** * Tests that SSEEventParser parses all fixture events correctly. */ const testSseEventParserParsesAllFixtures = (): void => { const parser = new SSEEventParser(); // Combine all individual event fixtures from each domain. const combinedEvents = [ ...priceOracleEvents, ...storageEvents, ...edgeCases ]; // Iterate over each combined fixture and test that the parser parses all events correctly. for (const { raw, parsed } of combinedEvents) { const events = parser.parseEvents(textEncoder.encode(raw)); expect(events).toEqual(parsed); } }; /** * Tests that SSEEventParser handles multiple events in the same chunk. */ const testSseEventParserHandlesMultipleEventsInOneChunk = (): void => { const parser = new SSEEventParser(); // Each combined fixture packs several events into one raw payload. const allEvents = [ priceOracleEventsCombined, storageEventsCombined, edgeCasesCombined ]; // Iterate over each combined fixture and test that the parser handles the multiple events in one chunk correctly. for (const { raw, parsed } of allEvents) { const bytes = textEncoder.encode(raw); const events = parser.parseEvents(bytes); expect(events).toEqual(parsed); } }; /** * Tests that SSEEventParser handles partial chunks delivered one character at a time. */ const testSseEventParserHandlesPartialChunks = (): void => { const fixtures = [ ...priceOracleEvents, ...storageEvents, ...edgeCases ]; // Iterate over each fixture and test that the parser handles the partial chunks correctly. for (const { raw, parsed } of fixtures) { const parser = new SSEEventParser(); const finalEvents: SSEvent[] = []; // Iterate over each character in the raw string and try to parse the events in its buffer for (const character of raw) { const bytes = textEncoder.encode(character); const events = parser.parseEvents(bytes); finalEvents.push(...events); } // Verify the events match the expected events. expect(finalEvents).toEqual(parsed ?? []); } }; /** * Tests that SSEEventParser handles partial byte chunks delivered one byte at a time. * This tests that unicode characters (like emojis) are still parsed correctly despite being split between two "chunks". */ const testSseEventParserHandlesPartialByteChunks = (): void => { // Combine all individual event fixtures from each domain. const fixtures = [ ...priceOracleEvents, ...storageEvents, ...edgeCases ]; // Iterate over each fixture and test that the parser handles the partial byte chunks correctly. for (const { raw, parsed } of fixtures) { const parser = new SSEEventParser(); const finalEvents: SSEvent[] = []; const bytes = textEncoder.encode(raw); // Iterate over each byte in the raw string and try to parse the events in its buffer for (const byte of bytes) { const uint8Bytes = Uint8Array.of(byte); const events = parser.parseEvents(uint8Bytes); finalEvents.push(...events); } // Verify the events match the expected events. expect(finalEvents).toEqual(parsed ?? []); } }; /** * Tests that SSEEventParser clears its buffer when reset is called. */ const testSseEventParserClearsBufferOnReset = (): void => { // Create a new parser. const parser = new SSEEventParser(); // Parse the events in the buffer. parser.parseEvents(textEncoder.encode('data: stale')); // Reset the parser. parser.reset(); // Parse the events in the buffer. const events = parser.parseEvents(textEncoder.encode('data: fresh\n\n')); // Verify the events match the expected events. expect(events).toEqual([ { data: 'fresh', }, ]); }; const runTests = async (): Promise => { test('SSEEventParser: parses a simple data event', testSseEventParserParsesSimpleEvent); test('SSEEventParser: parses all fixture events', testSseEventParserParsesAllFixtures); test('SSEEventParser: handles multiple events in one chunk', testSseEventParserHandlesMultipleEventsInOneChunk); test('SSEEventParser: handles partial chunks', testSseEventParserHandlesPartialChunks); test('SSEEventParser: handles partial byte chunks', testSseEventParserHandlesPartialByteChunks); test('SSEEventParser: clears the buffer on reset', testSseEventParserClearsBufferOnReset); }; await runTests();