Skip to content

Commit b63982b

Browse files
committed
Update launchdarkly and openfeature tests to be e2e
1 parent c76192e commit b63982b

File tree

4 files changed

+187
-77
lines changed

4 files changed

+187
-77
lines changed

tests/conftest.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,17 @@ def reset_integrations():
184184
_installed_integrations.clear()
185185

186186

187+
@pytest.fixture
188+
def uninstall_integration():
189+
"""Use to force the next call to sentry_init to re-install/setup an integration."""
190+
191+
def inner(identifier):
192+
_processed_integrations.discard(identifier)
193+
_installed_integrations.discard(identifier)
194+
195+
return inner
196+
197+
187198
@pytest.fixture
188199
def sentry_init(request):
189200
def inner(*a, **kw):

tests/integrations/featureflags/test_featureflags.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
import asyncio
22
import concurrent.futures as cf
33

4-
import pytest
5-
64
import sentry_sdk
7-
from sentry_sdk.integrations import _processed_integrations, _installed_integrations
85
from sentry_sdk.integrations.featureflags import (
96
FeatureFlagsIntegration,
107
add_feature_flag,
118
)
129

1310

14-
@pytest.fixture
15-
def uninstall_integration():
16-
"""Forces the next call to sentry_init to re-install/setup an integration."""
17-
18-
def inner(identifier):
19-
_processed_integrations.discard(identifier)
20-
_installed_integrations.discard(identifier)
21-
22-
return inner
23-
24-
2511
def test_featureflags_integration(sentry_init, capture_events, uninstall_integration):
2612
uninstall_integration(FeatureFlagsIntegration.identifier)
2713
sentry_init(integrations=[FeatureFlagsIntegration()])

tests/integrations/launchdarkly/test_launchdarkly.py

Lines changed: 91 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@
1919
"use_global_client",
2020
(False, True),
2121
)
22-
def test_launchdarkly_integration(sentry_init, use_global_client):
22+
def test_launchdarkly_integration(
23+
sentry_init, use_global_client, capture_events, uninstall_integration
24+
):
2325
td = TestData.data_source()
2426
config = Config("sdk-key", update_processor_class=td)
27+
28+
uninstall_integration(LaunchDarklyIntegration.identifier)
2529
if use_global_client:
2630
ldclient.set_config(config)
2731
sentry_init(integrations=[LaunchDarklyIntegration()])
@@ -39,72 +43,126 @@ def test_launchdarkly_integration(sentry_init, use_global_client):
3943
client.variation("world", Context.create("user1", "user"), False)
4044
client.variation("other", Context.create("user2", "user"), False)
4145

42-
assert sentry_sdk.get_current_scope().flags.get() == [
43-
{"flag": "hello", "result": True},
44-
{"flag": "world", "result": True},
45-
{"flag": "other", "result": False},
46-
]
46+
events = capture_events()
47+
sentry_sdk.capture_exception(Exception("something wrong!"))
48+
49+
assert len(events) == 1
50+
assert events[0]["contexts"]["flags"] == {
51+
"values": [
52+
{"flag": "hello", "result": True},
53+
{"flag": "world", "result": True},
54+
{"flag": "other", "result": False},
55+
]
56+
}
4757

4858

49-
def test_launchdarkly_integration_threaded(sentry_init):
59+
def test_launchdarkly_integration_threaded(
60+
sentry_init, capture_events, uninstall_integration
61+
):
5062
td = TestData.data_source()
5163
client = LDClient(config=Config("sdk-key", update_processor_class=td))
52-
sentry_init(integrations=[LaunchDarklyIntegration(ld_client=client)])
5364
context = Context.create("user1")
5465

66+
uninstall_integration(LaunchDarklyIntegration.identifier)
67+
sentry_init(integrations=[LaunchDarklyIntegration(ld_client=client)])
68+
events = capture_events()
69+
5570
def task(flag_key):
5671
# Creates a new isolation scope for the thread.
5772
# This means the evaluations in each task are captured separately.
5873
with sentry_sdk.isolation_scope():
5974
client.variation(flag_key, context, False)
60-
return sentry_sdk.get_current_scope().flags.get()
75+
# use a tag to identify to identify events later on
76+
sentry_sdk.set_tag("task_id", flag_key)
77+
sentry_sdk.capture_exception(Exception("something wrong!"))
6178

6279
td.update(td.flag("hello").variation_for_all(True))
6380
td.update(td.flag("world").variation_for_all(False))
6481
# Capture an eval before we split isolation scopes.
6582
client.variation("hello", context, False)
6683

