1
1
import { LDContext , LDEvaluationDetail , LDLogger } from '@launchdarkly/js-sdk-common' ;
2
2
3
- import { Hook , IdentifySeriesResult } from '../src/api/integrations/Hooks' ;
4
- import HookRunner from '../src/HookRunner' ;
3
+ import { Hook , IdentifySeriesResult } from '../../ src/api/integrations/Hooks' ;
4
+ import HookRunner from '../../ src/HookRunner' ;
5
5
6
6
describe ( 'given a hook runner and test hook' , ( ) => {
7
7
let logger : LDLogger ;
@@ -22,6 +22,7 @@ describe('given a hook runner and test hook', () => {
22
22
afterEvaluation : jest . fn ( ) ,
23
23
beforeIdentify : jest . fn ( ) ,
24
24
afterIdentify : jest . fn ( ) ,
25
+ afterTrack : jest . fn ( ) ,
25
26
} ;
26
27
27
28
hookRunner = new HookRunner ( logger , [ testHook ] ) ;
@@ -301,4 +302,125 @@ describe('given a hook runner and test hook', () => {
301
302
) ,
302
303
) ;
303
304
} ) ;
305
+
306
+ it ( 'should execute afterTrack hooks' , ( ) => {
307
+ const context : LDContext = { kind : 'user' , key : 'user-123' } ;
308
+ const key = 'test' ;
309
+ const data = { test : 'data' } ;
310
+ const metricValue = 42 ;
311
+
312
+ const trackContext = {
313
+ key,
314
+ context,
315
+ data,
316
+ metricValue,
317
+ } ;
318
+
319
+ testHook . afterTrack = jest . fn ( ) ;
320
+
321
+ hookRunner . afterTrack ( trackContext ) ;
322
+
323
+ expect ( testHook . afterTrack ) . toHaveBeenCalledWith ( trackContext ) ;
324
+ } ) ;
325
+
326
+ it ( 'should handle errors in afterTrack hooks' , ( ) => {
327
+ const errorHook : Hook = {
328
+ getMetadata : jest . fn ( ) . mockReturnValue ( { name : 'Error Hook' } ) ,
329
+ afterTrack : jest . fn ( ) . mockImplementation ( ( ) => {
330
+ throw new Error ( 'Hook error' ) ;
331
+ } ) ,
332
+ } ;
333
+
334
+ const errorHookRunner = new HookRunner ( logger , [ errorHook ] ) ;
335
+
336
+ errorHookRunner . afterTrack ( {
337
+ key : 'test' ,
338
+ context : { kind : 'user' , key : 'user-123' } ,
339
+ } ) ;
340
+
341
+ expect ( logger . error ) . toHaveBeenCalledWith (
342
+ expect . stringContaining (
343
+ 'An error was encountered in "afterTrack" of the "Error Hook" hook: Error: Hook error' ,
344
+ ) ,
345
+ ) ;
346
+ } ) ;
347
+
348
+ it ( 'should skip afterTrack execution if there are no hooks' , ( ) => {
349
+ const emptyHookRunner = new HookRunner ( logger , [ ] ) ;
350
+
351
+ emptyHookRunner . afterTrack ( {
352
+ key : 'test' ,
353
+ context : { kind : 'user' , key : 'user-123' } ,
354
+ } ) ;
355
+
356
+ expect ( logger . error ) . not . toHaveBeenCalled ( ) ;
357
+ } ) ;
358
+
359
+ it ( 'executes hook stages in the specified order' , ( ) => {
360
+ const beforeEvalOrder : string [ ] = [ ] ;
361
+ const afterEvalOrder : string [ ] = [ ] ;
362
+ const beforeIdentifyOrder : string [ ] = [ ] ;
363
+ const afterIdentifyOrder : string [ ] = [ ] ;
364
+ const afterTrackOrder : string [ ] = [ ] ;
365
+
366
+ const createMockHook = ( id : string ) : Hook => ( {
367
+ getMetadata : jest . fn ( ) . mockReturnValue ( { name : `Hook ${ id } ` } ) ,
368
+ beforeEvaluation : jest . fn ( ) . mockImplementation ( ( _context , data ) => {
369
+ beforeEvalOrder . push ( id ) ;
370
+ return data ;
371
+ } ) ,
372
+ afterEvaluation : jest . fn ( ) . mockImplementation ( ( _context , data , _detail ) => {
373
+ afterEvalOrder . push ( id ) ;
374
+ return data ;
375
+ } ) ,
376
+ beforeIdentify : jest . fn ( ) . mockImplementation ( ( _context , data ) => {
377
+ beforeIdentifyOrder . push ( id ) ;
378
+ return data ;
379
+ } ) ,
380
+ afterIdentify : jest . fn ( ) . mockImplementation ( ( _context , data , _result ) => {
381
+ afterIdentifyOrder . push ( id ) ;
382
+ return data ;
383
+ } ) ,
384
+ afterTrack : jest . fn ( ) . mockImplementation ( ( ) => {
385
+ afterTrackOrder . push ( id ) ;
386
+ } ) ,
387
+ } ) ;
388
+
389
+ const hookA = createMockHook ( 'a' ) ;
390
+ const hookB = createMockHook ( 'b' ) ;
391
+ const hookC = createMockHook ( 'c' ) ;
392
+
393
+ const runner = new HookRunner ( logger , [ hookA , hookB ] ) ;
394
+ runner . addHook ( hookC ) ;
395
+
396
+ // Test evaluation order
397
+ runner . withEvaluation ( 'flagKey' , { kind : 'user' , key : 'bob' } , 'default' , ( ) => ( {
398
+ value : false ,
399
+ reason : { kind : 'ERROR' , errorKind : 'FLAG_NOT_FOUND' } ,
400
+ variationIndex : null ,
401
+ } ) ) ;
402
+
403
+ // Test identify order
404
+ const identifyCallback = runner . identify ( { kind : 'user' , key : 'bob' } , 1000 ) ;
405
+ identifyCallback ( { status : 'completed' } ) ;
406
+
407
+ // Test track order
408
+ runner . afterTrack ( {
409
+ key : 'test' ,
410
+ context : { kind : 'user' , key : 'bob' } ,
411
+ data : { test : 'data' } ,
412
+ metricValue : 42 ,
413
+ } ) ;
414
+
415
+ // Verify evaluation hooks order
416
+ expect ( beforeEvalOrder ) . toEqual ( [ 'a' , 'b' , 'c' ] ) ;
417
+ expect ( afterEvalOrder ) . toEqual ( [ 'c' , 'b' , 'a' ] ) ;
418
+
419
+ // Verify identify hooks order
420
+ expect ( beforeIdentifyOrder ) . toEqual ( [ 'a' , 'b' , 'c' ] ) ;
421
+ expect ( afterIdentifyOrder ) . toEqual ( [ 'c' , 'b' , 'a' ] ) ;
422
+
423
+ // Verify track hooks order
424
+ expect ( afterTrackOrder ) . toEqual ( [ 'c' , 'b' , 'a' ] ) ;
425
+ } ) ;
304
426
} ) ;
0 commit comments