Skip to content

Commit a8b9126

Browse files
committed
Add coverage for openfeature
1 parent 6b4df67 commit a8b9126

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

sentry_sdk/integrations/openfeature.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616

1717
class OpenFeatureIntegration(Integration):
18+
identifier = "openfeature"
1819

1920
@staticmethod
2021
def setup_once():
@@ -32,8 +33,14 @@ def error_processor(event, exc_info):
3233

3334
class OpenFeatureHook(Hook):
3435

35-
def after(self, hook_context, details, hints) -> None:
36+
def after(self, hook_context, details, hints):
3637
# type: (HookContext, FlagEvaluationDetails, HookHints) -> None
3738
if isinstance(details.value, bool):
3839
flags = sentry_sdk.get_current_scope().flags
3940
flags.set(details.flag_key, details.value)
41+
42+
def error(self, hook_context, exception, hints):
43+
# type: (HookContext, Exception, HookHints) -> None
44+
if isinstance(hook_context.default_value, bool):
45+
flags = sentry_sdk.get_current_scope().flags
46+
flags.set(hook_context.flag_key, hook_context.default_value)

sentry_sdk/scope.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,7 @@ def flags(self):
15561556
# type: () -> FlagBuffer
15571557
if self._flags is None:
15581558
self._flags = FlagBuffer(capacity=100)
1559-
self._flags
1559+
return self._flags
15601560

15611561

15621562
@contextmanager
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import asyncio
2+
import concurrent.futures as cf
3+
import sentry_sdk
4+
5+
from openfeature import api
6+
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
7+
from sentry_sdk.integrations.openfeature import OpenFeatureIntegration
8+
9+
10+
def test_openfeature_integration(sentry_init):
11+
sentry_init(integrations=[OpenFeatureIntegration()])
12+
13+
flags = {
14+
"hello": InMemoryFlag("on", {"on": True, "off": False}),
15+
"world": InMemoryFlag("off", {"on": True, "off": False}),
16+
}
17+
api.set_provider(InMemoryProvider(flags))
18+
19+
client = api.get_client()
20+
client.get_boolean_value("hello", default_value=False)
21+
client.get_boolean_value("world", default_value=False)
22+
client.get_boolean_value("other", default_value=True)
23+
24+
assert sentry_sdk.get_current_scope().flags.get() == [
25+
{"flag": "hello", "result": True},
26+
{"flag": "world", "result": False},
27+
{"flag": "other", "result": True},
28+
]
29+
30+
31+
def test_openfeature_integration_threaded(sentry_init):
32+
sentry_init(integrations=[OpenFeatureIntegration()])
33+
34+
flags = {
35+
"hello": InMemoryFlag("on", {"on": True, "off": False}),
36+
"world": InMemoryFlag("off", {"on": True, "off": False}),
37+
}
38+
api.set_provider(InMemoryProvider(flags))
39+
40+
client = api.get_client()
41+
client.get_boolean_value("hello", default_value=False)
42+
43+
def task(flag):
44+
# Create a new isolation scope for the thread. This means the flags
45+
with sentry_sdk.isolation_scope():
46+
client.get_boolean_value(flag, default_value=False)
47+
return [f["flag"] for f in sentry_sdk.get_current_scope().flags.get()]
48+
49+
with cf.ThreadPoolExecutor(max_workers=2) as pool:
50+
results = list(pool.map(task, ["world", "other"]))
51+
52+
assert results[0] == ["hello", "world"]
53+
assert results[1] == ["hello", "other"]
54+
55+
56+
def test_openfeature_integration_asyncio(sentry_init):
57+
"""Assert concurrently evaluated flags do not pollute one another."""
58+
59+
async def task(flag):
60+
with sentry_sdk.isolation_scope():
61+
client.get_boolean_value(flag, default_value=False)
62+
return [f["flag"] for f in sentry_sdk.get_current_scope().flags.get()]
63+
64+
async def runner():
65+
return asyncio.gather(task("world"), task("other"))
66+
67+
sentry_init(integrations=[OpenFeatureIntegration()])
68+
69+
flags = {
70+
"hello": InMemoryFlag("on", {"on": True, "off": False}),
71+
"world": InMemoryFlag("off", {"on": True, "off": False}),
72+
}
73+
api.set_provider(InMemoryProvider(flags))
74+
75+
client = api.get_client()
76+
client.get_boolean_value("hello", default_value=False)
77+
78+
results = asyncio.run(runner()).result()
79+
assert results[0] == ["hello", "world"]
80+
assert results[1] == ["hello", "other"]

0 commit comments

Comments
 (0)