12
12
13
13
import java .util .*;
14
14
15
- import static org .assertj .core .api .Assertions .assertThatCode ;
16
15
import static org .assertj .core .api .Assertions .fail ;
16
+ import static org .assertj .core .api .Assertions .*;
17
17
import static org .junit .jupiter .api .Assertions .*;
18
18
import static org .mockito .ArgumentMatchers .any ;
19
19
import static org .mockito .Mockito .*;
@@ -115,7 +115,7 @@ void nullish_properties_on_hookcontext() {
115
115
116
116
}
117
117
118
- @ Specification (number = "4.1.2" , text = "The hook context SHOULD provide: access to the client metadata and the provider metadata fields." )
118
+ @ Specification (number = "4.1.2" , text = "The ` hook context` SHOULD provide access to the ` client metadata` and the ` provider metadata` fields." )
119
119
@ Test
120
120
void optional_properties () {
121
121
// don't specify
@@ -170,7 +170,7 @@ void feo_has_hook_list() {
170
170
void error_hook_run_during_non_finally_stage () {
171
171
final boolean [] error_called = {false };
172
172
Hook h = mockBooleanHook ();
173
- doThrow (RuntimeException .class ).when (h ).finallyAfter (any (), any ());
173
+ doThrow (RuntimeException .class ).when (h ).finallyAfter (any (), any (), any () );
174
174
175
175
verify (h , times (0 )).error (any (), any (), any ());
176
176
}
@@ -201,7 +201,7 @@ void error_hook_must_run_if_resolution_details_returns_an_error_code() {
201
201
202
202
verify (hook , times (1 )).before (any (), any ());
203
203
verify (hook , times (1 )).error (any (), captor .capture (), any ());
204
- verify (hook , times (1 )).finallyAfter (any (), any ());
204
+ verify (hook , times (1 )).finallyAfter (any (), any (), any () );
205
205
verify (hook , never ()).after (any (), any (), any ());
206
206
207
207
Exception exception = captor .getValue ();
@@ -241,7 +241,7 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
241
241
}
242
242
243
243
@ Override
244
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
244
+ public void finallyAfter (HookContext <Boolean > ctx , FlagEvaluationDetails < Boolean > details , Map <String , Object > hints ) {
245
245
evalOrder .add ("provider finally" );
246
246
}
247
247
});
@@ -266,7 +266,7 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
266
266
}
267
267
268
268
@ Override
269
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
269
+ public void finallyAfter (HookContext <Boolean > ctx , FlagEvaluationDetails < Boolean > details , Map <String , Object > hints ) {
270
270
evalOrder .add ("api finally" );
271
271
}
272
272
});
@@ -290,7 +290,7 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
290
290
}
291
291
292
292
@ Override
293
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
293
+ public void finallyAfter (HookContext <Boolean > ctx , FlagEvaluationDetails < Boolean > details , Map <String , Object > hints ) {
294
294
evalOrder .add ("client finally" );
295
295
}
296
296
});
@@ -315,7 +315,7 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
315
315
}
316
316
317
317
@ Override
318
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
318
+ public void finallyAfter (HookContext <Boolean > ctx , FlagEvaluationDetails < Boolean > details , Map <String , Object > hints ) {
319
319
evalOrder .add ("invocation finally" );
320
320
}
321
321
})
@@ -344,8 +344,8 @@ void error_stops_before() {
344
344
.hook (h2 )
345
345
.hook (h )
346
346
.build ());
347
- verify (h , times (1 )).before (any (), any ());
348
- verify (h2 , times (0 )).before (any (), any ());
347
+ verify (h , times (1 )).before (any (), any ());
348
+ verify (h2 , times (0 )).before (any (), any ());
349
349
}
350
350
351
351
@ Specification (number = "4.4.6" , text = "If an error occurs during the evaluation of before or after hooks, any remaining hooks in the before or after stages MUST NOT be invoked." )
@@ -393,7 +393,7 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
393
393
}
394
394
395
395
@ Override
396
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
396
+ public void finallyAfter (HookContext <Boolean > ctx , FlagEvaluationDetails < Boolean > details , Map <String , Object > hints ) {
397
397
assertThatCode (() -> hints .put (hintKey , "changed value" )).isInstanceOf (UnsupportedOperationException .class );
398
398
}
399
399
};
@@ -435,7 +435,7 @@ void flag_eval_hook_order() {
435
435
order .verify (hook ).before (any (), any ());
436
436
order .verify (provider ).getBooleanEvaluation (any (), any (), any ());
437
437
order .verify (hook ).after (any (), any (), any ());
438
- order .verify (hook ).finallyAfter (any (), any ());
438
+ order .verify (hook ).finallyAfter (any (), any (), any () );
439
439
}
440
440
441
441
@ Specification (number = "4.4.5" , text = "If an error occurs in the before or after hooks, the error hooks MUST be invoked." )
@@ -464,6 +464,58 @@ void error_hooks__after() {
464
464
verify (hook , times (1 )).error (any (), any (), any ());
465
465
}
466
466
467
+ @ Test
468
+ void erroneous_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails () {
469
+ Hook hook = mockBooleanHook ();
470
+ doThrow (RuntimeException .class ).when (hook ).after (any (), any (), any ());
471
+ String flagKey = "test-flag-key" ;
472
+ Client client = getClient (TestEventsProvider .newInitializedTestEventsProvider ());
473
+ client .getBooleanValue (
474
+ flagKey ,
475
+ true ,
476
+ new ImmutableContext (),
477
+ FlagEvaluationOptions .builder ().hook (hook ).build ()
478
+ );
479
+
480
+ ArgumentCaptor <FlagEvaluationDetails <Boolean >> captor = ArgumentCaptor .forClass (FlagEvaluationDetails .class );
481
+ verify (hook ).finallyAfter (any (), captor .capture (), any ());
482
+
483
+ FlagEvaluationDetails <Boolean > evaluationDetails = captor .getValue ();
484
+ assertThat (evaluationDetails ).isNotNull ();
485
+
486
+ assertThat (evaluationDetails .getErrorCode ()).isEqualTo (ErrorCode .GENERAL );
487
+ assertThat (evaluationDetails .getReason ()).isEqualTo ("ERROR" );
488
+ assertThat (evaluationDetails .getVariant ()).isEqualTo ("Passed in default" );
489
+ assertThat (evaluationDetails .getFlagKey ()).isEqualTo (flagKey );
490
+ assertThat (evaluationDetails .getFlagMetadata ()).isEqualTo (ImmutableMetadata .builder ().build ());
491
+ assertThat (evaluationDetails .getValue ()).isEqualTo (true );
492
+ }
493
+
494
+ @ Test
495
+ void successful_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails () {
496
+ Hook hook = mockBooleanHook ();
497
+ String flagKey = "test-flag-key" ;
498
+ Client client = getClient (TestEventsProvider .newInitializedTestEventsProvider ());
499
+ client .getBooleanValue (
500
+ flagKey ,
501
+ true ,
502
+ new ImmutableContext (),
503
+ FlagEvaluationOptions .builder ().hook (hook ).build ()
504
+ );
505
+
506
+ ArgumentCaptor <FlagEvaluationDetails <Boolean >> captor = ArgumentCaptor .forClass (FlagEvaluationDetails .class );
507
+ verify (hook ).finallyAfter (any (), captor .capture (), any ());
508
+
509
+ FlagEvaluationDetails <Boolean > evaluationDetails = captor .getValue ();
510
+ assertThat (evaluationDetails ).isNotNull ();
511
+ assertThat (evaluationDetails .getErrorCode ()).isNull ();
512
+ assertThat (evaluationDetails .getReason ()).isEqualTo ("DEFAULT" );
513
+ assertThat (evaluationDetails .getVariant ()).isEqualTo ("Passed in default" );
514
+ assertThat (evaluationDetails .getFlagKey ()).isEqualTo (flagKey );
515
+ assertThat (evaluationDetails .getFlagMetadata ()).isEqualTo (ImmutableMetadata .builder ().build ());
516
+ assertThat (evaluationDetails .getValue ()).isEqualTo (true );
517
+ }
518
+
467
519
@ Test
468
520
void multi_hooks_early_out__before () {
469
521
Hook <Boolean > hook = mockBooleanHook ();
@@ -556,7 +608,7 @@ void mergeHappensCorrectly() {
556
608
void first_finally_broken () {
557
609
Hook hook = mockBooleanHook ();
558
610
doThrow (RuntimeException .class ).when (hook ).before (any (), any ());
559
- doThrow (RuntimeException .class ).when (hook ).finallyAfter (any (), any ());
611
+ doThrow (RuntimeException .class ).when (hook ).finallyAfter (any (), any (), any () );
560
612
Hook hook2 = mockBooleanHook ();
561
613
InOrder order = inOrder (hook , hook2 );
562
614
@@ -568,8 +620,8 @@ void first_finally_broken() {
568
620
.build ());
569
621
570
622
order .verify (hook ).before (any (), any ());
571
- order .verify (hook2 ).finallyAfter (any (), any ());
572
- order .verify (hook ).finallyAfter (any (), any ());
623
+ order .verify (hook2 ).finallyAfter (any (), any (), any () );
624
+ order .verify (hook ).finallyAfter (any (), any (), any () );
573
625
}
574
626
575
627
@ Specification (number = "4.4.4" , text = "If an error hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining error hooks." )
@@ -616,8 +668,7 @@ void doesnt_use_finally() {
616
668
.as ("Not possible. Finally is a reserved word." )
617
669
.isInstanceOf (NoSuchMethodException .class );
618
670
619
- assertThatCode (() -> Hook .class .getMethod ("finallyAfter" , HookContext .class , Map .class ))
671
+ assertThatCode (() -> Hook .class .getMethod ("finallyAfter" , HookContext .class , FlagEvaluationDetails . class , Map .class ))
620
672
.doesNotThrowAnyException ();
621
673
}
622
-
623
674
}
0 commit comments