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_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_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)
Exemple #4
0
def test_segment_match_clause_falls_through_with_no_errors_if_segment_not_found(
):
    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, empty_store,
                    event_factory).detail.value == False
def test_flag_returns_error_if_rule_has_rollout_with_no_variations():
    rule = { 'id': 'id', 'clauses': [{'attribute': 'key', 'op': 'in', 'values': ['userkey']}],
        'rollout': {'variations': []} }
    flag = make_boolean_flag_with_rules([rule])
    user = { 'key': 'userkey' }
    detail = EvaluationDetail(None, None, {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'})
    assert evaluate(flag, user, empty_store, event_factory) == EvalResult(detail, [])
def test_flag_returns_error_if_rule_has_rollout_with_no_variations():
    rule = { 'id': 'id', 'clauses': [{'attribute': 'key', 'op': 'in', 'values': ['userkey']}],
        'rollout': {'variations': []} }
    flag = make_boolean_flag_with_rules([rule])
    user = { 'key': 'userkey' }
    detail = EvaluationDetail(None, None, {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
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_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_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_clause_returns_false_for_missing_attribute():
    clause = {
        'attribute': 'legs',
        'op': 'in',
        'values': [ 4 ]
    }
    user = { 'key': 'x', 'name': 'Bob' }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store).detail.value == False
def test_clause_matches_custom_attribute():
    clause = {
        'attribute': 'legs',
        'op': 'in',
        'values': [ 4 ]
    }
    user = { 'key': 'x', 'name': 'Bob', 'custom': { 'legs': 4 } }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store).detail.value == True
def test_flag_returns_none_if_flag_is_off_and_off_variation_is_unspecified():
    flag = {
        'key': 'feature',
        'on': False,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail(None, None, {'kind': 'OFF'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_clause_matches_builtin_attribute():
    clause = {
        'attribute': 'name',
        'op': 'in',
        'values': [ 'Bob' ]
    }
    user = { 'key': 'x', 'name': 'Bob' }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store, event_factory).detail.value == True
def test_flag_returns_none_if_flag_is_off_and_off_variation_is_unspecified():
    flag = {
        'key': 'feature',
        'on': False,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail(None, None, {'kind': 'OFF'})
    assert evaluate(flag, user, empty_store, event_factory) == EvalResult(detail, [])
def test_clause_matches_custom_attribute():
    clause = {
        'attribute': 'legs',
        'op': 'in',
        'values': [ 4 ]
    }
    user = { 'key': 'x', 'name': 'Bob', 'custom': { 'legs': 4 } }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store, event_factory).detail.value == True
def test_clause_returns_false_for_missing_attribute():
    clause = {
        'attribute': 'legs',
        'op': 'in',
        'values': [ 4 ]
    }
    user = { 'key': 'x', 'name': 'Bob' }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store, event_factory).detail.value == False
def test_clause_matches_builtin_attribute():
    clause = {
        'attribute': 'name',
        'op': 'in',
        'values': [ 'Bob' ]
    }
    user = { 'key': 'x', 'name': 'Bob' }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store).detail.value == True
Exemple #18
0
    def _evaluate_internal(self, key, user, default, include_reasons_in_events):
        default = self._config.get_default(key, default)

        if self._config.offline:
            return EvaluationDetail(default, None, error_reason('CLIENT_NOT_READY'))
        
        if user is not None:
            self._sanitize_user(user)

        def send_event(value, variation=None, flag=None, reason=None):
            self._send_event({'kind': 'feature', 'key': key, 'user': user,
                              'value': value, 'variation': variation, 'default': default,
                              'version': flag.get('version') if flag else None,
                              'trackEvents': flag.get('trackEvents') if flag else None,
                              'debugEventsUntilDate': flag.get('debugEventsUntilDate') if flag else None,
                              'reason': reason if include_reasons_in_events else None})

        if not self.is_initialized():
            if self._store.initialized:
                log.warn("Feature Flag evaluation attempted before client has initialized - using last known values from feature store for feature key: " + key)
            else:
                log.warn("Feature Flag evaluation attempted before client has initialized! Feature store unavailable - returning default: "
                         + str(default) + " for feature key: " + key)
                reason = error_reason('CLIENT_NOT_READY')
                send_event(default, None, None, reason)
                return EvaluationDetail(default, None, reason)
        
        if user is not None and user.get('key', "") == "":
            log.warn("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly.")

        flag = self._store.get(FEATURES, key, lambda x: x)
        if not flag:
            reason = error_reason('FLAG_NOT_FOUND')
            send_event(default, None, None, reason)
            return EvaluationDetail(default, None, reason)
        else:
            if user is None or user.get('key') is None:
                reason = error_reason('USER_NOT_SPECIFIED')
                send_event(default, None, flag, reason)
                return EvaluationDetail(default, None, reason)

            try:
                result = evaluate(flag, user, self._store, include_reasons_in_events)
                for event in result.events or []:
                    self._send_event(event)
                detail = result.detail
                if detail.is_default_value():
                    detail = EvaluationDetail(default, None, detail.reason)
                send_event(detail.value, detail.variation_index, flag, detail.reason)
                return detail
            except Exception as e:
                log.error("Unexpected error while evaluating feature flag \"%s\": %s" % (key, e))
                log.debug(traceback.format_exc())
                reason = error_reason('EXCEPTION')
                send_event(default, None, flag, reason)
                return EvaluationDetail(default, None, reason)
def test_clause_can_be_negated():
    clause = {
        'attribute': 'name',
        'op': 'in',
        'values': [ 'Bob' ],
        'negate': True
    }
    user = { 'key': 'x', 'name': 'Bob' }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store).detail.value == False
def test_flag_returns_error_if_off_variation_is_negative():
    flag = {
        'key': 'feature',
        'on': False,
        'offVariation': -1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail(None, None, {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_flag_returns_off_variation_if_flag_is_off():
    flag = {
        'key': 'feature',
        'on': False,
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail('b', 1, {'kind': 'OFF'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_clause_can_be_negated():
    clause = {
        'attribute': 'name',
        'op': 'in',
        'values': [ 'Bob' ],
        'negate': True
    }
    user = { 'key': 'x', 'name': 'Bob' }
    flag = _make_bool_flag_from_clause(clause)
    assert evaluate(flag, user, empty_store, event_factory).detail.value == False
def test_flag_returns_off_variation_if_flag_is_off():
    flag = {
        'key': 'feature',
        'on': False,
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'x'}
    detail = EvaluationDetail('b', 1, {'kind': 'OFF'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_flag_returns_error_if_fallthrough_has_no_variation_or_rollout():
    flag = {
        'key': 'feature',
        'on': True,
        'fallthrough': {},
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail(None, None, {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'})
    assert evaluate(flag, user, empty_store, event_factory) == EvalResult(detail, [])
def test_flag_returns_error_if_fallthrough_has_no_variation_or_rollout():
    flag = {
        'key': 'feature',
        'on': True,
        'fallthrough': {},
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail(None, None, {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_flag_returns_error_if_off_variation_is_negative():
    flag = {
        'key': 'feature',
        'on': False,
        'offVariation': -1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail(None, None, {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'})
    assert evaluate(flag, user, empty_store, event_factory) == EvalResult(detail, [])
Exemple #27
0
def test_flag_returns_none_if_flag_is_off_and_off_variation_is_unspecified():
    flag = {
        'key': 'feature',
        'on': False,
        'fallthrough': {
            'variation': 0
        },
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'x'}
    assert evaluate(flag, user, empty_store) == (None, [])
Exemple #28
0
    def variation(self, key, user, default):
        default = self._config.get_default(key, default)
        self._sanitize_user(user)

        if self._config.offline:
            return default

        def send_event(value, version=None):
            self._send_event({
                'kind': 'feature',
                'key': key,
                'user': user,
                'value': value,
                'default': default,
                'version': version
            })

        if not self.is_initialized():
            log.warn(
                "Feature Flag evaluation attempted before client has finished initializing! Returning default: "
                + str(default) + " for feature key: " + key)
            send_event(default)
            return default

        if user is None or user.get('key') is None:
            log.warn(
                "Missing user or user key when evaluating Feature Flag key: " +
                key + ". Returning default.")
            send_event(default)
            return default

        if user.get('key', "") == "":
            log.warn(
                "User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly."
            )

        flag = self._store.get(key)
        if not flag:
            log.warn("Feature Flag key: " + key +
                     " not found in Feature Store. Returning default.")
            send_event(default)
            return default

        value, events = evaluate(flag, user, self._store)
        for event in events or []:
            self._send_event(event)
            log.debug("Sending event: " + str(event))

        if value is not None:
            send_event(value, flag.get('version'))
            return value

        send_event(default, flag.get('version'))
        return default
def test_flag_returns_off_variation_if_prerequisite_not_found():
    flag = {
        'key': 'feature0',
        'on': True,
        'prerequisites': [{'key': 'badfeature', 'variation': 1}],
        'fallthrough': { 'variation': 0 },
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail('b', 1, {'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': 'badfeature'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_flag_matches_user_from_targets():
    flag = {
        'key': 'feature0',
        'on': True,
        'targets': [{ 'values': ['whoever', 'userkey'], 'variation': 2 }],
        'fallthrough': { 'variation': 0 },
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'userkey' }
    detail = EvaluationDetail('c', 2, {'kind': 'TARGET_MATCH'})
    assert evaluate(flag, user, empty_store, event_factory) == EvalResult(detail, [])
Exemple #31
0
    def all_flags_state(self, user, **kwargs):
        """Returns an object that encapsulates the state of all feature flags for a given user,
        including the flag values and also metadata that can be used on the front end. 
        
        This method does not send analytics events back to LaunchDarkly.

        :param dict user: the end user requesting the feature flags
        :param kwargs: optional parameters affecting how the state is computed: set
          `client_side_only=True` to limit it to only flags that are marked for use with the
          client-side SDK (by default, all flags are included); set `with_reasons=True` to
          include evaluation reasons in the state (see `variation_detail`)
        :return: a FeatureFlagsState object (will never be None; its 'valid' property will be False
          if the client is offline, has not been initialized, or the user is None or has no key)
        :rtype: FeatureFlagsState
        """
        if self._config.offline:
            log.warn("all_flags_state() called, but client is in offline mode. Returning empty state")
            return FeatureFlagsState(False)

        if not self.is_initialized():
            if self._store.initialized:
                log.warn("all_flags_state() called before client has finished initializing! Using last known values from feature store")
            else:
                log.warn("all_flags_state() called before client has finished initializing! Feature store unavailable - returning empty state")
                return FeatureFlagsState(False)

        if user is None or user.get('key') is None:
            log.warn("User or user key is None when calling all_flags_state(). Returning empty state.")
            return FeatureFlagsState(False)
        
        state = FeatureFlagsState(True)
        client_only = kwargs.get('client_side_only', False)
        with_reasons = kwargs.get('with_reasons', False)
        try:
            flags_map = self._store.all(FEATURES, lambda x: x)
        except Exception as e:
            log.error("Unable to read flags for all_flag_state: %s" % e)
            return FeatureFlagsState(False)
        
        for key, flag in flags_map.items():
            if client_only and not flag.get('clientSide', False):
                continue
            try:
                detail = evaluate(flag, user, self._store, False).detail
                state.add_flag(flag, detail.value, detail.variation_index,
                    detail.reason if with_reasons else None)
            except Exception as e:
                log.error("Error evaluating flag \"%s\" in all_flags_state: %s" % (key, e))
                log.debug(traceback.format_exc())
                reason = {'kind': 'ERROR', 'errorKind': 'EXCEPTION'}
                state.add_flag(flag, None, None, reason if with_reasons else None)
        
        return state
def test_flag_matches_user_from_targets():
    flag = {
        'key': 'feature0',
        'on': True,
        'targets': [{ 'values': ['whoever', 'userkey'], 'variation': 2 }],
        'fallthrough': { 'variation': 0 },
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'userkey' }
    detail = EvaluationDetail('c', 2, {'kind': 'TARGET_MATCH'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_flag_returns_off_variation_if_prerequisite_not_found():
    flag = {
        'key': 'feature0',
        'on': True,
        'prerequisites': [{'key': 'badfeature', 'variation': 1}],
        'fallthrough': { 'variation': 0 },
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = { 'key': 'x' }
    detail = EvaluationDetail('b', 1, {'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': 'badfeature'})
    assert evaluate(flag, user, empty_store, event_factory) == EvalResult(detail, [])
Exemple #34
0
def test_flag_returns_off_variation_if_flag_is_off():
    flag = {
        'key': 'feature',
        'on': False,
        'offVariation': 1,
        'fallthrough': {
            'variation': 0
        },
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'x'}
    assert evaluate(flag, user, empty_store) == ('b', [])
Exemple #35
0
    def all_flags(self, user):
        if self._config.offline:
            log.warn("all_flags() called, but client is in offline mode. Returning None")
            return None

        if not self.is_initialized():
            log.warn("all_flags() called before client has finished initializing! Returning None")
            return None

        if user is None or user.get('key') is None:
            log.warn("User or user key is None when calling all_flags(). Returning None.")
            return None

        return {k: evaluate(v, user, self._store)[0] for k, v in self._store.all().items() or {}}
def test_flag_returns_error_if_fallthrough_variation_is_too_high():
    flag = {
        'key': 'feature',
        'on': True,
        'fallthrough': {
            'variation': 999
        },
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'x'}
    detail = EvaluationDetail(None, None, {
        'kind': 'ERROR',
        'errorKind': 'MALFORMED_FLAG'
    })
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
Exemple #37
0
def test_flag_matches_user_from_targets():
    flag = {
        'key': 'feature0',
        'on': True,
        'targets': [{
            'values': ['whoever', 'userkey'],
            'variation': 2
        }],
        'fallthrough': {
            'variation': 0
        },
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'userkey'}
    assert evaluate(flag, user, empty_store) == ('c', [])
def test_secondary_key_is_coerced_to_string_for_evaluation():
    # We can't really verify that the rollout calculation works correctly, but we can at least
    # make sure it doesn't error out if there's a non-string secondary value (ch35189)
    rule = {
        'id': 'ruleid',
        'clauses': [
            { 'attribute': 'key', 'op': 'in', 'values': [ 'userkey' ] }
        ],
        'rollout': {
            'salt':  '',
            'variations': [ { 'weight': 100000, 'variation': 1 } ]
        }
    }
    flag = make_boolean_flag_with_rules([rule])
    user = { 'key': 'userkey', 'secondary': 999 }
    assert evaluate(flag, user, empty_store, event_factory).detail.value == True
Exemple #39
0
def test_flag_returns_off_variation_if_prerequisite_not_found():
    flag = {
        'key': 'feature0',
        'on': True,
        'prerequisites': [{
            'key': 'badfeature',
            'variation': 1
        }],
        'fallthrough': {
            'variation': 0
        },
        'offVariation': 1,
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'x'}
    assert evaluate(flag, user, empty_store) == ('b', [])
def test_secondary_key_is_coerced_to_string_for_evaluation():
    # We can't really verify that the rollout calculation works correctly, but we can at least
    # make sure it doesn't error out if there's a non-string secondary value (ch35189)
    rule = {
        'id': 'ruleid',
        'clauses': [
            { 'attribute': 'key', 'op': 'in', 'values': [ 'userkey' ] }
        ],
        'rollout': {
            'salt':  '',
            'variations': [ { 'weight': 100000, 'variation': 1 } ]
        }
    }
    flag = make_boolean_flag_with_rules([rule])
    user = { 'key': 'userkey', 'secondary': 999 }
    assert evaluate(flag, user, empty_store).detail.value == True
def test_flag_returns_error_if_rule_variation_is_negative():
    rule = {
        'id': 'id',
        'clauses': [{
            'attribute': 'key',
            'op': 'in',
            'values': ['userkey']
        }],
        'variation': -1
    }
    flag = make_boolean_flag_with_rules([rule])
    user = {'key': 'userkey'}
    detail = EvaluationDetail(None, None, {
        'kind': 'ERROR',
        'errorKind': 'MALFORMED_FLAG'
    })
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
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_flag_matches_user_from_rules():
    rule = {
        'id': 'id',
        'clauses': [{
            'attribute': 'key',
            'op': 'in',
            'values': ['userkey']
        }],
        'variation': 1
    }
    flag = make_boolean_flag_with_rules([rule])
    user = {'key': 'userkey'}
    detail = EvaluationDetail(True, 1, {
        'kind': 'RULE_MATCH',
        'ruleIndex': 0,
        'ruleId': 'id'
    })
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_segment_match_clause_falls_through_with_no_errors_if_segment_not_found():
    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, empty_store).detail.value == False
Exemple #45
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
    }
    store.upsert(FEATURES, flag1)
    user = {'key': 'x'}
    events_should_be = [{
        'kind': 'feature',
        'key': 'feature1',
        'value': 'e',
        'version': 2,
        'user': user,
        'prereqOf': 'feature0'
    }]
    assert evaluate(flag, user, store) == ('a', events_should_be)
Exemple #46
0
def test_flag_matches_user_from_rules():
    flag = {
        'key':
        'feature0',
        'on':
        True,
        'rules': [{
            'clauses': [{
                'attribute': 'key',
                'op': 'in',
                'values': ['userkey']
            }],
            'variation':
            2
        }],
        'fallthrough': {
            'variation': 0
        },
        'offVariation':
        1,
        'variations': ['a', 'b', 'c']
    }
    user = {'key': 'userkey'}
    assert evaluate(flag, user, empty_store) == ('c', [])
def test_flag_matches_user_from_rules():
    rule = { 'id': 'id', 'clauses': [{'attribute': 'key', 'op': 'in', 'values': ['userkey']}], 'variation': 1}
    flag = make_boolean_flag_with_rules([rule])
    user = { 'key': 'userkey' }
    detail = EvaluationDetail(True, 1, {'kind': 'RULE_MATCH', 'ruleIndex': 0, 'ruleId': 'id'})
    assert evaluate(flag, user, empty_store) == EvalResult(detail, [])
def test_user_key_is_coerced_to_string_for_evaluation():
    clause = { 'attribute': 'key', 'op': 'in', 'values': [ '999' ] }
    flag = _make_bool_flag_from_clause(clause)
    user = { 'key': 999 }
    assert evaluate(flag, user, empty_store).detail.value == True