def test_flag_returns_off_variation_and_event_if_prerequisite_is_off(): store = InMemoryFeatureStore() flag = { 'key': 'feature0', 'on': True, 'prerequisites': [{'key': 'feature1', 'variation': 1}], 'fallthrough': { 'variation': 0 }, 'offVariation': 1, 'variations': ['a', 'b', 'c'], 'version': 1 } flag1 = { 'key': 'feature1', 'off': False, 'offVariation': 1, # note that even though it returns the desired variation, it is still off and therefore not a match 'fallthrough': { 'variation': 0 }, 'variations': ['d', 'e'], 'version': 2, 'trackEvents': False } store.upsert(FEATURES, flag1) user = { 'key': 'x' } detail = EvaluationDetail('b', 1, {'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': 'feature1'}) events_should_be = [{'kind': 'feature', 'key': 'feature1', 'variation': 1, 'value': 'e', 'version': 2, 'user': user, 'prereqOf': 'feature0', 'trackEvents': False, 'debugEventsUntilDate': None, 'reason': None}] assert evaluate(flag, user, store) == EvalResult(detail, events_should_be)
def setup_ld_client(app): # define and set required env vars redis_prefix = app.config["LD_FRONTEND_KEY"] + "-featurestore" redis_conn = "redis://" + app.config["REDIS_HOST"] + ":6379" if os.environ.get("TESTING") is None or os.environ.get("TESTING") == False: store = Redis.new_feature_store(url=redis_conn, prefix=redis_prefix, caching=CacheConfig.disabled()) elif os.environ.get("FLASK_ENV") == "default": store = InMemoryFeatureStore() else: store = InMemoryFeatureStore() LD_CLIENT_KEY = app.config["LD_CLIENT_KEY"] LD_FRONTEND_KEY = app.config["LD_FRONTEND_KEY"] ld_config = LdConfig(sdk_key=LD_CLIENT_KEY, connect_timeout=30, read_timeout=30, feature_store=store, inline_users_in_events=True) # LaunchDarkly Config # If $LD_RELAY_URL is set, client will be pointed to a relay instance. if "LD_RELAY_URL" in os.environ: base_uri = os.environ.get("LD_RELAY_URL") ld_config = LdConfig( sdk_key=app.config["LD_CLIENT_KEY"], base_uri=base_uri, events_uri=os.environ.get("LD_RELAY_EVENTS_URL", base_uri), stream_uri=os.environ.get("LD_RELAY_STREAM_URL", base_uri), ) new_client = ldclient.LDClient(config=ld_config) return new_client
def test_flag_returns_fallthrough_and_event_if_prereq_is_met_and_there_are_no_rules(): store = InMemoryFeatureStore() flag = { 'key': 'feature0', 'on': True, 'prerequisites': [{ 'key': 'feature1', 'variation': 1 }], 'fallthrough': { 'variation': 0 }, 'offVariation': 1, 'variations': ['a', 'b', 'c'], 'version': 1 } flag1 = { 'key': 'feature1', 'on': True, 'fallthrough': { 'variation': 1 }, 'variations': ['d', 'e'], 'version': 2, 'trackEvents': False } store.upsert(FEATURES, flag1) user = { 'key': 'x' } detail = EvaluationDetail('a', 0, {'kind': 'FALLTHROUGH'}) events_should_be = [{'kind': 'feature', 'key': 'feature1', 'variation': 1, 'value': 'e', 'version': 2, 'user': user, 'prereqOf': 'feature0', 'trackEvents': False, 'debugEventsUntilDate': None, 'reason': None}] assert evaluate(flag, user, store) == EvalResult(detail, events_should_be)
def test_flag_returns_fallthrough_and_event_if_prereq_is_met_and_there_are_no_rules(): store = InMemoryFeatureStore() flag = { 'key': 'feature0', 'on': True, 'prerequisites': [{ 'key': 'feature1', 'variation': 1 }], 'fallthrough': { 'variation': 0 }, 'offVariation': 1, 'variations': ['a', 'b', 'c'], 'version': 1 } flag1 = { 'key': 'feature1', 'on': True, 'fallthrough': { 'variation': 1 }, 'variations': ['d', 'e'], 'version': 2, 'trackEvents': False } store.upsert(FEATURES, flag1) user = { 'key': 'x' } detail = EvaluationDetail('a', 0, {'kind': 'FALLTHROUGH'}) events_should_be = [{'kind': 'feature', 'key': 'feature1', 'variation': 1, 'value': 'e', 'default': None, 'version': 2, 'user': user, 'prereqOf': 'feature0'}] assert evaluate(flag, user, store, event_factory) == EvalResult(detail, events_should_be)
def test_event_for_existing_feature_with_tracked_rule(): feature = { 'key': 'feature.key', 'version': 100, 'salt': u'', 'on': True, 'rules': [ { 'clauses': [ { 'attribute': 'key', 'op': 'in', 'values': [ user['key'] ] } ], 'variation': 0, 'trackEvents': True, 'id': 'rule_id' } ], 'variations': [ 'value' ] } store = InMemoryFeatureStore() store.init({FEATURES: {feature['key']: feature}}) client = make_client(store) assert 'value' == client.variation(feature['key'], user, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == feature['key'] and e['user'] == user and e['version'] == feature['version'] and e['value'] == 'value' and e['variation'] == 0 and e['reason'] == { 'kind': 'RULE_MATCH', 'ruleIndex': 0, 'ruleId': 'rule_id' } and e['default'] == 'default' and e['trackEvents'] == True and e.get('debugEventsUntilDate') is None)
def test_variation_detail_when_user_has_no_key(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) expected = EvaluationDetail('default', None, {'kind': 'ERROR', 'errorKind': 'USER_NOT_SPECIFIED'}) assert expected == client.variation_detail('feature.key', { }, default='default')
def test_segment_match_clause_retrieves_segment_from_store(): store = InMemoryFeatureStore() segment = { "key": "segkey", "included": [ "foo" ], "version": 1 } store.upsert(SEGMENTS, segment) user = { "key": "foo" } flag = { "key": "test", "variations": [ False, True ], "fallthrough": { "variation": 0 }, "on": True, "rules": [ { "clauses": [ { "attribute": "", "op": "segmentMatch", "values": [ "segkey" ] } ], "variation": 1 } ] } assert evaluate(flag, user, store, event_factory).detail.value == True
def test_all_flags_state_returns_state_with_reasons(): store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2 } }) client = make_client(store) state = client.all_flags_state(user, with_reasons=True) assert state.valid == True result = state.to_json_dict() assert result == { 'key1': 'value1', 'key2': 'value2', '$flagsState': { 'key1': { 'variation': 0, 'version': 100, 'reason': {'kind': 'OFF'} }, 'key2': { 'variation': 1, 'version': 200, 'trackEvents': True, 'debugEventsUntilDate': 1000, 'reason': {'kind': 'OFF'} } }, '$valid': True }
def test_variation_detail_for_existing_feature(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) expected = EvaluationDetail('value', 0, {'kind': 'OFF'}) assert expected == client.variation_detail('feature.key', user, default='default')
def test_all_flags_state_returns_state_with_reasons(): store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2 } }) client = make_client(store) state = client.all_flags_state(user, with_reasons=True) assert state.valid == True result = state.to_json_dict() assert result == { 'key1': 'value1', 'key2': 'value2', '$flagsState': { 'key1': { 'variation': 0, 'version': 100, 'reason': {'kind': 'OFF'} }, 'key2': { 'variation': 1, 'version': 200, 'trackEvents': True, 'debugEventsUntilDate': 1000, 'reason': {'kind': 'OFF'} } }, '$valid': True }
def test_segment_match_clause_retrieves_segment_from_store(): store = InMemoryFeatureStore() segment = { "key": "segkey", "included": [ "foo" ], "version": 1 } store.upsert(SEGMENTS, segment) user = { "key": "foo" } flag = { "key": "test", "variations": [ False, True ], "fallthrough": { "variation": 0 }, "on": True, "rules": [ { "clauses": [ { "attribute": "", "op": "segmentMatch", "values": [ "segkey" ] } ], "variation": 1 } ] } assert evaluate(flag, user, store).detail.value == True
def test_event_for_existing_feature_with_untracked_fallthrough(): feature = { 'key': 'feature.key', 'version': 100, 'salt': u'', 'on': True, 'rules': [], 'fallthrough': { 'variation': 0 }, 'variations': [ 'value' ], 'trackEventsFallthrough': False } store = InMemoryFeatureStore() store.init({FEATURES: {feature['key']: feature}}) client = make_client(store) assert 'value' == client.variation(feature['key'], user, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == feature['key'] and e['user'] == user and e['version'] == feature['version'] and e['value'] == 'value' and e['variation'] == 0 and e.get('reason') is None and e['default'] == 'default' and e.get('trackEvents', False) == False and e.get('debugEventsUntilDate') is None)
def test_variation_when_user_has_no_key(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) expected = EvaluationDetail('default', None, {'kind': 'ERROR', 'errorKind': 'USER_NOT_SPECIFIED'}) assert expected == client.variation_detail('feature.key', { }, default='default')
def test_flag_returns_off_variation_and_event_if_prerequisite_is_off(): store = InMemoryFeatureStore() flag = { 'key': 'feature0', 'on': True, 'prerequisites': [{'key': 'feature1', 'variation': 1}], 'fallthrough': { 'variation': 0 }, 'offVariation': 1, 'variations': ['a', 'b', 'c'], 'version': 1 } flag1 = { 'key': 'feature1', 'off': False, 'offVariation': 1, # note that even though it returns the desired variation, it is still off and therefore not a match 'fallthrough': { 'variation': 0 }, 'variations': ['d', 'e'], 'version': 2, 'trackEvents': False } store.upsert(FEATURES, flag1) user = { 'key': 'x' } detail = EvaluationDetail('b', 1, {'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': 'feature1'}) events_should_be = [{'kind': 'feature', 'key': 'feature1', 'variation': 1, 'value': 'e', 'default': None, 'version': 2, 'user': user, 'prereqOf': 'feature0'}] assert evaluate(flag, user, store, event_factory) == EvalResult(detail, events_should_be)
def test_variation_detail_for_existing_feature(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) expected = EvaluationDetail('value', 0, {'kind': 'OFF'}) assert expected == client.variation_detail('feature.key', user, default='default')
def __init__(self, sdk_key=None, base_uri='https://app.launchdarkly.com', events_uri='https://events.launchdarkly.com', connect_timeout=10, read_timeout=15, events_upload_max_batch_size=100, events_max_pending=10000, stream_uri='https://stream.launchdarkly.com', stream=True, verify_ssl=True, defaults=None, events_enabled=True, update_processor_class=None, poll_interval=1, use_ldd=False, feature_store=InMemoryFeatureStore(), feature_requester_class=None, event_consumer_class=None, offline=False): """ :param update_processor_class: A factory for an UpdateProcessor implementation taking the sdk key, config, and FeatureStore implementation :type update_processor_class: (str, Config, FeatureStore) -> UpdateProcessor :param feature_store: A FeatureStore implementation :type feature_store: FeatureStore :param feature_requester_class: A factory for a FeatureRequester implementation taking the sdk key and config :type feature_requester_class: (str, Config, FeatureStore) -> FeatureRequester :param event_consumer_class: A factory for an EventConsumer implementation taking the event queue, sdk key, and config :type event_consumer_class: (queue.Queue, str, Config) -> EventConsumer """ self.__sdk_key = sdk_key if defaults is None: defaults = {} self.__base_uri = base_uri.rstrip('\\') self.__events_uri = events_uri.rstrip('\\') self.__stream_uri = stream_uri.rstrip('\\') self.__update_processor_class = update_processor_class self.__stream = stream if poll_interval < 1: poll_interval = 1 self.__poll_interval = poll_interval self.__use_ldd = use_ldd self.__feature_store = InMemoryFeatureStore() if not feature_store else feature_store self.__event_consumer_class = EventConsumerImpl if not event_consumer_class else event_consumer_class self.__feature_requester_class = feature_requester_class self.__connect_timeout = connect_timeout self.__read_timeout = read_timeout self.__events_upload_max_batch_size = events_upload_max_batch_size self.__events_max_pending = events_max_pending self.__verify_ssl = verify_ssl self.__defaults = defaults if offline is True: events_enabled = False self.__events_enabled = events_enabled self.__offline = offline
def test_variation_when_user_is_none(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) assert 'default' == client.variation('feature.key', None, default='default')
def test_variation_for_flag_that_evaluates_to_none(): empty_flag = {'key': 'feature.key', 'on': False, 'offVariation': None} store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': empty_flag}}) client = make_client(store) assert 'default' == client.variation('feature.key', user, default='default')
def test_variation_detail_for_flag_that_evaluates_to_none(): empty_flag = {'key': 'feature.key', 'on': False, 'offVariation': None} store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': empty_flag}}) client = make_client(store) expected = EvaluationDetail('default', None, {'kind': 'OFF'}) actual = client.variation_detail('feature.key', user, default='default') assert expected == actual assert actual.is_default_value() == True
def test_variation_for_flag_that_evaluates_to_none(): empty_flag = { 'key': 'feature.key', 'on': False, 'offVariation': None } store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': empty_flag}}) client = make_client(store) assert 'default' == client.variation('feature.key', user, default='default')
def test_all_flags_state_can_omit_details_for_untracked_flags(): future_time = (time.time() * 1000) + 100000 flag1 = { 'key': 'key1', 'version': 100, 'on': False, 'offVariation': 0, 'variations': [ 'value1' ], 'trackEvents': False } flag2 = { 'key': 'key2', 'version': 200, 'on': False, 'offVariation': 1, 'variations': [ 'x', 'value2' ], 'trackEvents': True } flag3 = { 'key': 'key3', 'version': 300, 'on': False, 'offVariation': 1, 'variations': [ 'x', 'value3' ], 'debugEventsUntilDate': future_time } store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2, 'key3': flag3 } }) client = make_client(store) state = client.all_flags_state(user, with_reasons=True, details_only_for_tracked_flags=True) assert state.valid == True result = state.to_json_dict() assert result == { 'key1': 'value1', 'key2': 'value2', 'key3': 'value3', '$flagsState': { 'key1': { 'variation': 0 }, 'key2': { 'variation': 1, 'version': 200, 'trackEvents': True, 'reason': {'kind': 'OFF'} }, 'key3': { 'variation': 1, 'version': 300, 'debugEventsUntilDate': future_time, 'reason': {'kind': 'OFF'} } }, '$valid': True }
def test_all_flags_state_can_omit_details_for_untracked_flags(): future_time = (time.time() * 1000) + 100000 flag1 = { 'key': 'key1', 'version': 100, 'on': False, 'offVariation': 0, 'variations': [ 'value1' ], 'trackEvents': False } flag2 = { 'key': 'key2', 'version': 200, 'on': False, 'offVariation': 1, 'variations': [ 'x', 'value2' ], 'trackEvents': True } flag3 = { 'key': 'key3', 'version': 300, 'on': False, 'offVariation': 1, 'variations': [ 'x', 'value3' ], 'debugEventsUntilDate': future_time } store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2, 'key3': flag3 } }) client = make_client(store) state = client.all_flags_state(user, with_reasons=True, details_only_for_tracked_flags=True) assert state.valid == True result = state.to_json_dict() assert result == { 'key1': 'value1', 'key2': 'value2', 'key3': 'value3', '$flagsState': { 'key1': { 'variation': 0 }, 'key2': { 'variation': 1, 'version': 200, 'trackEvents': True, 'reason': {'kind': 'OFF'} }, 'key3': { 'variation': 1, 'version': 300, 'debugEventsUntilDate': future_time, 'reason': {'kind': 'OFF'} } }, '$valid': True }
def test_event_for_unknown_feature(): store = InMemoryFeatureStore() store.init({FEATURES: {}}) client = make_client(store) assert 'default' == client.variation('feature.key', user, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == 'feature.key' and e['user'] == user and e['value'] == 'default' and e['variation'] == None and e['default'] == 'default')
def test_event_for_unknown_feature(): store = InMemoryFeatureStore() store.init({FEATURES: {}}) with make_client(store) as client: assert 'default' == client.variation('feature.key', user, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == 'feature.key' and e['user'] == user and e['value'] == 'default' and e['variation'] == None and e['default'] == 'default')
def test_variation_detail_for_flag_that_evaluates_to_none(): empty_flag = { 'key': 'feature.key', 'on': False, 'offVariation': None } store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': empty_flag}}) client = make_client(store) expected = EvaluationDetail('default', None, {'kind': 'OFF'}) actual = client.variation_detail('feature.key', user, default='default') assert expected == actual assert actual.is_default_value() == True
def test_event_for_existing_feature(): feature = make_off_flag_with_value('feature.key', 'value') feature['trackEvents'] = True feature['debugEventsUntilDate'] = 1000 store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) assert 'value' == client.variation('feature.key', user, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == 'feature.key' and e['user'] == user and e['version'] == feature['version'] and e['value'] == 'value' and e['variation'] == 0 and e.get('reason') is None and e['default'] == 'default' and e['trackEvents'] == True and e['debugEventsUntilDate'] == 1000)
def test_receives_patch_events(): store = InMemoryFeatureStore() ready = Event() flagv1 = {'key': 'flagkey', 'version': 1} flagv2 = {'key': 'flagkey', 'version': 2} segmentv1 = {'key': 'segkey', 'version': 1} segmentv2 = {'key': 'segkey', 'version': 1} with start_server() as server: with stream_content(make_put_event([flagv1], [segmentv1])) as stream: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.for_path('/all', stream) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() ready.wait(start_wait) assert sp.initialized() expect_item(store, FEATURES, flagv1) expect_item(store, SEGMENTS, segmentv1) stream.push(make_patch_event(FEATURES, flagv2)) expect_update(store, FEATURES, flagv2) stream.push(make_patch_event(SEGMENTS, segmentv2)) expect_update(store, SEGMENTS, segmentv2)
def test_reconnects_if_stream_is_broken(): store = InMemoryFeatureStore() ready = Event() flagv1 = {'key': 'flagkey', 'version': 1} flagv2 = {'key': 'flagkey', 'version': 2} with start_server() as server: with stream_content(make_put_event([flagv1])) as stream1: with stream_content(make_put_event([flagv2])) as stream2: config = Config(sdk_key='sdk-key', stream_uri=server.uri, initial_reconnect_delay=brief_delay) server.for_path('/all', SequentialHandler(stream1, stream2)) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() server.await_request ready.wait(start_wait) assert sp.initialized() expect_item(store, FEATURES, flagv1) stream1.close() server.await_request expect_update(store, FEATURES, flagv2)
def test_event_for_existing_feature_with_no_user_key(): feature = make_off_flag_with_value('feature.key', 'value') feature['trackEvents'] = True feature['debugEventsUntilDate'] = 1000 store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) bad_user = {u'name': u'Bob'} assert 'default' == client.variation('feature.key', bad_user, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == 'feature.key' and e['user'] == bad_user and e['version'] == feature['version'] and e['value'] == 'default' and e['variation'] == None and e['default'] == 'default' and e['trackEvents'] == True and e['debugEventsUntilDate'] == 1000)
def make_client(store = InMemoryFeatureStore()): return LDClient(config=Config(sdk_key = 'SDK_KEY', base_uri=unreachable_uri, events_uri=unreachable_uri, stream_uri=unreachable_uri, event_processor_class=MockEventProcessor, update_processor_class=MockUpdateProcessor, feature_store=store))
def test_all_flags_state_can_be_filtered_for_client_side_flags(): flag1 = { 'key': 'server-side-1', 'on': False, 'offVariation': 0, 'variations': ['a'], 'clientSide': False } flag2 = { 'key': 'server-side-2', 'on': False, 'offVariation': 0, 'variations': ['b'], 'clientSide': False } flag3 = { 'key': 'client-side-1', 'on': False, 'offVariation': 0, 'variations': ['value1'], 'clientSide': True } flag4 = { 'key': 'client-side-2', 'on': False, 'offVariation': 0, 'variations': ['value2'], 'clientSide': True } store = InMemoryFeatureStore() store.init({ FEATURES: { flag1['key']: flag1, flag2['key']: flag2, flag3['key']: flag3, flag4['key']: flag4 } }) client = make_client(store) state = client.all_flags_state(user, client_side_only=True) assert state.valid values = state.to_values_map() assert values == {'client-side-1': 'value1', 'client-side-2': 'value2'}
def test_flag_returns_off_variation_and_event_if_prerequisite_is_not_met(): store = InMemoryFeatureStore() flag = { 'key': 'feature0', 'on': True, 'prerequisites': [{ 'key': 'feature1', 'variation': 1 }], 'fallthrough': { 'variation': 0 }, 'offVariation': 1, 'variations': ['a', 'b', 'c'], 'version': 1 } flag1 = { 'key': 'feature1', 'on': True, 'fallthrough': { 'variation': 0 }, 'variations': ['d', 'e'], 'version': 2, 'trackEvents': False } store.upsert(FEATURES, flag1) user = {'key': 'x'} detail = EvaluationDetail('b', 1, { 'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': 'feature1' }) events_should_be = [{ 'kind': 'feature', 'key': 'feature1', 'variation': 0, 'value': 'd', 'version': 2, 'user': user, 'prereqOf': 'feature0', 'trackEvents': False, 'debugEventsUntilDate': None, 'reason': None }] assert evaluate(flag, user, store) == EvalResult(detail, events_should_be)
def test_event_for_existing_feature_with_no_user(): feature = make_off_flag_with_value('feature.key', 'value') feature['trackEvents'] = True feature['debugEventsUntilDate'] = 1000 store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) with make_client(store) as client: assert 'default' == client.variation('feature.key', None, default='default') e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == 'feature.key' and e['user'] == None and e['version'] == feature['version'] and e['value'] == 'default' and e['variation'] == None and e['default'] == 'default' and e['trackEvents'] == True and e['debugEventsUntilDate'] == 1000)
def test_variation_detail_for_unknown_feature(): store = InMemoryFeatureStore() client = make_client(store) expected = EvaluationDetail('default', None, { 'kind': 'ERROR', 'errorKind': 'FLAG_NOT_FOUND' }) assert expected == client.variation_detail('feature.key', user, default='default')
def test_defaults_and_online(): expected = "bar" my_client = LDClient(config=Config(base_uri="http://localhost:3000", defaults={"foo": expected}, event_consumer_class=MockConsumer, feature_requester_class=MockFeatureRequester, feature_store=InMemoryFeatureStore())) actual = my_client.variation('foo', user, default="originalDefault") assert actual == expected assert wait_for_event(my_client, lambda e: e['kind'] == 'feature' and e['key'] == u'foo' and e['user'] == user)
def test_event_for_existing_feature_with_reason(): feature = make_off_flag_with_value('feature.key', 'value') feature['trackEvents'] = True feature['debugEventsUntilDate'] = 1000 store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) with make_client(store) as client: assert 'value' == client.variation_detail('feature.key', user, default='default').value e = get_first_event(client) assert (e['kind'] == 'feature' and e['key'] == 'feature.key' and e['user'] == user and e['version'] == feature['version'] and e['value'] == 'value' and e['variation'] == 0 and e['reason'] == {'kind': 'OFF'} and e['default'] == 'default' and e['trackEvents'] == True and e['debugEventsUntilDate'] == 1000)
def _verify_https_proxy_is_used(server, config): store = InMemoryFeatureStore() ready = Event() with stream_content(make_put_event()) as stream: server.for_path(config.stream_base_uri + '/all', stream) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() # Our simple stub server implementation can't really do HTTPS proxying, so the request will fail, but # it can still record that it *got* the request, which proves that the request went to the proxy. req = server.await_request() assert req.method == 'CONNECT'
def test_defaults_and_online(): expected = "bar" my_client = LDClient( config=Config(base_uri="http://localhost:3000", defaults={"foo": expected}, event_processor_class=MockEventProcessor, update_processor_class=MockUpdateProcessor, feature_store=InMemoryFeatureStore())) actual = my_client.variation('foo', user, default="originalDefault") assert actual == expected e = get_first_event(my_client) assert e['kind'] == 'feature' and e['key'] == u'foo' and e['user'] == user
def test_all_flags_state_can_be_filtered_for_client_side_flags(): flag1 = { 'key': 'server-side-1', 'on': False, 'offVariation': 0, 'variations': [ 'a' ], 'clientSide': False } flag2 = { 'key': 'server-side-2', 'on': False, 'offVariation': 0, 'variations': [ 'b' ], 'clientSide': False } flag3 = { 'key': 'client-side-1', 'on': False, 'offVariation': 0, 'variations': [ 'value1' ], 'clientSide': True } flag4 = { 'key': 'client-side-2', 'on': False, 'offVariation': 0, 'variations': [ 'value2' ], 'clientSide': True } store = InMemoryFeatureStore() store.init({ FEATURES: { flag1['key']: flag1, flag2['key']: flag2, flag3['key']: flag3, flag4['key']: flag4 } }) client = make_client(store) state = client.all_flags_state(user, client_side_only=True) assert state.valid == True values = state.to_values_map() assert values == { 'client-side-1': 'value1', 'client-side-2': 'value2' }
def test_sends_wrapper_header_without_version(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri, wrapper_name='Flask') server.setup_response('/all', 200, fake_event, response_headers) with StreamingUpdateProcessor(config, None, store, ready, None) as sp: sp.start() req = server.await_request() assert req.headers.get('X-LaunchDarkly-Wrapper') == 'Flask'
def test_uses_stream_uri(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.setup_response('/all', 200, fake_event, response_headers) with StreamingUpdateProcessor(config, None, store, ready, None) as sp: sp.start() req = server.await_request() assert req.method == 'GET' ready.wait(1) assert sp.initialized()
def test_sends_headers(): store = InMemoryFeatureStore() ready = Event() with start_server() as server: config = Config(sdk_key='sdk-key', stream_uri=server.uri) server.setup_response('/all', 200, fake_event, response_headers) with StreamingUpdateProcessor(config, None, store, ready, None) as sp: sp.start() req = server.await_request() assert req.headers.get('Authorization') == 'sdk-key' assert req.headers.get('User-Agent') == 'PythonClient/' + VERSION assert req.headers.get('X-LaunchDarkly-Wrapper') is None
def _verify_http_proxy_is_used(server, config): store = InMemoryFeatureStore() ready = Event() with stream_content(make_put_event()) as stream: server.for_path(config.stream_base_uri + '/all', stream) with StreamingUpdateProcessor(config, store, ready, None) as sp: sp.start() # For an insecure proxy request, our stub server behaves enough like the real thing to satisfy the # HTTP client, so we should be able to see the request go through. Note that the URI path will # actually be an absolute URI for a proxy request. req = server.await_request() assert req.method == 'GET' ready.wait(start_wait) assert sp.initialized()
def test_all_flags_returns_values(): store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2 } }) client = make_client(store) result = client.all_flags(user) assert result == { 'key1': 'value1', 'key2': 'value2' }
def test_all_flags_returns_none_if_user_has_no_key(): store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2 } }) client = make_client(store) result = client.all_flags({ }) assert result is None
def test_all_flags_state_returns_empty_state_if_user_has_no_key(): store = InMemoryFeatureStore() store.init({ FEATURES: { 'key1': flag1, 'key2': flag2 } }) client = make_client(store) state = client.all_flags_state({ }) assert state.valid == False
def test_variation_for_existing_feature(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) assert 'value' == client.variation('feature.key', user, default='default')
def test_variation_when_user_has_no_key(): feature = make_off_flag_with_value('feature.key', 'value') store = InMemoryFeatureStore() store.init({FEATURES: {'feature.key': feature}}) client = make_client(store) assert 'default' == client.variation('feature.key', { }, default='default')