1+ import { v4 as randomUUID } from 'uuid' ;
2+
13import ArrayBackedNamedEventQueue from './array-backed-named-event-queue' ;
24import BatchEventProcessor from './batch-event-processor' ;
35import DefaultEventDispatcher , {
@@ -120,6 +122,7 @@ describe('DefaultEventDispatcher', () => {
120122 let fetchOptions = fetch . mock . calls [ 0 ] [ 1 ] ;
121123 let payload = JSON . parse ( fetchOptions . body ) ;
122124 expect ( payload ) . toEqual ( {
125+ context : { } ,
123126 eppo_events : [
124127 expect . objectContaining ( { payload : { foo : 'event1' } } ) ,
125128 expect . objectContaining ( { payload : { foo : 'event2' } } ) ,
@@ -139,6 +142,7 @@ describe('DefaultEventDispatcher', () => {
139142 fetchOptions = fetch . mock . calls [ 1 ] [ 1 ] ;
140143 payload = JSON . parse ( fetchOptions . body ) ;
141144 expect ( payload ) . toEqual ( {
145+ context : { } ,
142146 eppo_events : [ expect . objectContaining ( { payload : { foo : 'event3' } } ) ] ,
143147 } ) ;
144148 } ) ;
@@ -319,6 +323,57 @@ describe('DefaultEventDispatcher', () => {
319323 } ) ;
320324 } ) ;
321325
326+ describe ( 'attachContext' , ( ) => {
327+ it ( 'should throw an error if the value is an object' , ( ) => {
328+ const eventQueue = new ArrayBackedNamedEventQueue < Event > ( 'test-queue' ) ;
329+ const { dispatcher } = createDispatcher ( { maxRetries : 1 } , eventQueue ) ;
330+ expect ( ( ) => dispatcher . attachContext ( 'foo' , { } as any ) ) . toThrow ( ) ;
331+ expect ( ( ) => dispatcher . attachContext ( 'foo' , [ ] as any ) ) . toThrow ( ) ;
332+ } ) ;
333+
334+ it ( 'should not throw an error if the value is a string, number, boolean, or null' , ( ) => {
335+ const eventQueue = new ArrayBackedNamedEventQueue < Event > ( 'test-queue' ) ;
336+ const { dispatcher } = createDispatcher ( { maxRetries : 1 } , eventQueue ) ;
337+ expect ( ( ) => dispatcher . attachContext ( 'foo' , 'bar' ) ) . not . toThrow ( ) ;
338+ expect ( ( ) => dispatcher . attachContext ( 'foo' , 1 ) ) . not . toThrow ( ) ;
339+ expect ( ( ) => dispatcher . attachContext ( 'foo' , true ) ) . not . toThrow ( ) ;
340+ expect ( ( ) => dispatcher . attachContext ( 'foo' , null ) ) . not . toThrow ( ) ;
341+ } ) ;
342+
343+ it ( 'should throw an error if the context value is too long' , ( ) => {
344+ const eventQueue = new ArrayBackedNamedEventQueue < Event > ( 'test-queue' ) ;
345+ const { dispatcher } = createDispatcher ( { maxRetries : 1 } , eventQueue ) ;
346+ expect ( ( ) => dispatcher . attachContext ( 'foo' , 'a' . repeat ( 2049 ) ) ) . toThrow ( ) ;
347+ } ) ;
348+
349+ it ( 'attaches a context to be included with all events dispatched by this dispatcher' , async ( ) => {
350+ const eventQueue = new ArrayBackedNamedEventQueue < Event > ( 'test-queue' ) ;
351+ const { dispatcher } = createDispatcher ( { maxRetries : 1 } , eventQueue ) ;
352+ dispatcher . attachContext ( 'foo' , 'bar' ) ;
353+ dispatcher . attachContext ( 'baz' , 'qux' ) ;
354+ const event = {
355+ uuid : randomUUID ( ) ,
356+ payload : { foo : 'event1' } ,
357+ timestamp : new Date ( ) . getTime ( ) ,
358+ type : 'foo' ,
359+ } ;
360+ dispatcher . dispatch ( event ) ;
361+ const fetch = global . fetch as jest . Mock ;
362+ fetch . mockResolvedValue ( { ok : true , json : ( ) => Promise . resolve ( [ ] ) } ) ;
363+
364+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
365+
366+ expect ( global . fetch ) . toHaveBeenCalledWith ( 'http://example.com' , {
367+ method : 'POST' ,
368+ headers : { 'Content-Type' : 'application/json' , 'x-eppo-token' : 'test-sdk-key' } ,
369+ body : JSON . stringify ( {
370+ eppo_events : [ event ] ,
371+ context : { foo : 'bar' , baz : 'qux' } ,
372+ } ) ,
373+ } ) ;
374+ } ) ;
375+ } ) ;
376+
322377 describe ( 'validation' , ( ) => {
323378 it ( 'should throw an error if the serialized event is too long' , ( ) => {
324379 const { dispatcher } = createDispatcher ( ) ;
0 commit comments