Skip to content

Commit 47d29c3

Browse files
committed
Threaded and asyncio tests
1 parent 0538bce commit 47d29c3

File tree

1 file changed

+202
-0
lines changed

1 file changed

+202
-0
lines changed

tests/integrations/unleash/test_unleash.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import concurrent.futures as cf
2+
import sys
13
from random import random
24
from unittest.mock import patch
35

6+
import pytest
7+
48
import sentry_sdk
59
from sentry_sdk.integrations.unleash import UnleashIntegration
610
from tests.integrations.unleash.testutils import MockUnleashClient
@@ -57,6 +61,204 @@ def test_get_variant(sentry_init, capture_events, uninstall_integration):
5761
}
5862

5963

64+
@patch("sentry_sdk.integrations.unleash.UnleashClient", MockUnleashClient)
65+
def test_is_enabled_threaded(sentry_init, capture_events, uninstall_integration):
66+
client = MockUnleashClient()
67+
uninstall_integration(UnleashIntegration)
68+
sentry_init(integrations=[UnleashIntegration()])
69+
events = capture_events()
70+
71+
def task(flag_key):
72+
# Creates a new isolation scope for the thread.
73+
# This means the evaluations in each task are captured separately.
74+
with sentry_sdk.isolation_scope():
75+
client.is_enabled(flag_key)
76+
# use a tag to identify to identify events later on
77+
sentry_sdk.set_tag("task_id", flag_key)
78+
sentry_sdk.capture_exception(Exception("something wrong!"))
79+
80+
# Capture an eval before we split isolation scopes.
81+
client.is_enabled("hello")
82+
83+
with cf.ThreadPoolExecutor(max_workers=2) as pool:
84+
pool.map(task, ["world", "other"])
85+
86+
# Capture error in original scope
87+
sentry_sdk.set_tag("task_id", "0")
88+
sentry_sdk.capture_exception(Exception("something wrong!"))
89+
90+
assert len(events) == 3
91+
events.sort(key=lambda e: e["tags"]["task_id"])
92+
93+
assert events[0]["contexts"]["flags"] == {
94+
"values": [
95+
{"flag": "hello", "result": True},
96+
]
97+
}
98+
assert events[1]["contexts"]["flags"] == {
99+
"values": [
100+
{"flag": "hello", "result": True},
101+
{"flag": "other", "result": False},
102+
]
103+
}
104+
assert events[2]["contexts"]["flags"] == {
105+
"values": [
106+
{"flag": "hello", "result": True},
107+
{"flag": "world", "result": False},
108+
]
109+
}
110+
111+
112+
@patch("sentry_sdk.integrations.unleash.UnleashClient", MockUnleashClient)
113+
def test_get_variant_threaded(sentry_init, capture_events, uninstall_integration):
114+
client = MockUnleashClient()
115+
uninstall_integration(UnleashIntegration)
116+
sentry_init(integrations=[UnleashIntegration()])
117+
events = capture_events()
118+
119+
def task(flag_key):
120+
# Creates a new isolation scope for the thread.
121+
# This means the evaluations in each task are captured separately.
122+
with sentry_sdk.isolation_scope():
123+
client.get_variant(flag_key)
124+
# use a tag to identify to identify events later on
125+
sentry_sdk.set_tag("task_id", flag_key)
126+
sentry_sdk.capture_exception(Exception("something wrong!"))
127+
128+
# Capture an eval before we split isolation scopes.
129+
client.get_variant("hello")
130+
131+
with cf.ThreadPoolExecutor(max_workers=2) as pool:
132+
pool.map(task, ["other", "toggle_feature"])
133+
134+
# Capture error in original scope
135+
sentry_sdk.set_tag("task_id", "0")
136+
sentry_sdk.capture_exception(Exception("something wrong!"))
137+
138+
assert len(events) == 3
139+
events.sort(key=lambda e: e["tags"]["task_id"])
140+
141+
assert events[0]["contexts"]["flags"] == {
142+
"values": [
143+
{"flag": "hello", "result": False},
144+
]
145+
}
146+
assert events[1]["contexts"]["flags"] == {
147+
"values": [
148+
{"flag": "hello", "result": False},
149+
{"flag": "other", "result": False},
150+
]
151+
}
152+
assert events[2]["contexts"]["flags"] == {
153+
"values": [
154+
{"flag": "hello", "result": False},
155+
{"flag": "toggle_feature", "result": True},
156+
]
157+
}
158+
159+
160+
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
161+
@patch("sentry_sdk.integrations.unleash.UnleashClient", MockUnleashClient)
162+
def test_is_enabled_asyncio(sentry_init, capture_events, uninstall_integration):
163+
asyncio = pytest.importorskip("asyncio")
164+
165+
client = MockUnleashClient()
166+
uninstall_integration(UnleashIntegration)
167+
sentry_init(integrations=[UnleashIntegration()])
168+
events = capture_events()
169+
170+
async def task(flag_key):
171+
with sentry_sdk.isolation_scope():
172+
client.is_enabled(flag_key)
173+
# use a tag to identify to identify events later on
174+
sentry_sdk.set_tag("task_id", flag_key)
175+
sentry_sdk.capture_exception(Exception("something wrong!"))
176+
177+
async def runner():
178+
return asyncio.gather(task("world"), task("other"))
179+
180+
# Capture an eval before we split isolation scopes.
181+
client.is_enabled("hello")
182+
183+
asyncio.run(runner())
184+
185+
# Capture error in original scope
186+
sentry_sdk.set_tag("task_id", "0")
187+
sentry_sdk.capture_exception(Exception("something wrong!"))
188+
189+
assert len(events) == 3
190+
events.sort(key=lambda e: e["tags"]["task_id"])
191+
192+
assert events[0]["contexts"]["flags"] == {
193+
"values": [
194+
{"flag": "hello", "result": True},
195+
]
196+
}
197+
assert events[1]["contexts"]["flags"] == {
198+
"values": [
199+
{"flag": "hello", "result": True},
200+
{"flag": "other", "result": False},
201+
]
202+
}
203+
assert events[2]["contexts"]["flags"] == {
204+
"values": [
205+
{"flag": "hello", "result": True},
206+
{"flag": "world", "result": False},
207+
]
208+
}
209+
210+
211+
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
212+
@patch("sentry_sdk.integrations.unleash.UnleashClient", MockUnleashClient)
213+
def test_get_variant_asyncio(sentry_init, capture_events, uninstall_integration):
214+
asyncio = pytest.importorskip("asyncio")
215+
216+
client = MockUnleashClient()
217+
uninstall_integration(UnleashIntegration)
218+
sentry_init(integrations=[UnleashIntegration()])
219+
events = capture_events()
220+
221+
async def task(flag_key):
222+
with sentry_sdk.isolation_scope():
223+
client.get_variant(flag_key)
224+
# use a tag to identify to identify events later on
225+
sentry_sdk.set_tag("task_id", flag_key)
226+
sentry_sdk.capture_exception(Exception("something wrong!"))
227+
228+
async def runner():
229+
return asyncio.gather(task("other"), task("toggle_feature"))
230+
231+
# Capture an eval before we split isolation scopes.
232+
client.get_variant("hello")
233+
234+
asyncio.run(runner())
235+
236+
# Capture error in original scope
237+
sentry_sdk.set_tag("task_id", "0")
238+
sentry_sdk.capture_exception(Exception("something wrong!"))
239+
240+
assert len(events) == 3
241+
events.sort(key=lambda e: e["tags"]["task_id"])
242+
243+
assert events[0]["contexts"]["flags"] == {
244+
"values": [
245+
{"flag": "hello", "result": False},
246+
]
247+
}
248+
assert events[1]["contexts"]["flags"] == {
249+
"values": [
250+
{"flag": "hello", "result": False},
251+
{"flag": "other", "result": False},
252+
]
253+
}
254+
assert events[2]["contexts"]["flags"] == {
255+
"values": [
256+
{"flag": "hello", "result": False},
257+
{"flag": "toggle_feature", "result": True},
258+
]
259+
}
260+
261+
60262
@patch("sentry_sdk.integrations.unleash.UnleashClient", MockUnleashClient)
61263
def test_wraps_original(sentry_init, uninstall_integration):
62264
uninstall_integration(UnleashIntegration)

0 commit comments

Comments
 (0)