Skip to content

Commit

Permalink
feat: add tests for context merging
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Reining <lukas.reining@codecentric.de>
  • Loading branch information
lukas-reining committed Nov 16, 2024
1 parent 2a21b9d commit 67e4368
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 13 deletions.
4 changes: 3 additions & 1 deletion openfeature/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
]

_evaluation_context = EvaluationContext()
_evaluation_transaction_context_propagator = NoOpTransactionContextPropagator()
_evaluation_transaction_context_propagator: TransactionContextPropagator = (
NoOpTransactionContextPropagator()
)

_hooks: typing.List[Hook] = []

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
TransactionContextPropagator,
)

_transaction_context_var: ContextVar[EvaluationContext] = ContextVar(
"transaction_context", default=EvaluationContext()
)


class ContextVarsTransactionContextPropagator(TransactionContextPropagator):
_transaction_context_var: ContextVar[EvaluationContext] = ContextVar(
"transaction_context", default=EvaluationContext()
)

def get_transaction_context(self) -> EvaluationContext:
return _transaction_context_var.get()
return self._transaction_context_var.get()

def set_transaction_context(self, transaction_context: EvaluationContext) -> None:
_transaction_context_var.set(transaction_context)
self._transaction_context_var.set(transaction_context)
49 changes: 48 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import time
import uuid
from concurrent.futures import ThreadPoolExecutor
from unittest.mock import MagicMock
from unittest.mock import MagicMock, Mock

import pytest

from openfeature import api
from openfeature.api import add_hooks, clear_hooks, get_client, set_provider
from openfeature.client import OpenFeatureClient
from openfeature.evaluation_context import EvaluationContext
from openfeature.event import EventDetails, ProviderEvent, ProviderEventDetails
from openfeature.exception import ErrorCode, OpenFeatureError
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
from openfeature.hook import Hook
from openfeature.provider import FeatureProvider, ProviderStatus
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
from openfeature.provider.no_op_provider import NoOpProvider
from openfeature.transaction_context import ContextVarsTransactionContextPropagator


@pytest.mark.parametrize(
Expand Down Expand Up @@ -384,3 +387,47 @@ def emit_events_task():
f2 = executor.submit(emit_events_task)
f1.result()
f2.result()


def test_client_should_merge_contexts():
api.clear_hooks()
api.set_transaction_context_propagator(ContextVarsTransactionContextPropagator())

provider = NoOpProvider()
provider.resolve_boolean_details = MagicMock(wraps=provider.resolve_boolean_details)
api.set_provider(provider)

# Global evaluation context
global_context = EvaluationContext(
targeting_key="global", attributes={"global_attr": "global_value"}
)
api.set_evaluation_context(global_context)

# Transaction context
transaction_context = EvaluationContext(
targeting_key="transaction",
attributes={"transaction_attr": "transaction_value"},
)
api.set_transaction_context(transaction_context)

# Client-specific context
client_context = EvaluationContext(
targeting_key="client", attributes={"client_attr": "client_value"}
)
client = OpenFeatureClient(domain=None, version=None, context=client_context)

# Invocation-specific context
invocation_context = EvaluationContext(
targeting_key="invocation", attributes={"invocation_attr": "invocation_value"}
)
client.get_boolean_details("flag", False, invocation_context)

# Retrieve the call arguments
args, kwargs = provider.resolve_boolean_details.call_args
flag_key, default_value, context = args

assert context.targeting_key == "invocation" # Last one in the merge chain
assert context.attributes["global_attr"] == "global_value"
assert context.attributes["transaction_attr"] == "transaction_value"
assert context.attributes["client_attr"] == "client_value"
assert context.attributes["invocation_attr"] == "invocation_value"
11 changes: 6 additions & 5 deletions tests/test_transaction_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
NoOpTransactionContextPropagator,
TransactionContextPropagator,
)
from openfeature.transaction_context.context_var_transaction_context_propagator import (
_transaction_context_var,
)


# Test cases
Expand Down Expand Up @@ -97,8 +94,12 @@ def test_should_propagate_event_when_context_set():
set_transaction_context(evaluation_context)

# Then
assert _transaction_context_var.get().targeting_key == "custom_key"
assert _transaction_context_var.get().attributes == {"attr1": "val1"}
assert (
custom_propagator._transaction_context_var.get().targeting_key == "custom_key"
)
assert custom_propagator._transaction_context_var.get().attributes == {
"attr1": "val1"
}


def test_context_vars_transaction_context_propagator_multiple_threads():
Expand Down

0 comments on commit 67e4368

Please sign in to comment.