1use std::{collections::HashMap, ops::Deref, sync::Arc};
2
3use crate::{
4 provider::ProviderMetadata, ClientMetadata, EvaluationContext, EvaluationDetails,
5 EvaluationError, Type, Value,
6};
7
8mod logging;
9pub use logging::LoggingHook;
10
11#[cfg_attr(
20 feature = "test-util",
21 mockall::automock,
22 allow(clippy::ref_option_ref)
23)] #[async_trait::async_trait]
25pub trait Hook: Send + Sync + 'static {
26 async fn before<'a>(
28 &self,
29 context: &HookContext<'a>,
30 hints: Option<&'a HookHints>,
31 ) -> Result<Option<EvaluationContext>, EvaluationError>;
32
33 async fn after<'a>(
35 &self,
36 context: &HookContext<'a>,
37 details: &EvaluationDetails<Value>,
38 hints: Option<&'a HookHints>,
39 ) -> Result<(), EvaluationError>;
40
41 async fn error<'a>(
43 &self,
44 context: &HookContext<'a>,
45 error: &EvaluationError,
46 hints: Option<&'a HookHints>,
47 );
48
49 async fn finally<'a>(
51 &self,
52 context: &HookContext<'a>,
53 evaluation_details: &EvaluationDetails<Value>,
54 hints: Option<&'a HookHints>,
55 );
56}
57
58#[allow(missing_docs)]
63#[derive(Clone)]
64pub struct HookWrapper(Arc<dyn Hook>);
65
66impl HookWrapper {
67 #[allow(missing_docs)]
68 pub fn new(hook: impl Hook) -> Self {
69 Self(Arc::new(hook))
70 }
71}
72
73impl Deref for HookWrapper {
74 type Target = dyn Hook;
75
76 fn deref(&self) -> &Self::Target {
77 &*self.0
78 }
79}
80
81#[allow(missing_docs)]
86#[derive(Clone, Default, PartialEq, Debug)]
87pub struct HookHints {
88 hints: HashMap<String, Value>,
89}
90
91#[allow(missing_docs)]
97#[derive(Clone, PartialEq, Debug)]
98pub struct HookContext<'a> {
99 pub flag_key: &'a str,
100 pub flag_type: Type,
101 pub evaluation_context: &'a EvaluationContext,
102 pub provider_metadata: ProviderMetadata,
103 pub default_value: Option<Value>,
104 pub client_metadata: ClientMetadata,
105}
106
107#[cfg(test)]
108mod tests {
109
110 use spec::spec;
111
112 use crate::{
113 provider::{MockFeatureProvider, ResolutionDetails},
114 EvaluationErrorCode, EvaluationOptions, EvaluationReason, OpenFeature, StructValue,
115 };
116
117 use super::*;
118
119 #[spec(
120 number = "4.1.1",
121 text = "Hook context MUST provide: the flag key, flag value type, evaluation context, and the default value."
122 )]
123 #[spec(
124 number = "4.1.2",
125 text = "The hook context SHOULD provide: access to the client metadata and the provider metadata fields."
126 )]
127 #[spec(
128 number = "4.1.3",
129 text = "The flag key, flag type, and default value properties MUST be immutable. If the language does not support immutability, the hook MUST NOT modify these properties."
130 )]
131 #[test]
132 fn hook_context() {
133 let context = HookContext {
134 flag_key: "flag_key",
135 flag_type: Type::Bool,
136 evaluation_context: &EvaluationContext::default(),
137 provider_metadata: ProviderMetadata::default(),
138 default_value: Some(Value::Bool(true)),
139 client_metadata: ClientMetadata::default(),
140 };
141
142 assert_eq!(context.flag_key, "flag_key");
143 assert_eq!(context.flag_type, Type::Bool);
144 assert_eq!(context.evaluation_context, &EvaluationContext::default());
145 assert_eq!(context.provider_metadata, ProviderMetadata::default());
146 assert_eq!(context.default_value, Some(Value::Bool(true)));
147 assert_eq!(context.client_metadata, ClientMetadata::default());
148 }
149
150 #[spec(
151 number = "4.2.1",
152 text = "hook hints MUST be a structure supports definition of arbitrary properties, with keys of type string, and values of type boolean | string | number | datetime | structure."
153 )]
154 #[test]
155 fn hook_hints() {
156 let mut hints = HookHints::default();
157 hints.hints.insert("key".to_string(), Value::Bool(true));
158 hints
159 .hints
160 .insert("key2".to_string(), Value::String("value".to_string()));
161 hints.hints.insert("key3".to_string(), Value::Int(42));
162 hints.hints.insert("key4".to_string(), Value::Float(3.14));
163 hints.hints.insert("key5".to_string(), Value::Array(vec![]));
164 hints
165 .hints
166 .insert("key6".to_string(), Value::Struct(StructValue::default()));
167
168 assert_eq!(hints.hints.len(), 6);
169 assert_eq!(hints.hints.get("key"), Some(&Value::Bool(true)));
170 assert_eq!(
171 hints.hints.get("key2"),
172 Some(&Value::String("value".to_string()))
173 );
174 assert_eq!(hints.hints.get("key3"), Some(&Value::Int(42)));
175 assert_eq!(hints.hints.get("key4"), Some(&Value::Float(3.14)));
176 assert_eq!(hints.hints.get("key5"), Some(&Value::Array(vec![])));
177 assert_eq!(
178 hints.hints.get("key6"),
179 Some(&Value::Struct(StructValue::default()))
180 );
181 }
182
183 #[spec(number = "4.2.2.1", text = "Hook hints MUST be immutable.")]
184 #[test]
185 fn hook_hints_mutability_checked_by_type_system() {}
186
187 #[spec(
188 number = "4.2.2.2",
189 text = "The client metadata field in the hook context MUST be immutable."
190 )]
191 #[test]
192 fn client_metadata_mutability_checked_by_type_system() {}
193
194 #[spec(
195 number = "4.2.2.3",
196 text = "The provider metadata field in the hook context MUST be immutable."
197 )]
198 #[test]
199 fn provider_metadata_mutability_checked_by_type_system() {}
200
201 #[spec(number = "4.3.1", text = "Hooks MUST specify at least one stage.")]
202 #[test]
203 fn hook_interface_implementation_checked_by_type_system() {}
204
205 #[spec(
206 number = "4.3.2.1",
207 text = "The before stage MUST run before flag resolution occurs. It accepts a hook context (required) and hook hints (optional) as parameters and returns either an evaluation context or nothing."
208 )]
209 #[test]
210 fn hook_before_function_interface_implementation_checked_by_type_system() {}
211
212 #[spec(
213 number = "4.3.4",
214 text = "Any evaluation context returned from a before hook MUST be passed to subsequent before hooks (via HookContext)."
215 )]
216 #[tokio::test]
217 async fn before_hook_context_passing() {
218 let mut mock_hook_1 = MockHook::new();
219 let mut mock_hook_2 = MockHook::new();
220
221 let mut api = OpenFeature::default();
222 let mut client = api.create_named_client("test");
223 let mut mock_provider = MockFeatureProvider::default();
224
225 mock_provider.expect_hooks().return_const(vec![]);
226 mock_provider.expect_initialize().return_const(());
227 mock_provider
228 .expect_metadata()
229 .return_const(ProviderMetadata::default());
230 mock_provider
231 .expect_resolve_bool_value()
232 .withf(|_, ctx| {
233 assert_eq!(
234 ctx,
235 &EvaluationContext::default()
236 .with_targeting_key("mock_hook_1")
237 .with_custom_field("is", "a test")
238 );
239 true
240 })
241 .return_const(Ok(ResolutionDetails::new(true)));
242
243 api.set_provider(mock_provider).await;
244 drop(api);
245
246 let flag_key = "flag";
247
248 let eval_ctx = EvaluationContext::default().with_custom_field("is", "a test");
249
250 let expected_eval_ctx = eval_ctx.clone();
251 let client_metadata = client.metadata().clone();
252 mock_hook_1
253 .expect_before()
254 .withf(move |ctx, _| {
255 let hook_ctx_1 = HookContext {
256 flag_key,
257 flag_type: Type::Bool,
258 evaluation_context: &expected_eval_ctx,
259 default_value: Some(Value::Bool(false)),
260 provider_metadata: ProviderMetadata::default(),
261 client_metadata: client_metadata.clone(),
262 };
263
264 assert_eq!(ctx, &hook_ctx_1);
265 true
266 })
267 .once()
268 .returning(move |_, _| {
269 Ok(Some(
270 EvaluationContext::default().with_targeting_key("mock_hook_1"),
271 ))
272 });
273
274 let expected_eval_ctx_2 = EvaluationContext::default().with_targeting_key("mock_hook_1");
275 let client_metadata = client.metadata().clone();
276 mock_hook_2
277 .expect_before()
278 .withf(move |ctx, _| {
279 let hook_ctx_1 = HookContext {
280 flag_key,
281 flag_type: Type::Bool,
282 evaluation_context: &expected_eval_ctx_2,
283 default_value: Some(Value::Bool(false)),
284 provider_metadata: ProviderMetadata::default(),
285 client_metadata: client_metadata.clone(),
286 };
287
288 assert_eq!(ctx, &hook_ctx_1);
289 true
290 })
291 .once()
292 .returning(move |_, _| Ok(None));
293
294 mock_hook_1.expect_after().return_const(Ok(()));
295 mock_hook_2.expect_after().return_const(Ok(()));
296 mock_hook_1.expect_finally().return_const(());
297 mock_hook_2.expect_finally().return_const(());
298
299 client = client.with_hook(mock_hook_1).with_hook(mock_hook_2);
301
302 let result = client.get_bool_value(flag_key, Some(&eval_ctx), None).await;
303
304 assert!(result.is_ok());
305 }
306
307 #[spec(
308 number = "4.3.5",
309 text = "When before hooks have finished executing, any resulting evaluation context MUST be merged with the existing evaluation context."
310 )]
311 #[tokio::test]
312 async fn before_hook_context_merging() {
313 let mut mock_hook = MockHook::new();
314
315 let mut api = OpenFeature::default();
316 api.set_evaluation_context(
317 EvaluationContext::default()
318 .with_custom_field("key", "api context")
319 .with_custom_field("lowestPriority", true),
320 )
321 .await;
322
323 let mut client = api.create_named_client("test");
324 client.set_evaluation_context(
325 EvaluationContext::default()
326 .with_custom_field("key", "client context")
327 .with_custom_field("lowestPriority", false)
328 .with_custom_field("beatsClient", false),
329 );
330
331 mock_hook.expect_before().once().returning(move |_, _| {
332 Ok(Some(
333 EvaluationContext::default()
334 .with_custom_field("key", "hook value")
335 .with_custom_field("multiplier", 3),
336 ))
337 });
338
339 mock_hook.expect_after().return_const(Ok(()));
340 mock_hook.expect_finally().return_const(());
341
342 let flag_key = "flag";
343 let eval_ctx = EvaluationContext::default()
344 .with_custom_field("key", "invocation context")
345 .with_custom_field("on", true)
346 .with_custom_field("beatsClient", true);
347
348 let expected_ctx = EvaluationContext::default()
349 .with_custom_field("key", "hook value")
350 .with_custom_field("multiplier", 3)
351 .with_custom_field("on", true)
352 .with_custom_field("lowestPriority", false)
353 .with_custom_field("beatsClient", true);
354
355 let mut mock_provider = MockFeatureProvider::default();
356
357 mock_provider.expect_hooks().return_const(vec![]);
358 mock_provider.expect_initialize().return_const(());
359 mock_provider
360 .expect_metadata()
361 .return_const(ProviderMetadata::default());
362 mock_provider
363 .expect_resolve_string_value()
364 .withf(move |_, ctx| {
365 assert_eq!(ctx, &expected_ctx);
366 true
367 })
368 .return_const(Ok(ResolutionDetails::new("value")));
369
370 api.set_provider(mock_provider).await;
371 drop(api);
372
373 client = client.with_hook(mock_hook);
374
375 let result = client
376 .get_string_value(flag_key, Some(&eval_ctx), None)
377 .await;
378
379 assert!(result.is_ok());
380 }
381
382 #[spec(
383 number = "4.3.6",
384 text = "The after stage MUST run after flag resolution occurs. It accepts a hook context (required), evaluation details (required) and hook hints (optional). It has no return value."
385 )]
386 #[tokio::test]
387 async fn after_hook() {
388 let mut mock_hook = MockHook::new();
389
390 let mut api = OpenFeature::default();
391 let mut client = api.create_client();
392 let mut mock_provider = MockFeatureProvider::default();
393
394 let mut seq = mockall::Sequence::new();
395
396 mock_provider.expect_hooks().return_const(vec![]);
397 mock_provider.expect_initialize().return_const(());
398 mock_provider
399 .expect_metadata()
400 .return_const(ProviderMetadata::default());
401 mock_provider
402 .expect_resolve_bool_value()
403 .once()
404 .in_sequence(&mut seq)
405 .return_const(Ok(ResolutionDetails::new(true)));
406
407 api.set_provider(mock_provider).await;
408 drop(api);
409
410 mock_hook.expect_before().returning(|_, _| Ok(None));
411
412 mock_hook
413 .expect_after()
414 .once()
415 .in_sequence(&mut seq)
416 .return_const(Ok(()));
417
418 mock_hook.expect_finally().return_const(());
419
420 client = client.with_hook(mock_hook);
422
423 let flag_key = "flag";
424 let eval_ctx = EvaluationContext::default().with_custom_field("is", "a test");
425
426 let result = client.get_bool_value(flag_key, Some(&eval_ctx), None).await;
427
428 assert!(result.is_ok());
429 }
430
431 #[spec(
432 number = "4.3.7",
433 text = "The error hook MUST run when errors are encountered in the before stage, the after stage or during flag resolution. It accepts hook context (required), exception representing what went wrong (required), and hook hints (optional). It has no return value."
434 )]
435 #[tokio::test]
436 async fn error_hook() {
437 {
439 let mut mock_hook = MockHook::new();
440
441 let mut api = OpenFeature::default();
442 let mut client = api.create_client();
443 let mut mock_provider = MockFeatureProvider::default();
444
445 let mut seq = mockall::Sequence::new();
446
447 mock_provider.expect_hooks().return_const(vec![]);
448 mock_provider.expect_initialize().return_const(());
449 mock_provider.expect_resolve_bool_value().never();
450 mock_provider
451 .expect_metadata()
452 .return_const(ProviderMetadata::default());
453
454 api.set_provider(mock_provider).await;
455 drop(api);
456
457 mock_hook.expect_before().returning(|_, _| error());
458
459 mock_hook
460 .expect_error()
461 .once()
462 .in_sequence(&mut seq)
463 .return_const(());
464
465 mock_hook
466 .expect_finally()
467 .withf(|ctx, details, _| {
468 assert_eq!(ctx.flag_key, "flag");
469 assert_eq!(ctx.flag_type, Type::Bool);
470 assert_eq!(
471 ctx.evaluation_context,
472 &EvaluationContext::default().with_custom_field("is", "a test")
473 );
474 assert_eq!(ctx.default_value, Some(Value::Bool(false)));
475 assert_eq!(details.flag_key, "flag");
476 assert_eq!(details.value, Value::Bool(false));
477 assert_eq!(details.reason, Some(EvaluationReason::Error));
478 true
479 })
480 .return_const(());
481
482 client = client.with_hook(mock_hook);
484
485 let flag_key = "flag";
486 let eval_ctx = EvaluationContext::default().with_custom_field("is", "a test");
487
488 let result = client.get_bool_value(flag_key, Some(&eval_ctx), None).await;
489
490 assert!(result.is_err());
491 }
492
493 {
495 let mut mock_hook = MockHook::new();
496
497 let mut api = OpenFeature::default();
498 let mut client = api.create_client();
499 let mut mock_provider = MockFeatureProvider::default();
500
501 let mut seq = mockall::Sequence::new();
502
503 mock_provider.expect_hooks().return_const(vec![]);
504 mock_provider.expect_initialize().return_const(());
505 mock_provider
506 .expect_metadata()
507 .return_const(ProviderMetadata::default());
508
509 mock_hook.expect_before().returning(|_, _| Ok(None));
510
511 mock_provider
512 .expect_resolve_bool_value()
513 .once()
514 .in_sequence(&mut seq)
515 .return_const(error());
516
517 mock_hook
518 .expect_error()
519 .once()
520 .in_sequence(&mut seq)
521 .return_const(());
522
523 mock_hook.expect_finally().return_const(());
524
525 api.set_provider(mock_provider).await;
526 drop(api);
527
528 client = client.with_hook(mock_hook);
530
531 let flag_key = "flag";
532 let eval_ctx = EvaluationContext::default().with_custom_field("is", "a test");
533
534 let result = client.get_bool_value(flag_key, Some(&eval_ctx), None).await;
535
536 assert!(result.is_err());
537 }
538 }
539
540 #[spec(
541 number = "4.3.8",
542 text = "The finally hook MUST run after the before, after, and error stages. It accepts a hook context (required), evaluation details (required) and hook hints (optional). It has no return value."
543 )]
544 #[tokio::test]
545 async fn finally_hook() {
546 let mut mock_hook = MockHook::new();
547
548 let mut api = OpenFeature::default();
549 let mut client = api.create_client();
550 let mut mock_provider = MockFeatureProvider::default();
551
552 let mut seq = mockall::Sequence::new();
553
554 mock_provider.expect_hooks().return_const(vec![]);
555 mock_provider.expect_initialize().return_const(());
556 mock_provider
557 .expect_metadata()
558 .return_const(ProviderMetadata::default());
559 mock_provider
560 .expect_resolve_bool_value()
561 .return_const(Ok(ResolutionDetails::new(true)));
562
563 api.set_provider(mock_provider).await;
564
565 mock_hook
566 .expect_before()
567 .once()
568 .in_sequence(&mut seq)
569 .returning(|_, _| Ok(None));
570 mock_hook
571 .expect_after()
572 .once()
573 .in_sequence(&mut seq)
574 .return_const(Ok(()));
575
576 mock_hook
577 .expect_finally()
578 .once()
579 .in_sequence(&mut seq)
580 .withf(|ctx, details, _| {
581 assert_eq!(ctx.flag_key, "flag");
582 assert_eq!(ctx.flag_type, Type::Bool);
583 assert_eq!(
584 ctx.evaluation_context,
585 &EvaluationContext::default().with_custom_field("is", "a test")
586 );
587 assert_eq!(ctx.default_value, Some(Value::Bool(false)));
588 assert_eq!(details.flag_key, "flag");
589 assert_eq!(details.value, Value::Bool(true));
590 true
591 })
592 .return_const(());
593
594 client = client.with_hook(mock_hook);
596
597 let flag_key = "flag";
598 let eval_ctx = EvaluationContext::default().with_custom_field("is", "a test");
599
600 let result = client.get_bool_value(flag_key, Some(&eval_ctx), None).await;
601
602 assert!(result.is_ok());
603 }
604
605 #[spec(
606 number = "4.4.1",
607 text = "The API, Client, Provider, and invocation MUST have a method for registering hooks."
608 )]
609 #[spec(
610 number = "4.4.2",
611 text = "Hooks MUST be evaluated in the following order -> before: API, Client, Invocation, Provider. after: Provider, Invocation, Client, API. error(if applicable): Provider, Invocation, Client, API. finally: Provider, Invocation, Client, API."
612 )]
613 #[tokio::test]
614 async fn hook_evaluation_order() {
615 let mut mock_api_hook = MockHook::new();
616 let mut mock_client_hook = MockHook::new();
617 let mut mock_provider_hook = MockHook::new();
618 let mut mock_invocation_hook = MockHook::new();
619
620 let mut api = OpenFeature::default();
621 let mut client = api.create_client();
622 let mut provider = MockFeatureProvider::default();
623
624 let mut seq = mockall::Sequence::new();
625
626 mock_api_hook
628 .expect_before()
629 .once()
630 .in_sequence(&mut seq)
631 .returning(|_, _| Ok(None));
632 mock_client_hook
633 .expect_before()
634 .once()
635 .in_sequence(&mut seq)
636 .returning(|_, _| Ok(None));
637 mock_invocation_hook
638 .expect_before()
639 .once()
640 .in_sequence(&mut seq)
641 .returning(|_, _| Ok(None));
642 mock_provider_hook
643 .expect_before()
644 .once()
645 .in_sequence(&mut seq)
646 .returning(|_, _| Ok(None));
647
648 provider
650 .expect_resolve_bool_value()
651 .once()
652 .in_sequence(&mut seq)
653 .return_const(Ok(ResolutionDetails::new(true)));
654
655 mock_provider_hook
657 .expect_after()
658 .once()
659 .in_sequence(&mut seq)
660 .returning(|_, _, _| Ok(()));
661 mock_invocation_hook
662 .expect_after()
663 .once()
664 .in_sequence(&mut seq)
665 .returning(|_, _, _| Ok(()));
666 mock_client_hook
667 .expect_after()
668 .once()
669 .in_sequence(&mut seq)
670 .returning(|_, _, _| Ok(()));
671 mock_api_hook
672 .expect_after()
673 .once()
674 .in_sequence(&mut seq)
675 .returning(|_, _, _| Ok(()));
676
677 mock_provider_hook
679 .expect_finally()
680 .once()
681 .in_sequence(&mut seq)
682 .returning(|_, _, _| {});
683 mock_invocation_hook
684 .expect_finally()
685 .once()
686 .in_sequence(&mut seq)
687 .returning(|_, _, _| {});
688 mock_client_hook
689 .expect_finally()
690 .once()
691 .in_sequence(&mut seq)
692 .returning(|_, _, _| {});
693 mock_api_hook
694 .expect_finally()
695 .once()
696 .in_sequence(&mut seq)
697 .returning(|_, _, _| {});
698
699 provider
700 .expect_hooks()
701 .return_const(vec![HookWrapper::new(mock_provider_hook)]);
702 provider.expect_initialize().return_const(());
703 provider
704 .expect_metadata()
705 .return_const(ProviderMetadata::default());
706
707 api.set_provider(provider).await;
708 api.add_hook(mock_api_hook).await;
709 client = client.with_hook(mock_client_hook);
710
711 let eval = EvaluationOptions::default().with_hook(mock_invocation_hook);
712 let _ = client.get_bool_value("flag", None, Some(&eval)).await;
713 }
714
715 #[spec(
716 number = "4.4.3",
717 text = "If a finally hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining finally hooks."
718 )]
719 #[test]
720 fn finally_hook_not_throw_checked_by_type_system() {}
721
722 #[spec(
723 number = "4.4.4",
724 text = "If an error hook abnormally terminates, evaluation MUST proceed, including the execution of any remaining error hooks."
725 )]
726 #[test]
727 fn error_hook_not_throw_checked_by_type_system() {}
728
729 #[spec(
730 number = "4.4.5",
731 text = "If an error occurs in the before or after hooks, the error hooks MUST be invoked."
732 )]
733 #[tokio::test]
734 async fn error_hook_invoked_on_error() {
735 let mut mock_hook = MockHook::new();
736
737 let mut api = OpenFeature::default();
738 let mut client = api.create_client();
739 let mut mock_provider = MockFeatureProvider::default();
740
741 let mut seq = mockall::Sequence::new();
742
743 mock_provider.expect_hooks().return_const(vec![]);
744 mock_provider.expect_initialize().return_const(());
745 mock_provider.expect_resolve_bool_value().never();
746 mock_provider
747 .expect_metadata()
748 .return_const(ProviderMetadata::default());
749
750 api.set_provider(mock_provider).await;
751
752 mock_hook
753 .expect_before()
754 .once()
755 .in_sequence(&mut seq)
756 .returning(|_, _| error());
757
758 mock_hook
759 .expect_error()
760 .once()
761 .in_sequence(&mut seq)
762 .return_const(());
763
764 mock_hook.expect_finally().return_const(());
765
766 client = client.with_hook(mock_hook);
768
769 let flag_key = "flag";
770 let eval_ctx = EvaluationContext::default().with_custom_field("is", "a test");
771
772 let result = client.get_bool_value(flag_key, Some(&eval_ctx), None).await;
773
774 assert!(result.is_err());
775 }
776
777 #[spec(
778 number = "4.4.6",
779 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."
780 )]
781 #[tokio::test]
782 async fn do_not_evaluate_remaining_hooks_on_error() {
783 let mut mock_api_hook = MockHook::new();
784 let mut mock_client_hook = MockHook::new();
785 let mut mock_provider_hook = MockHook::new();
786 let mut mock_invocation_hook = MockHook::new();
787
788 let mut api = OpenFeature::default();
789 let mut client = api.create_client();
790 let mut provider = MockFeatureProvider::default();
791
792 let mut seq = mockall::Sequence::new();
793
794 mock_api_hook
796 .expect_before()
797 .once()
798 .in_sequence(&mut seq)
799 .returning(|_, _| Ok(None));
800 mock_client_hook
801 .expect_before()
802 .once()
803 .in_sequence(&mut seq)
804 .returning(|_, _| error());
805
806 mock_invocation_hook.expect_before().never();
808 mock_provider_hook.expect_before().never();
809
810 provider.expect_resolve_bool_value().never();
812
813 mock_provider_hook.expect_after().never();
815 mock_invocation_hook.expect_after().never();
816 mock_client_hook.expect_after().never();
817 mock_api_hook.expect_after().never();
818
819 mock_provider_hook
821 .expect_error()
822 .once()
823 .in_sequence(&mut seq)
824 .returning(|_, _, _| {});
825 mock_invocation_hook
826 .expect_error()
827 .once()
828 .in_sequence(&mut seq)
829 .returning(|_, _, _| {});
830 mock_client_hook
831 .expect_error()
832 .once()
833 .in_sequence(&mut seq)
834 .returning(|_, _, _| {});
835 mock_api_hook
836 .expect_error()
837 .once()
838 .in_sequence(&mut seq)
839 .returning(|_, _, _| {});
840
841 mock_provider_hook
843 .expect_finally()
844 .once()
845 .in_sequence(&mut seq)
846 .returning(|_, _, _| {});
847 mock_invocation_hook
848 .expect_finally()
849 .once()
850 .in_sequence(&mut seq)
851 .returning(|_, _, _| {});
852 mock_client_hook
853 .expect_finally()
854 .once()
855 .in_sequence(&mut seq)
856 .returning(|_, _, _| {});
857 mock_api_hook
858 .expect_finally()
859 .once()
860 .in_sequence(&mut seq)
861 .returning(|_, _, _| {});
862
863 provider
864 .expect_hooks()
865 .return_const(vec![HookWrapper::new(mock_provider_hook)]);
866 provider.expect_initialize().return_const(());
867 provider
868 .expect_metadata()
869 .return_const(ProviderMetadata::default());
870
871 api.set_provider(provider).await;
872 api.add_hook(mock_api_hook).await;
873 client = client.with_hook(mock_client_hook);
874
875 let eval = EvaluationOptions::default().with_hook(mock_invocation_hook);
876 let result = client.get_bool_value("flag", None, Some(&eval)).await;
877
878 assert!(result.is_err());
879 }
880
881 #[spec(
882 number = "4.4.7",
883 text = "If an error occurs in the before hooks, the default value MUST be returned."
884 )]
885 #[test]
886 fn default_value_covered_by_implementing_default_trait() {}
887
888 fn error<T>() -> Result<T, EvaluationError> {
889 Err(EvaluationError {
890 code: EvaluationErrorCode::General("error".to_string()),
891 message: None,
892 })
893 }
894}