1
1
package dev .openfeature .sdk ;
2
2
3
+ import static org .assertj .core .api .Assertions .assertThat ;
3
4
import static org .assertj .core .api .Assertions .assertThatCode ;
4
5
import static org .assertj .core .api .Assertions .fail ;
5
- import static org .junit .jupiter .api .Assertions .*;
6
+ import static org .junit .jupiter .api .Assertions .assertEquals ;
7
+ import static org .junit .jupiter .api .Assertions .assertInstanceOf ;
8
+ import static org .junit .jupiter .api .Assertions .assertNotNull ;
9
+ import static org .junit .jupiter .api .Assertions .assertTrue ;
6
10
import static org .mockito .ArgumentMatchers .any ;
7
- import static org .mockito .Mockito .*;
11
+ import static org .mockito .Mockito .doThrow ;
12
+ import static org .mockito .Mockito .inOrder ;
13
+ import static org .mockito .Mockito .mock ;
14
+ import static org .mockito .Mockito .never ;
15
+ import static org .mockito .Mockito .times ;
16
+ import static org .mockito .Mockito .verify ;
17
+ import static org .mockito .Mockito .when ;
8
18
9
19
import dev .openfeature .sdk .exceptions .FlagNotFoundError ;
10
20
import dev .openfeature .sdk .fixtures .HookFixtures ;
@@ -187,7 +197,7 @@ void feo_has_hook_list() {
187
197
void error_hook_run_during_non_finally_stage () {
188
198
final boolean [] error_called = {false };
189
199
Hook h = mockBooleanHook ();
190
- doThrow (RuntimeException .class ).when (h ).finallyAfter (any (), any ());
200
+ doThrow (RuntimeException .class ).when (h ).finallyAfter (any (), any (), any () );
191
201
192
202
verify (h , times (0 )).error (any (), any (), any ());
193
203
}
@@ -219,7 +229,7 @@ void error_hook_must_run_if_resolution_details_returns_an_error_code() {
219
229
220
230
verify (hook , times (1 )).before (any (), any ());
221
231
verify (hook , times (1 )).error (any (), captor .capture (), any ());
222
- verify (hook , times (1 )).finallyAfter (any (), any ());
232
+ verify (hook , times (1 )).finallyAfter (any (), any (), any () );
223
233
verify (hook , never ()).after (any (), any (), any ());
224
234
225
235
Exception exception = captor .getValue ();
@@ -274,7 +284,10 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
274
284
}
275
285
276
286
@ Override
277
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
287
+ public void finallyAfter (
288
+ HookContext <Boolean > ctx ,
289
+ FlagEvaluationDetails <Boolean > details ,
290
+ Map <String , Object > hints ) {
278
291
evalOrder .add ("provider finally" );
279
292
}
280
293
});
@@ -300,7 +313,8 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
300
313
}
301
314
302
315
@ Override
303
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
316
+ public void finallyAfter (
317
+ HookContext <Boolean > ctx , FlagEvaluationDetails <Boolean > details , Map <String , Object > hints ) {
304
318
evalOrder .add ("api finally" );
305
319
}
306
320
});
@@ -325,7 +339,8 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
325
339
}
326
340
327
341
@ Override
328
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
342
+ public void finallyAfter (
343
+ HookContext <Boolean > ctx , FlagEvaluationDetails <Boolean > details , Map <String , Object > hints ) {
329
344
evalOrder .add ("client finally" );
330
345
}
331
346
});
@@ -357,7 +372,10 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
357
372
}
358
373
359
374
@ Override
360
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
375
+ public void finallyAfter (
376
+ HookContext <Boolean > ctx ,
377
+ FlagEvaluationDetails <Boolean > details ,
378
+ Map <String , Object > hints ) {
361
379
evalOrder .add ("invocation finally" );
362
380
}
363
381
})
@@ -462,7 +480,8 @@ public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object>
462
480
}
463
481
464
482
@ Override
465
- public void finallyAfter (HookContext <Boolean > ctx , Map <String , Object > hints ) {
483
+ public void finallyAfter (
484
+ HookContext <Boolean > ctx , FlagEvaluationDetails <Boolean > details , Map <String , Object > hints ) {
466
485
assertThatCode (() -> hints .put (hintKey , "changed value" ))
467
486
.isInstanceOf (UnsupportedOperationException .class );
468
487
}
@@ -509,7 +528,7 @@ void flag_eval_hook_order() {
509
528
order .verify (hook ).before (any (), any ());
510
529
order .verify (provider ).getBooleanEvaluation (any (), any (), any ());
511
530
order .verify (hook ).after (any (), any (), any ());
512
- order .verify (hook ).finallyAfter (any (), any ());
531
+ order .verify (hook ).finallyAfter (any (), any (), any () );
513
532
}
514
533
515
534
@ Specification (
@@ -550,6 +569,58 @@ void error_hooks__after() {
550
569
verify (hook , times (1 )).error (any (), any (), any ());
551
570
}
552
571
572
+ @ Test
573
+ void erroneous_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails () {
574
+ Hook hook = mockBooleanHook ();
575
+ doThrow (RuntimeException .class ).when (hook ).after (any (), any (), any ());
576
+ String flagKey = "test-flag-key" ;
577
+ Client client = getClient (TestEventsProvider .newInitializedTestEventsProvider ());
578
+ client .getBooleanValue (
579
+ flagKey ,
580
+ true ,
581
+ new ImmutableContext (),
582
+ FlagEvaluationOptions .builder ().hook (hook ).build ());
583
+
584
+ ArgumentCaptor <FlagEvaluationDetails <Boolean >> captor = ArgumentCaptor .forClass (FlagEvaluationDetails .class );
585
+ verify (hook ).finallyAfter (any (), captor .capture (), any ());
586
+
587
+ FlagEvaluationDetails <Boolean > evaluationDetails = captor .getValue ();
588
+ assertThat (evaluationDetails ).isNotNull ();
589
+
590
+ assertThat (evaluationDetails .getErrorCode ()).isEqualTo (ErrorCode .GENERAL );
591
+ assertThat (evaluationDetails .getReason ()).isEqualTo ("ERROR" );
592
+ assertThat (evaluationDetails .getVariant ()).isEqualTo ("Passed in default" );
593
+ assertThat (evaluationDetails .getFlagKey ()).isEqualTo (flagKey );
594
+ assertThat (evaluationDetails .getFlagMetadata ())
595
+ .isEqualTo (ImmutableMetadata .builder ().build ());
596
+ assertThat (evaluationDetails .getValue ()).isTrue ();
597
+ }
598
+
599
+ @ Test
600
+ void successful_flagResolution_setsAppropriateFieldsInFlagEvaluationDetails () {
601
+ Hook hook = mockBooleanHook ();
602
+ String flagKey = "test-flag-key" ;
603
+ Client client = getClient (TestEventsProvider .newInitializedTestEventsProvider ());
604
+ client .getBooleanValue (
605
+ flagKey ,
606
+ true ,
607
+ new ImmutableContext (),
608
+ FlagEvaluationOptions .builder ().hook (hook ).build ());
609
+
610
+ ArgumentCaptor <FlagEvaluationDetails <Boolean >> captor = ArgumentCaptor .forClass (FlagEvaluationDetails .class );
611
+ verify (hook ).finallyAfter (any (), captor .capture (), any ());
612
+
613
+ FlagEvaluationDetails <Boolean > evaluationDetails = captor .getValue ();
614
+ assertThat (evaluationDetails ).isNotNull ();
615
+ assertThat (evaluationDetails .getErrorCode ()).isNull ();
616
+ assertThat (evaluationDetails .getReason ()).isEqualTo ("DEFAULT" );
617
+ assertThat (evaluationDetails .getVariant ()).isEqualTo ("Passed in default" );
618
+ assertThat (evaluationDetails .getFlagKey ()).isEqualTo (flagKey );
619
+ assertThat (evaluationDetails .getFlagMetadata ())
620
+ .isEqualTo (ImmutableMetadata .builder ().build ());
621
+ assertThat (evaluationDetails .getValue ()).isTrue ();
622
+ }
623
+
553
624
@ Test
554
625
void multi_hooks_early_out__before () {
555
626
Hook <Boolean > hook = mockBooleanHook ();
@@ -649,7 +720,7 @@ void mergeHappensCorrectly() {
649
720
void first_finally_broken () {
650
721
Hook hook = mockBooleanHook ();
651
722
doThrow (RuntimeException .class ).when (hook ).before (any (), any ());
652
- doThrow (RuntimeException .class ).when (hook ).finallyAfter (any (), any ());
723
+ doThrow (RuntimeException .class ).when (hook ).finallyAfter (any (), any (), any () );
653
724
Hook hook2 = mockBooleanHook ();
654
725
InOrder order = inOrder (hook , hook2 );
655
726
@@ -661,8 +732,8 @@ void first_finally_broken() {
661
732
FlagEvaluationOptions .builder ().hook (hook2 ).hook (hook ).build ());
662
733
663
734
order .verify (hook ).before (any (), any ());
664
- order .verify (hook2 ).finallyAfter (any (), any ());
665
- order .verify (hook ).finallyAfter (any (), any ());
735
+ order .verify (hook2 ).finallyAfter (any (), any (), any () );
736
+ order .verify (hook ).finallyAfter (any (), any (), any () );
666
737
}
667
738
668
739
@ Specification (
@@ -711,7 +782,8 @@ void doesnt_use_finally() {
711
782
.as ("Not possible. Finally is a reserved word." )
712
783
.isInstanceOf (NoSuchMethodException .class );
713
784
714
- assertThatCode (() -> Hook .class .getMethod ("finallyAfter" , HookContext .class , Map .class ))
785
+ assertThatCode (() ->
786
+ Hook .class .getMethod ("finallyAfter" , HookContext .class , FlagEvaluationDetails .class , Map .class ))
715
787
.doesNotThrowAnyException ();
716
788
}
717
789
}
0 commit comments