6784
with cf.ThreadPoolExecutor(max_workers=2) as pool:
68-
results = list(pool.map(task, ["world", "other"]))
69-
70-
assert results[0] == [
71-
{"flag": "hello", "result": True},
72-
{"flag": "world", "result": False},
73-
]
74-
assert results[1] == [
75-
{"flag": "hello", "result": True},
76-
{"flag": "other", "result": False},
77-
]
78-
79-
80-
def test_launchdarkly_integration_asyncio(sentry_init):
85+
pool.map(task, ["world", "other"])
86+
87+
# Capture error in original scope
88+
sentry_sdk.set_tag("task_id", "0")
89+
sentry_sdk.capture_exception(Exception("something wrong!"))
90+
91+
assert len(events) == 3
92+
events.sort(key=lambda e: e["tags"]["task_id"])
93+
94+
assert events[0]["contexts"]["flags"] == {
95+
"values": [
96+
{"flag": "hello", "result": True},
97+
]
98+
}
99+
assert events[1]["contexts"]["flags"] == {
100+
"values": [
101+
{"flag": "hello", "result": True},
102+
{"flag": "other", "result": False},
103+
]
104+
}
105+
assert events[2]["contexts"]["flags"] == {
106+
"values": [
107+
{"flag": "hello", "result": True},
108+
{"flag": "world", "result": False},
109+
]
110+
}
111+
112+
113+
def test_launchdarkly_integration_asyncio(
114+
sentry_init, capture_events, uninstall_integration
115+
):
81116
"""Assert concurrently evaluated flags do not pollute one another."""
82117
td = TestData.data_source()
83118
client = LDClient(config=Config("sdk-key", update_processor_class=td))
84-
sentry_init(integrations=[LaunchDarklyIntegration(ld_client=client)])
85119
context = Context.create("user1")
86120

121+
uninstall_integration(LaunchDarklyIntegration.identifier)
122+
sentry_init(integrations=[LaunchDarklyIntegration(ld_client=client)])
123+
events = capture_events()
124+
87125
async def task(flag_key):
88126
with sentry_sdk.isolation_scope():
89127
client.variation(flag_key, context, False)
90-
return sentry_sdk.get_current_scope().flags.get()
128+
# use a tag to identify to identify events later on
129+
sentry_sdk.set_tag("task_id", flag_key)
130+
sentry_sdk.capture_exception(Exception("something wrong!"))
91131

92132
async def runner():
93133
return asyncio.gather(task("world"), task("other"))
94134

95135
td.update(td.flag("hello").variation_for_all(True))
96136
td.update(td.flag("world").variation_for_all(False))
137+
# Capture an eval before we split isolation scopes.
97138
client.variation("hello", context, False)
98139

99-
results = asyncio.run(runner()).result()
100-
assert results[0] == [
101-
{"flag": "hello", "result": True},
102-
{"flag": "world", "result": False},
103-
]
104-
assert results[1] == [
105-
{"flag": "hello", "result": True},
106-
{"flag": "other", "result": False},
107-
]
140+
asyncio.run(runner())
141+
142+
# Capture error in original scope
143+
sentry_sdk.set_tag("task_id", "0")
144+
sentry_sdk.capture_exception(Exception("something wrong!"))
145+
146+
assert len(events) == 3
147+
events.sort(key=lambda e: e["tags"]["task_id"])
148+
149+
assert events[0]["contexts"]["flags"] == {
150+
"values": [
151+
{"flag": "hello", "result": True},
152+
]
153+
}
154+
assert events[1]["contexts"]["flags"] == {
155+
"values": [
156+
{"flag": "hello", "result": True},
157+
{"flag": "other", "result": False},
158+
]
159+
}
160+
assert events[2]["contexts"]["flags"] == {
161+
"values": [
162+
{"flag": "hello", "result": True},
163+
{"flag": "world", "result": False},
164+
]
165+
}
108166

109167

110168
def test_launchdarkly_integration_did_not_enable(monkeypatch):

tests/integrations/openfeature/test_openfeature.py

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from sentry_sdk.integrations.openfeature import OpenFeatureIntegration
88

99

10-
def test_openfeature_integration(sentry_init):
10+
def test_openfeature_integration(sentry_init, capture_events, uninstall_integration):
11+
uninstall_integration(OpenFeatureIntegration.identifier)
1112
sentry_init(integrations=[OpenFeatureIntegration()])
1213

1314
flags = {
@@ -21,72 +22,126 @@ def test_openfeature_integration(sentry_init):
2122
client.get_boolean_value("world", default_value=False)
2223
client.get_boolean_value("other", default_value=True)
2324

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-
]
25+
events = capture_events()
26+
sentry_sdk.capture_exception(Exception("something wrong!"))
27+
28+
assert len(events) == 1
29+
assert events[0]["contexts"]["flags"] == {
30+
"values": [
31+
{"flag": "hello", "result": True},
32+
{"flag": "world", "result": False},
33+
{"flag": "other", "result": True},
34+
]
35+
}
2936

3037

31-
def test_openfeature_integration_threaded(sentry_init):
38+
def test_openfeature_integration_threaded(
39+
sentry_init, capture_events, uninstall_integration
40+
):
41+
uninstall_integration(OpenFeatureIntegration.identifier)
3242
sentry_init(integrations=[OpenFeatureIntegration()])
43+
events = capture_events()
3344

