Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions switcher_client/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional, Callable

from switcher_client.lib.bypasser import Bypasser, Key
from switcher_client.lib.globals.global_auth import GlobalAuth
from switcher_client.lib.globals.global_snapshot import GlobalSnapshot, LoadSnapshotOptions
from switcher_client.lib.globals.global_context import Context, ContextOptions, DEFAULT_ENVIRONMENT
Expand Down Expand Up @@ -249,6 +250,16 @@ def subscribe_notify_error(callback: Callable[[Exception], None]) -> None:
"""
ExecutionLogger.subscribe_notify_error(callback)

@staticmethod
def assume(key: str) -> Key:
""" Force a switcher value to return a given value by calling one of both methods - true() false() """
return Bypasser.assume(key)

@staticmethod
def forget(key: str) -> None:
""" Remove forced value from a switcher """
Bypasser.forget(key)

@staticmethod
def _is_check_snapshot_available(fetch_remote = False) -> bool:
return Client.snapshot_version() == 0 and (fetch_remote or not Client._context.options.local)
Expand Down
7 changes: 7 additions & 0 deletions switcher_client/lib/bypasser/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from switcher_client.lib.bypasser.bypasser import Bypasser
from switcher_client.lib.bypasser.key import Key

__all__ = [
'Bypasser',
'Key',
]
33 changes: 33 additions & 0 deletions switcher_client/lib/bypasser/bypasser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from switcher_client.lib.bypasser.key import Key

class Bypasser:
"""
Bypasser allows to force a switcher value to return a given value by calling one of both methods - true() false()
"""

_bypassed_keys = []

@staticmethod
def assume(key: str) -> Key:
# Remove previous forced value if exists to avoid conflicts
Bypasser.forget(key)

new_key = Key(key)
Bypasser._bypassed_keys.append(new_key)
return new_key

@staticmethod
def forget(key: str) -> None:
""" Remove forced value from a switcher """
key_stored = Bypasser.search_key(key)
if key_stored is not None:
Bypasser._bypassed_keys.remove(key_stored)

@staticmethod
def search_key(key: str) -> Key | None:
""" Search for key registered via 'assume' """
for bypassed_key in Bypasser._bypassed_keys:
if bypassed_key.key == key:
return bypassed_key

return None
20 changes: 20 additions & 0 deletions switcher_client/lib/bypasser/key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from switcher_client.lib.types import ResultDetail

class Key:
""" Key record used to store key response when bypassing criteria execution """

def __init__(self, key: str):
self._key = key
self._result = None

def true(self):
""" Force a switcher value to return true """
self._result = True

def get_response(self, input_list: list[str]) -> ResultDetail:
return ResultDetail.create(result=True, reason=f"Forced to '{self._result}' - input: {input_list}")

@property
def key(self):
""" Get the key of the switcher """
return self._key
6 changes: 6 additions & 0 deletions switcher_client/switcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime
from typing import Optional

from switcher_client.lib.bypasser import Bypasser
from switcher_client.lib.globals.global_context import Context
from switcher_client.lib.globals.global_snapshot import GlobalSnapshot
from switcher_client.lib.remote_auth import RemoteAuth
Expand Down Expand Up @@ -51,6 +52,11 @@ def is_on(self, key: Optional[str] = None) -> bool:
self._show_details = False
self._validate_args(key, details=False)

# verify if query from Bypasser
bypass_key = Bypasser.search_key(self._key)
if bypass_key is not None:
return bypass_key.get_response(self._input).result

# try get cached result
cached_result = self._try_cached_result()
if cached_result is not None:
Expand Down
28 changes: 28 additions & 0 deletions tests/test_switcher_stub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from tests.test_switcher_integration import given_context

from switcher_client.client import Client, ContextOptions

context_options_local = ContextOptions(snapshot_location='tests/snapshots', local=True, logger=True)

class TestSwitcherStub:
""" Test suite for SwitcherStub (Bypasser) - Client.assume() """

key = 'FF2FOR2020'

def setup_method(self):
given_context(options=context_options_local)
Client.load_snapshot()

def teardown_method(self):
Client.forget(self.key)

def test_switcher_stub_result(self):
""" Should bypass Switcher evaluation and return the stubbed result """

# given
switcher = Client.get_switcher(self.key)

# test
Client.assume(self.key).true()

assert switcher.is_on()