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_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_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_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 evaluate(flag, user, store, event_factory): sanitized_user = stringify_attrs( user, __USER_ATTRS_TO_STRINGIFY_FOR_EVALUATION__) value = flag.get('value') version = flag.get('version') variation_index = flag.get('variation') reason = flag.get('reason') detail = EvaluationDetail(value, variation_index, reason) return EvalResult(detail=detail, events=None)
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, [])
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_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_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, [])
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, [])
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, [])
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, [])