3445
flags = {
3546
"hello": InMemoryFlag("on", {"on": True, "off": False}),
3647
"world": InMemoryFlag("off", {"on": True, "off": False}),
3748
}
3849
api.set_provider(InMemoryProvider(flags))
3950

51+
# Capture an eval before we split isolation scopes.
4052
client = api.get_client()
4153
client.get_boolean_value("hello", default_value=False)
4254

4355
def task(flag):
4456
# Create a new isolation scope for the thread. This means the flags
4557
with sentry_sdk.isolation_scope():
4658
client.get_boolean_value(flag, default_value=False)
47-
return sentry_sdk.get_current_scope().flags.get()
59+
# use a tag to identify to identify events later on
60+
sentry_sdk.set_tag("task_id", flag)
61+
sentry_sdk.capture_exception(Exception("something wrong!"))
4862

63+
# Run tasks in separate threads
4964
with cf.ThreadPoolExecutor(max_workers=2) as pool:
50-
results = list(pool.map(task, ["world", "other"]))
65+
pool.map(task, ["world", "other"])
5166

52-
assert results[0] == [
53-
{"flag": "hello", "result": True},
54-
{"flag": "world", "result": False},
55-
]
56-
assert results[1] == [
57-
{"flag": "hello", "result": True},
58-
{"flag": "other", "result": False},
59-
]
67+
# Capture error in original scope
68+
sentry_sdk.set_tag("task_id", "0")
69+
sentry_sdk.capture_exception(Exception("something wrong!"))
6070

71+
assert len(events) == 3
72+
events.sort(key=lambda e: e["tags"]["task_id"])
6173

62-
def test_openfeature_integration_asyncio(sentry_init):
74+
assert events[0]["contexts"]["flags"] == {
75+
"values": [
76+
{"flag": "hello", "result": True},
77+
]
78+
}
79+
assert events[1]["contexts"]["flags"] == {
80+
"values": [
81+
{"flag": "hello", "result": True},
82+
{"flag": "other", "result": False},
83+
]
84+
}
85+
assert events[2]["contexts"]["flags"] == {
86+
"values": [
87+
{"flag": "hello", "result": True},
88+
{"flag": "world", "result": False},
89+
]
90+
}
91+
92+
93+
def test_openfeature_integration_asyncio(
94+
sentry_init, capture_events, uninstall_integration
95+
):
6396
"""Assert concurrently evaluated flags do not pollute one another."""
6497

98+
uninstall_integration(OpenFeatureIntegration.identifier)
99+
sentry_init(integrations=[OpenFeatureIntegration()])
100+
events = capture_events()
101+
65102
async def task(flag):
66103
with sentry_sdk.isolation_scope():
67104
client.get_boolean_value(flag, default_value=False)
68-
return sentry_sdk.get_current_scope().flags.get()
105+
# use a tag to identify to identify events later on
106+
sentry_sdk.set_tag("task_id", flag)
107+
sentry_sdk.capture_exception(Exception("something wrong!"))
69108

70109
async def runner():
71110
return asyncio.gather(task("world"), task("other"))
72111

73-
sentry_init(integrations=[OpenFeatureIntegration()])
74-
75112
flags = {
76113
"hello": InMemoryFlag("on", {"on": True, "off": False}),
77114
"world": InMemoryFlag("off", {"on": True, "off": False}),
78115
}
79116
api.set_provider(InMemoryProvider(flags))
80117

118+
# Capture an eval before we split isolation scopes.
81119
client = api.get_client()
82120
client.get_boolean_value("hello", default_value=False)
83121

84-
results = asyncio.run(runner()).result()
85-
assert results[0] == [
86-
{"flag": "hello", "result": True},
87-
{"flag": "world", "result": False},
88-
]
89-
assert results[1] == [
90-
{"flag": "hello", "result": True},
91-
{"flag": "other", "result": False},
92-
]
122+
asyncio.run(runner())
123+
124+
# Capture error in original scope
125+
sentry_sdk.set_tag("task_id", "0")
126+
sentry_sdk.capture_exception(Exception("something wrong!"))
127+
128+
assert len(events) == 3
129+
events.sort(key=lambda e: e["tags"]["task_id"])
130+
131+
assert events[0]["contexts"]["flags"] == {
132+
"values": [
133+
{"flag": "hello", "result": True},
134+
]
135+
}
136+
assert events[1]["contexts"]["flags"] == {
137+
"values": [
138+
{"flag": "hello", "result": True},
139+
{"flag": "other", "result": False},
140+
]
141+
}
142+
assert events[2]["contexts"]["flags"] == {
143+
"values": [
144+
{"flag": "hello", "result": True},
145+
{"flag": "world", "result": False},
146+
]
147+
}

0 commit comments

Comments
 (0)