From a153e192c44352f312c8d7f0dcbe2ed4ddea966f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 10 Jan 2025 14:40:31 +0100 Subject: [PATCH 01/14] Moved adding of flags context into scope --- sentry_sdk/flag_utils.py | 11 +---------- sentry_sdk/integrations/feature_flags.py | 8 -------- sentry_sdk/integrations/launchdarkly.py | 7 ------- sentry_sdk/integrations/openfeature.py | 4 ---- sentry_sdk/integrations/unleash.py | 5 ----- sentry_sdk/scope.py | 3 +++ 6 files changed, 4 insertions(+), 34 deletions(-) diff --git a/sentry_sdk/flag_utils.py b/sentry_sdk/flag_utils.py index cf4800e855..5a6e483e21 100644 --- a/sentry_sdk/flag_utils.py +++ b/sentry_sdk/flag_utils.py @@ -1,11 +1,9 @@ from typing import TYPE_CHECKING -import sentry_sdk from sentry_sdk._lru_cache import LRUCache if TYPE_CHECKING: - from typing import TypedDict, Optional - from sentry_sdk._types import Event, ExcInfo + from typing import TypedDict FlagData = TypedDict("FlagData", {"flag": str, "result": bool}) @@ -31,10 +29,3 @@ def get(self): def set(self, flag, result): # type: (str, bool) -> None self.buffer.set(flag, result) - - -def flag_error_processor(event, exc_info): - # type: (Event, ExcInfo) -> Optional[Event] - scope = sentry_sdk.get_current_scope() - event["contexts"]["flags"] = {"values": scope.flags.get()} - return event diff --git a/sentry_sdk/integrations/feature_flags.py b/sentry_sdk/integrations/feature_flags.py index 2aeabffbfa..2739e93f3e 100644 --- a/sentry_sdk/integrations/feature_flags.py +++ b/sentry_sdk/integrations/feature_flags.py @@ -1,5 +1,3 @@ -from sentry_sdk.flag_utils import flag_error_processor - import sentry_sdk from sentry_sdk.integrations import Integration @@ -27,12 +25,6 @@ class FeatureFlagsIntegration(Integration): identifier = "feature_flags" - @staticmethod - def setup_once(): - # type: () -> None - scope = sentry_sdk.get_current_scope() - scope.add_error_processor(flag_error_processor) - def add_feature_flag(flag, result): # type: (str, bool) -> None diff --git a/sentry_sdk/integrations/launchdarkly.py b/sentry_sdk/integrations/launchdarkly.py index a9eef9e1a9..e0c1561a28 100644 --- a/sentry_sdk/integrations/launchdarkly.py +++ b/sentry_sdk/integrations/launchdarkly.py @@ -2,7 +2,6 @@ import sentry_sdk from sentry_sdk.integrations import DidNotEnable, Integration -from sentry_sdk.flag_utils import flag_error_processor try: import ldclient @@ -38,12 +37,6 @@ def __init__(self, ld_client=None): # Register the flag collection hook with the LD client. client.add_hook(LaunchDarklyHook()) - @staticmethod - def setup_once(): - # type: () -> None - scope = sentry_sdk.get_current_scope() - scope.add_error_processor(flag_error_processor) - class LaunchDarklyHook(Hook): diff --git a/sentry_sdk/integrations/openfeature.py b/sentry_sdk/integrations/openfeature.py index 18f968a703..bf66b94e8b 100644 --- a/sentry_sdk/integrations/openfeature.py +++ b/sentry_sdk/integrations/openfeature.py @@ -2,7 +2,6 @@ import sentry_sdk from sentry_sdk.integrations import DidNotEnable, Integration -from sentry_sdk.flag_utils import flag_error_processor try: from openfeature import api @@ -21,9 +20,6 @@ class OpenFeatureIntegration(Integration): @staticmethod def setup_once(): # type: () -> None - scope = sentry_sdk.get_current_scope() - scope.add_error_processor(flag_error_processor) - # Register the hook within the global openfeature hooks list. api.add_hooks(hooks=[OpenFeatureHook()]) diff --git a/sentry_sdk/integrations/unleash.py b/sentry_sdk/integrations/unleash.py index 33b0a4b9dc..442ec39d0f 100644 --- a/sentry_sdk/integrations/unleash.py +++ b/sentry_sdk/integrations/unleash.py @@ -2,7 +2,6 @@ from typing import Any import sentry_sdk -from sentry_sdk.flag_utils import flag_error_processor from sentry_sdk.integrations import Integration, DidNotEnable try: @@ -49,7 +48,3 @@ def sentry_get_variant(self, feature, *args, **kwargs): UnleashClient.is_enabled = sentry_is_enabled # type: ignore UnleashClient.get_variant = sentry_get_variant # type: ignore - - # Error processor - scope = sentry_sdk.get_current_scope() - scope.add_error_processor(flag_error_processor) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index cf72fabdd1..85a36109ef 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1378,6 +1378,9 @@ def _apply_contexts_to_event(self, event, hint, options): else: contexts["trace"] = self.get_trace_context() + # Add "flags" context + contexts.setdefault("flags", {}).update({"values": self.flags.get()}) + def _drop(self, cause, ty): # type: (Any, str) -> Optional[Any] logger.info("%s (%s) dropped event", ty, cause) From ea9d1365844eab00a66d9f8abd0cc483aaf4cad9 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 10 Jan 2025 14:59:42 +0100 Subject: [PATCH 02/14] Only add flags when there are some --- sentry_sdk/scope.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 85a36109ef..a611355c17 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1379,7 +1379,9 @@ def _apply_contexts_to_event(self, event, hint, options): contexts["trace"] = self.get_trace_context() # Add "flags" context - contexts.setdefault("flags", {}).update({"values": self.flags.get()}) + flags = self.flags.get() + if len(flags) > 0: + contexts.setdefault("flags", {}).update({"values": flags}) def _drop(self, cause, ty): # type: (Any, str) -> Optional[Any] From a5c6da2ce37db58ff73bfce68c36af49991ee5d3 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 10 Jan 2025 15:03:23 +0100 Subject: [PATCH 03/14] Added necessary functions --- sentry_sdk/integrations/feature_flags.py | 4 ++++ sentry_sdk/integrations/launchdarkly.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/sentry_sdk/integrations/feature_flags.py b/sentry_sdk/integrations/feature_flags.py index 2739e93f3e..1759252641 100644 --- a/sentry_sdk/integrations/feature_flags.py +++ b/sentry_sdk/integrations/feature_flags.py @@ -25,6 +25,10 @@ class FeatureFlagsIntegration(Integration): identifier = "feature_flags" + def setup_once(): + # type: () -> None + pass + def add_feature_flag(flag, result): # type: (str, bool) -> None diff --git a/sentry_sdk/integrations/launchdarkly.py b/sentry_sdk/integrations/launchdarkly.py index e0c1561a28..0bf81845eb 100644 --- a/sentry_sdk/integrations/launchdarkly.py +++ b/sentry_sdk/integrations/launchdarkly.py @@ -37,6 +37,10 @@ def __init__(self, ld_client=None): # Register the flag collection hook with the LD client. client.add_hook(LaunchDarklyHook()) + def setup_once(): + # type: () -> None + pass + class LaunchDarklyHook(Hook): From c17b654586c66510a53215106a91c114733f6752 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 09:57:49 +0100 Subject: [PATCH 04/14] Always add the flags context (even if empty) but only for error events. --- sentry_sdk/scope.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index a611355c17..8cfe1f4dbd 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1378,10 +1378,10 @@ def _apply_contexts_to_event(self, event, hint, options): else: contexts["trace"] = self.get_trace_context() - # Add "flags" context + def _apply_flags_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None flags = self.flags.get() - if len(flags) > 0: - contexts.setdefault("flags", {}).update({"values": flags}) + event.setdefault("contexts", {}).setdefault("flags", {}).update({"values": flags}) def _drop(self, cause, ty): # type: (Any, str) -> Optional[Any] @@ -1481,6 +1481,7 @@ def apply_to_event( if not is_transaction and not is_check_in: self._apply_breadcrumbs_to_event(event, hint, options) + self._apply_flags_to_event(event, hint, options) event = self.run_error_processors(event, hint) if event is None: From d9d76867af40101070dcba294c72ee7817d7f434 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 10:27:39 +0100 Subject: [PATCH 05/14] Make flags work when merging scopes --- sentry_sdk/scope.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 8cfe1f4dbd..edb53f7b50 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1524,6 +1524,12 @@ def update_from_scope(self, scope): self._propagation_context = scope._propagation_context if scope._session: self._session = scope._session + if scope._flags: + if not self._flags: + self._flags = deepcopy(scope._flags) + else: + for flag in scope._flags.get(): + self._flags.set(flag["flag"], flag["result"]) def update_from_kwargs( self, From 404f3498b9fc5c4f03d3b248f710f826e00b18f4 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 10:43:04 +0100 Subject: [PATCH 06/14] formatting --- sentry_sdk/scope.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index edb53f7b50..0af3b2be0e 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1381,7 +1381,9 @@ def _apply_contexts_to_event(self, event, hint, options): def _apply_flags_to_event(self, event, hint, options): # type: (Event, Hint, Optional[Dict[str, Any]]) -> None flags = self.flags.get() - event.setdefault("contexts", {}).setdefault("flags", {}).update({"values": flags}) + event.setdefault("contexts", {}).setdefault("flags", {}).update( + {"values": flags} + ) def _drop(self, cause, ty): # type: (Any, str) -> Optional[Any] From a497db24049bd89617b64c2e0668698ac0f539a2 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 11:38:18 +0100 Subject: [PATCH 07/14] Only add flags context if there are flags --- sentry_sdk/scope.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 0af3b2be0e..219d863148 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1381,9 +1381,10 @@ def _apply_contexts_to_event(self, event, hint, options): def _apply_flags_to_event(self, event, hint, options): # type: (Event, Hint, Optional[Dict[str, Any]]) -> None flags = self.flags.get() - event.setdefault("contexts", {}).setdefault("flags", {}).update( - {"values": flags} - ) + if len(flags) > 0: + event.setdefault("contexts", {}).setdefault("flags", {}).update( + {"values": flags} + ) def _drop(self, cause, ty): # type: (Any, str) -> Optional[Any] From ba9101051e997261d5765b4862fd9ce9d5e68fe6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 11:58:20 +0100 Subject: [PATCH 08/14] Cleanup --- sentry_sdk/integrations/feature_flags.py | 29 ------------------------ sentry_sdk/integrations/launchdarkly.py | 1 + 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/sentry_sdk/integrations/feature_flags.py b/sentry_sdk/integrations/feature_flags.py index 1759252641..b683aae301 100644 --- a/sentry_sdk/integrations/feature_flags.py +++ b/sentry_sdk/integrations/feature_flags.py @@ -1,33 +1,4 @@ import sentry_sdk -from sentry_sdk.integrations import Integration - - -class FeatureFlagsIntegration(Integration): - """ - Sentry integration for capturing feature flags on error events. To manually buffer flag data, - call `integrations.featureflags.add_feature_flag`. We recommend you do this on each flag - evaluation. - - See the [feature flag documentation](https://develop.sentry.dev/sdk/expected-features/#feature-flags) - for more information. - - @example - ``` - import sentry_sdk - from sentry_sdk.integrations.feature_flags import FeatureFlagsIntegration, add_feature_flag - - sentry_sdk.init(dsn="my_dsn", integrations=[FeatureFlagsIntegration()]); - - add_feature_flag('my-flag', true); - sentry_sdk.capture_exception(Exception('broke')); // 'my-flag' should be captured on this Sentry event. - ``` - """ - - identifier = "feature_flags" - - def setup_once(): - # type: () -> None - pass def add_feature_flag(flag, result): diff --git a/sentry_sdk/integrations/launchdarkly.py b/sentry_sdk/integrations/launchdarkly.py index 0bf81845eb..cb9e911463 100644 --- a/sentry_sdk/integrations/launchdarkly.py +++ b/sentry_sdk/integrations/launchdarkly.py @@ -37,6 +37,7 @@ def __init__(self, ld_client=None): # Register the flag collection hook with the LD client. client.add_hook(LaunchDarklyHook()) + @staticmethod def setup_once(): # type: () -> None pass From 21123847b24c798ecddfc1b478af5e597bb78279 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 12:01:55 +0100 Subject: [PATCH 09/14] More cleanup --- sentry_sdk/integrations/feature_flags.py | 2 +- .../feature_flags/test_feature_flags.py | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/sentry_sdk/integrations/feature_flags.py b/sentry_sdk/integrations/feature_flags.py index b683aae301..8b693e7a48 100644 --- a/sentry_sdk/integrations/feature_flags.py +++ b/sentry_sdk/integrations/feature_flags.py @@ -4,7 +4,7 @@ def add_feature_flag(flag, result): # type: (str, bool) -> None """ - Records a flag and its value to be sent on subsequent error events by FeatureFlagsIntegration. + Records a flag and its value to be sent on subsequent error events. We recommend you do this on flag evaluations. Flags are buffered per Sentry scope. """ flags = sentry_sdk.get_current_scope().flags diff --git a/tests/integrations/feature_flags/test_feature_flags.py b/tests/integrations/feature_flags/test_feature_flags.py index ca6ac16949..bb3c34c410 100644 --- a/tests/integrations/feature_flags/test_feature_flags.py +++ b/tests/integrations/feature_flags/test_feature_flags.py @@ -4,15 +4,11 @@ import pytest import sentry_sdk -from sentry_sdk.integrations.feature_flags import ( - FeatureFlagsIntegration, - add_feature_flag, -) +from sentry_sdk.integrations.feature_flags import add_feature_flag def test_featureflags_integration(sentry_init, capture_events, uninstall_integration): - uninstall_integration(FeatureFlagsIntegration.identifier) - sentry_init(integrations=[FeatureFlagsIntegration()]) + sentry_init() add_feature_flag("hello", False) add_feature_flag("world", True) @@ -34,8 +30,7 @@ def test_featureflags_integration(sentry_init, capture_events, uninstall_integra def test_featureflags_integration_threaded( sentry_init, capture_events, uninstall_integration ): - uninstall_integration(FeatureFlagsIntegration.identifier) - sentry_init(integrations=[FeatureFlagsIntegration()]) + sentry_init() events = capture_events() # Capture an eval before we split isolation scopes. @@ -86,8 +81,7 @@ def test_featureflags_integration_asyncio( ): asyncio = pytest.importorskip("asyncio") - uninstall_integration(FeatureFlagsIntegration.identifier) - sentry_init(integrations=[FeatureFlagsIntegration()]) + sentry_init() events = capture_events() # Capture an eval before we split isolation scopes. From 2ab47b05f870b62b47a1abee899f2b0d58ba8706 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 13:49:05 +0100 Subject: [PATCH 10/14] Moved add_feature_flag out of integrations --- sentry_sdk/{integrations => }/feature_flags.py | 0 tests/integrations/feature_flags/test_feature_flags.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename sentry_sdk/{integrations => }/feature_flags.py (100%) diff --git a/sentry_sdk/integrations/feature_flags.py b/sentry_sdk/feature_flags.py similarity index 100% rename from sentry_sdk/integrations/feature_flags.py rename to sentry_sdk/feature_flags.py diff --git a/tests/integrations/feature_flags/test_feature_flags.py b/tests/integrations/feature_flags/test_feature_flags.py index bb3c34c410..08290f0a97 100644 --- a/tests/integrations/feature_flags/test_feature_flags.py +++ b/tests/integrations/feature_flags/test_feature_flags.py @@ -4,7 +4,7 @@ import pytest import sentry_sdk -from sentry_sdk.integrations.feature_flags import add_feature_flag +from sentry_sdk.feature_flags import add_feature_flag def test_featureflags_integration(sentry_init, capture_events, uninstall_integration): From 230ac511962d94a74f94ddda6f0686ddb5ab6b53 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 13:59:52 +0100 Subject: [PATCH 11/14] Moved tests out of integrations --- tests/integrations/feature_flags/__init__.py | 0 tests/{integrations/feature_flags => }/test_feature_flags.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/integrations/feature_flags/__init__.py rename tests/{integrations/feature_flags => }/test_feature_flags.py (100%) diff --git a/tests/integrations/feature_flags/__init__.py b/tests/integrations/feature_flags/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/integrations/feature_flags/test_feature_flags.py b/tests/test_feature_flags.py similarity index 100% rename from tests/integrations/feature_flags/test_feature_flags.py rename to tests/test_feature_flags.py From a8e6c4851bf801c71f523931301da1f62f4a2564 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 14:01:50 +0100 Subject: [PATCH 12/14] Refactored flag_utils.py away --- sentry_sdk/feature_flags.py | 31 +++++++++++++++++++++++++++++++ sentry_sdk/flag_utils.py | 31 ------------------------------- 2 files changed, 31 insertions(+), 31 deletions(-) delete mode 100644 sentry_sdk/flag_utils.py diff --git a/sentry_sdk/feature_flags.py b/sentry_sdk/feature_flags.py index 8b693e7a48..1187c2fa12 100644 --- a/sentry_sdk/feature_flags.py +++ b/sentry_sdk/feature_flags.py @@ -1,4 +1,35 @@ import sentry_sdk +from sentry_sdk._lru_cache import LRUCache + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import TypedDict + + FlagData = TypedDict("FlagData", {"flag": str, "result": bool}) + + +DEFAULT_FLAG_CAPACITY = 100 + + +class FlagBuffer: + + def __init__(self, capacity): + # type: (int) -> None + self.buffer = LRUCache(capacity) + self.capacity = capacity + + def clear(self): + # type: () -> None + self.buffer = LRUCache(self.capacity) + + def get(self): + # type: () -> list[FlagData] + return [{"flag": key, "result": value} for key, value in self.buffer.get_all()] + + def set(self, flag, result): + # type: (str, bool) -> None + self.buffer.set(flag, result) def add_feature_flag(flag, result): diff --git a/sentry_sdk/flag_utils.py b/sentry_sdk/flag_utils.py deleted file mode 100644 index 5a6e483e21..0000000000 --- a/sentry_sdk/flag_utils.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import TYPE_CHECKING - -from sentry_sdk._lru_cache import LRUCache - -if TYPE_CHECKING: - from typing import TypedDict - - FlagData = TypedDict("FlagData", {"flag": str, "result": bool}) - - -DEFAULT_FLAG_CAPACITY = 100 - - -class FlagBuffer: - - def __init__(self, capacity): - # type: (int) -> None - self.buffer = LRUCache(capacity) - self.capacity = capacity - - def clear(self): - # type: () -> None - self.buffer = LRUCache(self.capacity) - - def get(self): - # type: () -> list[FlagData] - return [{"flag": key, "result": value} for key, value in self.buffer.get_all()] - - def set(self, flag, result): - # type: (str, bool) -> None - self.buffer.set(flag, result) From 55eb38973cc2c034cd0cc89fa99e1a78f8bb9d73 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 14:02:29 +0100 Subject: [PATCH 13/14] Fixed imports --- sentry_sdk/scope.py | 2 +- tests/test_flag_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 219d863148..ab0f1f4156 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -11,7 +11,7 @@ from sentry_sdk.attachments import Attachment from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES, INSTRUMENTER -from sentry_sdk.flag_utils import FlagBuffer, DEFAULT_FLAG_CAPACITY +from sentry_sdk.feature_flags import FlagBuffer, DEFAULT_FLAG_CAPACITY from sentry_sdk.profiler.continuous_profiler import try_autostart_continuous_profiler from sentry_sdk.profiler.transaction_profiler import Profile from sentry_sdk.session import Session diff --git a/tests/test_flag_utils.py b/tests/test_flag_utils.py index 3fa4f3abfe..024efee68f 100644 --- a/tests/test_flag_utils.py +++ b/tests/test_flag_utils.py @@ -1,4 +1,4 @@ -from sentry_sdk.flag_utils import FlagBuffer +from sentry_sdk.feature_flags import FlagBuffer def test_flag_tracking(): From 718dde6946bddfe852297c8dc985292fa07cfa13 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 13 Jan 2025 14:05:00 +0100 Subject: [PATCH 14/14] Moved tests into one file --- tests/test_feature_flags.py | 44 ++++++++++++++++++++++++++++++++++++- tests/test_flag_utils.py | 43 ------------------------------------ 2 files changed, 43 insertions(+), 44 deletions(-) delete mode 100644 tests/test_flag_utils.py diff --git a/tests/test_feature_flags.py b/tests/test_feature_flags.py index 08290f0a97..14d74cb04b 100644 --- a/tests/test_feature_flags.py +++ b/tests/test_feature_flags.py @@ -4,7 +4,7 @@ import pytest import sentry_sdk -from sentry_sdk.feature_flags import add_feature_flag +from sentry_sdk.feature_flags import add_feature_flag, FlagBuffer def test_featureflags_integration(sentry_init, capture_events, uninstall_integration): @@ -125,3 +125,45 @@ async def runner(): {"flag": "world", "result": False}, ] } + + +def test_flag_tracking(): + """Assert the ring buffer works.""" + buffer = FlagBuffer(capacity=3) + buffer.set("a", True) + flags = buffer.get() + assert len(flags) == 1 + assert flags == [{"flag": "a", "result": True}] + + buffer.set("b", True) + flags = buffer.get() + assert len(flags) == 2 + assert flags == [{"flag": "a", "result": True}, {"flag": "b", "result": True}] + + buffer.set("c", True) + flags = buffer.get() + assert len(flags) == 3 + assert flags == [ + {"flag": "a", "result": True}, + {"flag": "b", "result": True}, + {"flag": "c", "result": True}, + ] + + buffer.set("d", False) + flags = buffer.get() + assert len(flags) == 3 + assert flags == [ + {"flag": "b", "result": True}, + {"flag": "c", "result": True}, + {"flag": "d", "result": False}, + ] + + buffer.set("e", False) + buffer.set("f", False) + flags = buffer.get() + assert len(flags) == 3 + assert flags == [ + {"flag": "d", "result": False}, + {"flag": "e", "result": False}, + {"flag": "f", "result": False}, + ] diff --git a/tests/test_flag_utils.py b/tests/test_flag_utils.py deleted file mode 100644 index 024efee68f..0000000000 --- a/tests/test_flag_utils.py +++ /dev/null @@ -1,43 +0,0 @@ -from sentry_sdk.feature_flags import FlagBuffer - - -def test_flag_tracking(): - """Assert the ring buffer works.""" - buffer = FlagBuffer(capacity=3) - buffer.set("a", True) - flags = buffer.get() - assert len(flags) == 1 - assert flags == [{"flag": "a", "result": True}] - - buffer.set("b", True) - flags = buffer.get() - assert len(flags) == 2 - assert flags == [{"flag": "a", "result": True}, {"flag": "b", "result": True}] - - buffer.set("c", True) - flags = buffer.get() - assert len(flags) == 3 - assert flags == [ - {"flag": "a", "result": True}, - {"flag": "b", "result": True}, - {"flag": "c", "result": True}, - ] - - buffer.set("d", False) - flags = buffer.get() - assert len(flags) == 3 - assert flags == [ - {"flag": "b", "result": True}, - {"flag": "c", "result": True}, - {"flag": "d", "result": False}, - ] - - buffer.set("e", False) - buffer.set("f", False) - flags = buffer.get() - assert len(flags) == 3 - assert flags == [ - {"flag": "d", "result": False}, - {"flag": "e", "result": False}, - {"flag": "f", "result": False}, - ]