|
1 | 1 | import time |
2 | 2 | import uuid |
3 | 3 | from concurrent.futures import ThreadPoolExecutor |
4 | | -from unittest.mock import MagicMock |
| 4 | +from unittest.mock import MagicMock, Mock |
5 | 5 |
|
6 | 6 | import pytest |
7 | 7 |
|
| 8 | +from openfeature import api |
8 | 9 | from openfeature.api import add_hooks, clear_hooks, get_client, set_provider |
9 | 10 | from openfeature.client import OpenFeatureClient |
| 11 | +from openfeature.evaluation_context import EvaluationContext |
10 | 12 | from openfeature.event import EventDetails, ProviderEvent, ProviderEventDetails |
11 | 13 | from openfeature.exception import ErrorCode, OpenFeatureError |
12 | 14 | from openfeature.flag_evaluation import FlagResolutionDetails, Reason |
13 | 15 | from openfeature.hook import Hook |
14 | 16 | from openfeature.provider import FeatureProvider, ProviderStatus |
15 | 17 | from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider |
16 | 18 | from openfeature.provider.no_op_provider import NoOpProvider |
| 19 | +from openfeature.transaction_context import ContextVarsTransactionContextPropagator |
17 | 20 |
|
18 | 21 |
|
19 | 22 | @pytest.mark.parametrize( |
@@ -384,3 +387,47 @@ def emit_events_task(): |
384 | 387 | f2 = executor.submit(emit_events_task) |
385 | 388 | f1.result() |
386 | 389 | f2.result() |
| 390 | + |
| 391 | + |
| 392 | +def test_client_should_merge_contexts(): |
| 393 | + api.clear_hooks() |
| 394 | + api.set_transaction_context_propagator(ContextVarsTransactionContextPropagator()) |
| 395 | + |
| 396 | + provider = NoOpProvider() |
| 397 | + provider.resolve_boolean_details = MagicMock(wraps=provider.resolve_boolean_details) |
| 398 | + api.set_provider(provider) |
| 399 | + |
| 400 | + # Global evaluation context |
| 401 | + global_context = EvaluationContext( |
| 402 | + targeting_key="global", attributes={"global_attr": "global_value"} |
| 403 | + ) |
| 404 | + api.set_evaluation_context(global_context) |
| 405 | + |
| 406 | + # Transaction context |
| 407 | + transaction_context = EvaluationContext( |
| 408 | + targeting_key="transaction", |
| 409 | + attributes={"transaction_attr": "transaction_value"}, |
| 410 | + ) |
| 411 | + api.set_transaction_context(transaction_context) |
| 412 | + |
| 413 | + # Client-specific context |
| 414 | + client_context = EvaluationContext( |
| 415 | + targeting_key="client", attributes={"client_attr": "client_value"} |
| 416 | + ) |
| 417 | + client = OpenFeatureClient(domain=None, version=None, context=client_context) |
| 418 | + |
| 419 | + # Invocation-specific context |
| 420 | + invocation_context = EvaluationContext( |
| 421 | + targeting_key="invocation", attributes={"invocation_attr": "invocation_value"} |
| 422 | + ) |
| 423 | + client.get_boolean_details("flag", False, invocation_context) |
| 424 | + |
| 425 | + # Retrieve the call arguments |
| 426 | + args, kwargs = provider.resolve_boolean_details.call_args |
| 427 | + flag_key, default_value, context = args |
| 428 | + |
| 429 | + assert context.targeting_key == "invocation" # Last one in the merge chain |
| 430 | + assert context.attributes["global_attr"] == "global_value" |
| 431 | + assert context.attributes["transaction_attr"] == "transaction_value" |
| 432 | + assert context.attributes["client_attr"] == "client_value" |
| 433 | + assert context.attributes["invocation_attr"] == "invocation_value" |
0 commit comments