@@ -943,11 +943,12 @@ describe('Optimizely', () => {
943943 trafficAllocation : [ { entityId : `${ id } _var` , endOfRange : 10000 } ] ,
944944 } ) ;
945945
946- const setup = ( localHoldouts : any [ ] , sendFlagDecisions = true ) => {
946+ const setup = ( localHoldouts : any [ ] , sendFlagDecisions = true , cmabService : any = { } ) => {
947947 const datafile : any = getDecisionTestDatafile ( ) ;
948948 datafile . holdouts = [ ] ;
949949 datafile . localHoldouts = localHoldouts ;
950950 datafile . sendFlagDecisions = sendFlagDecisions ;
951+ datafile . experiments . find ( ( e : any ) => e . key === 'exp_3' ) . cmab . trafficAllocation = 10000 ;
951952 const eventProcessor = getForwardingEventProcessor ( {
952953 dispatchEvent : vi . fn ( ( ) => Promise . resolve ( { statusCode : 200 } ) ) ,
953954 } ) ;
@@ -962,7 +963,7 @@ describe('Optimizely', () => {
962963 logger,
963964 odpManager,
964965 disposable : true ,
965- cmabService : { } as any
966+ cmabService,
966967 } ) ;
967968 const holdoutEvents = ( ) => processSpy . mock . calls
968969 . map ( ( call : any ) => call [ 0 ] as ImpressionEvent )
@@ -1012,6 +1013,22 @@ describe('Optimizely', () => {
10121013 expect ( processSpy ) . not . toHaveBeenCalled ( ) ;
10131014 } ) ;
10141015
1016+ it ( 'should return an error decision and send no impressions when a later rule errors after a local holdout hit' , async ( ) => {
1017+ const cmabService = { getDecision : vi . fn ( ) . mockRejectedValue ( new Error ( 'cmab fetch failed' ) ) } ;
1018+ const { optimizely, processSpy } = setup ( [ makeLocalHoldout ( 'lh' , [ '2001' ] ) ] , true , cmabService ) ;
1019+ const user = new OptimizelyUserContext ( { optimizely, userId : 'tester' , attributes : { country : 'BD' , age : 80 } } ) ;
1020+
1021+ const decision = await optimizely . decideAsync ( user , 'flag_1' , [ OptimizelyDecideOption . INCLUDE_REASONS ] ) ;
1022+
1023+ expect ( cmabService . getDecision ) . toHaveBeenCalledOnce ( ) ;
1024+ expect ( decision . variationKey ) . toBeNull ( ) ;
1025+ expect ( decision . enabled ) . toBe ( false ) ;
1026+ expect ( decision . ruleKey ) . toBeNull ( ) ;
1027+ expect ( decision . reasons ) . toContain ( 'Failed to fetch CMAB data for experiment exp_3.' ) ;
1028+ expect ( decision . reasons . some ( ( r ) => r . includes ( 'lh_key' ) ) ) . toBe ( true ) ;
1029+ expect ( processSpy ) . not . toHaveBeenCalled ( ) ;
1030+ } ) ;
1031+
10151032 it ( 'should skip the held-out rule and send no holdout impression for isFeatureEnabled' , ( ) => {
10161033 const { optimizely, processSpy, holdoutEvents } = setup ( [ makeLocalHoldout ( 'lh' , [ '2001' ] ) ] ) ;
10171034
0 commit comments