コード例 #1
0
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)
コード例 #2
0
ファイル: factory.py プロジェクト: BrianJayChung/SS_deploy
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
コード例 #3
0
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)
コード例 #4
0
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)
コード例 #5
0
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)
コード例 #6
0
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')
コード例 #7
0
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
コード例 #8
0
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
    }
コード例 #9
0
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')
コード例 #10
0
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
    }
コード例 #11
0
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
コード例 #12
0
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)
コード例 #13
0
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')
コード例 #14
0
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)
コード例 #15
0
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')
コード例 #16
0
    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
コード例 #17
0
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')
コード例 #18
0
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')
コード例 #19
0
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
コード例 #20
0
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')
コード例 #21
0
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
    }
コード例 #22
0
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
    }
コード例 #23
0
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')
コード例 #24
0
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')
コード例 #25
0
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
コード例 #26
0
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)
コード例 #27
0
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)
コード例 #28
0
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)
コード例 #29
0
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)
コード例 #30
0
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))
コード例 #31
0
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'}
コード例 #32
0
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)
コード例 #33
0
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)
コード例 #34
0
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')
コード例 #35
0
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)
コード例 #36
0
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)
コード例 #37
0
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'
コード例 #38
0
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
コード例 #39
0
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' }
コード例 #40
0
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'
コード例 #41
0
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()
コード例 #42
0
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
コード例 #43
0
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()
コード例 #44
0
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' }
コード例 #45
0
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
コード例 #46
0
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
コード例 #47
0
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')
コード例 #48
0
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')