1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
import concurrent.futures as cf
import sys
from random import random
from unittest import mock
from UnleashClient import UnleashClient
import pytest
import sentry_sdk
from sentry_sdk.integrations.unleash import UnleashIntegration
from tests.integrations.unleash.testutils import mock_unleash_client
def test_is_enabled(sentry_init, capture_events, uninstall_integration):
uninstall_integration(UnleashIntegration.identifier)
with mock_unleash_client():
client = UnleashClient() # type: ignore[arg-type]
sentry_init(integrations=[UnleashIntegration()])
client.is_enabled("hello")
client.is_enabled("world")
client.is_enabled("other")
events = capture_events()
sentry_sdk.capture_exception(Exception("something wrong!"))
assert len(events) == 1
assert events[0]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
{"flag": "world", "result": False},
{"flag": "other", "result": False},
]
}
def test_is_enabled_threaded(sentry_init, capture_events, uninstall_integration):
uninstall_integration(UnleashIntegration.identifier)
with mock_unleash_client():
client = UnleashClient() # type: ignore[arg-type]
sentry_init(integrations=[UnleashIntegration()])
events = capture_events()
def task(flag_key):
# Creates a new isolation scope for the thread.
# This means the evaluations in each task are captured separately.
with sentry_sdk.isolation_scope():
client.is_enabled(flag_key)
# use a tag to identify to identify events later on
sentry_sdk.set_tag("task_id", flag_key)
sentry_sdk.capture_exception(Exception("something wrong!"))
# Capture an eval before we split isolation scopes.
client.is_enabled("hello")
with cf.ThreadPoolExecutor(max_workers=2) as pool:
pool.map(task, ["world", "other"])
# Capture error in original scope
sentry_sdk.set_tag("task_id", "0")
sentry_sdk.capture_exception(Exception("something wrong!"))
assert len(events) == 3
events.sort(key=lambda e: e["tags"]["task_id"])
assert events[0]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
]
}
assert events[1]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
{"flag": "other", "result": False},
]
}
assert events[2]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
{"flag": "world", "result": False},
]
}
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
def test_is_enabled_asyncio(sentry_init, capture_events, uninstall_integration):
asyncio = pytest.importorskip("asyncio")
uninstall_integration(UnleashIntegration.identifier)
with mock_unleash_client():
client = UnleashClient() # type: ignore[arg-type]
sentry_init(integrations=[UnleashIntegration()])
events = capture_events()
async def task(flag_key):
with sentry_sdk.isolation_scope():
client.is_enabled(flag_key)
# use a tag to identify to identify events later on
sentry_sdk.set_tag("task_id", flag_key)
sentry_sdk.capture_exception(Exception("something wrong!"))
async def runner():
return asyncio.gather(task("world"), task("other"))
# Capture an eval before we split isolation scopes.
client.is_enabled("hello")
asyncio.run(runner())
# Capture error in original scope
sentry_sdk.set_tag("task_id", "0")
sentry_sdk.capture_exception(Exception("something wrong!"))
assert len(events) == 3
events.sort(key=lambda e: e["tags"]["task_id"])
assert events[0]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
]
}
assert events[1]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
{"flag": "other", "result": False},
]
}
assert events[2]["contexts"]["flags"] == {
"values": [
{"flag": "hello", "result": True},
{"flag": "world", "result": False},
]
}
def test_wraps_original(sentry_init, uninstall_integration):
with mock_unleash_client():
client = UnleashClient() # type: ignore[arg-type]
mock_is_enabled = mock.Mock(return_value=random() < 0.5)
client.is_enabled = mock_is_enabled
uninstall_integration(UnleashIntegration.identifier)
sentry_init(integrations=[UnleashIntegration()]) # type: ignore
res = client.is_enabled("test-flag", "arg", kwarg=1)
assert res == mock_is_enabled.return_value
assert mock_is_enabled.call_args == (
("test-flag", "arg"),
{"kwarg": 1},
)
def test_wrapper_attributes(sentry_init, uninstall_integration):
with mock_unleash_client():
client = UnleashClient() # type: ignore[arg-type]
original_is_enabled = client.is_enabled
uninstall_integration(UnleashIntegration.identifier)
sentry_init(integrations=[UnleashIntegration()]) # type: ignore
# Mock clients methods have not lost their qualified names after decoration.
assert client.is_enabled.__name__ == "is_enabled"
assert client.is_enabled.__qualname__ == original_is_enabled.__qualname__
|