spec/evaluation_spec.rb in launchdarkly-server-sdk-5.5.12 vs spec/evaluation_spec.rb in launchdarkly-server-sdk-5.6.0
- old
+ new
@@ -5,10 +5,12 @@
include LaunchDarkly::Evaluation
let(:features) { LaunchDarkly::InMemoryFeatureStore.new }
+ let(:factory) { LaunchDarkly::Impl::EventFactory.new(false) }
+
let(:user) {
{
key: "userkey",
email: "test@example.com",
name: "Bob"
@@ -34,11 +36,11 @@
fallthrough: { variation: 0 },
variations: ['a', 'b', 'c']
}
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new('b', 1, { kind: 'OFF' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns nil if flag is off and off variation is unspecified" do
@@ -48,11 +50,11 @@
fallthrough: { variation: 0 },
variations: ['a', 'b', 'c']
}
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'OFF' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if off variation is too high" do
@@ -64,11 +66,11 @@
variations: ['a', 'b', 'c']
}
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if off variation is negative" do
@@ -80,11 +82,11 @@
variations: ['a', 'b', 'c']
}
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns off variation if prerequisite is not found" do
@@ -97,11 +99,11 @@
variations: ['a', 'b', 'c']
}
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'badfeature' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns off variation and event if prerequisite of a prerequisite is not found" do
@@ -125,14 +127,13 @@
features.upsert(LaunchDarkly::FEATURES, flag1)
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
events_should_be = [{
- kind: 'feature', key: 'feature1', user: user, variation: nil, value: nil, version: 2, prereqOf: 'feature0',
- trackEvents: nil, debugEventsUntilDate: nil
+ kind: 'feature', key: 'feature1', user: user, value: nil, default: nil, variation: nil, version: 2, prereqOf: 'feature0'
}]
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq(events_should_be)
end
it "returns off variation and event if prerequisite is off" do
@@ -157,14 +158,13 @@
features.upsert(LaunchDarkly::FEATURES, flag1)
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
events_should_be = [{
- kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', version: 2, prereqOf: 'feature0',
- trackEvents: nil, debugEventsUntilDate: nil
+ kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', default: nil, version: 2, prereqOf: 'feature0'
}]
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq(events_should_be)
end
it "returns off variation and event if prerequisite is not met" do
@@ -187,14 +187,13 @@
features.upsert(LaunchDarkly::FEATURES, flag1)
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
events_should_be = [{
- kind: 'feature', key: 'feature1', user: user, variation: 0, value: 'd', version: 2, prereqOf: 'feature0',
- trackEvents: nil, debugEventsUntilDate: nil
+ kind: 'feature', key: 'feature1', user: user, variation: 0, value: 'd', default: nil, version: 2, prereqOf: 'feature0'
}]
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq(events_should_be)
end
it "returns fallthrough variation and event if prerequisite is met and there are no rules" do
@@ -216,14 +215,13 @@
}
features.upsert(LaunchDarkly::FEATURES, flag1)
user = { key: 'x' }
detail = LaunchDarkly::EvaluationDetail.new('a', 0, { kind: 'FALLTHROUGH' })
events_should_be = [{
- kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', version: 2, prereqOf: 'feature0',
- trackEvents: nil, debugEventsUntilDate: nil
+ kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', default: nil, version: 2, prereqOf: 'feature0'
}]
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq(events_should_be)
end
it "returns an error if fallthrough variation is too high" do
@@ -234,11 +232,11 @@
offVariation: 1,
variations: ['a', 'b', 'c']
}
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if fallthrough variation is negative" do
@@ -249,11 +247,11 @@
offVariation: 1,
variations: ['a', 'b', 'c']
}
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if fallthrough has no variation or rollout" do
@@ -264,11 +262,11 @@
offVariation: 1,
variations: ['a', 'b', 'c']
}
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if fallthrough has a rollout with no variations" do
@@ -279,11 +277,11 @@
offVariation: 1,
variations: ['a', 'b', 'c']
}
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "matches user from targets" do
@@ -297,55 +295,55 @@
offVariation: 1,
variations: ['a', 'b', 'c']
}
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new('c', 2, { kind: 'TARGET_MATCH' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "matches user from rules" do
rule = { id: 'ruleid', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], variation: 1 }
flag = boolean_flag_with_rules([rule])
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(true, 1,
{ kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if rule variation is too high" do
rule = { id: 'ruleid', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], variation: 999 }
flag = boolean_flag_with_rules([rule])
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if rule variation is negative" do
rule = { id: 'ruleid', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], variation: -1 }
flag = boolean_flag_with_rules([rule])
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if rule has neither variation nor rollout" do
rule = { id: 'ruleid', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }] }
flag = boolean_flag_with_rules([rule])
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "returns an error if rule has a rollout with no variations" do
@@ -353,79 +351,79 @@
rollout: { variations: [] } }
flag = boolean_flag_with_rules([rule])
user = { key: 'userkey' }
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail).to eq(detail)
expect(result.events).to eq([])
end
it "coerces user key to a string for evaluation" do
clause = { attribute: 'key', op: 'in', values: ['999'] }
flag = boolean_flag_with_clauses([clause])
user = { key: 999 }
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail.value).to eq(true)
end
it "coerces secondary key to a string for evaluation" do
# 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 = boolean_flag_with_rules([rule])
user = { key: "userkey", secondary: 999 }
- result = evaluate(flag, user, features, logger)
+ result = evaluate(flag, user, features, logger, factory)
expect(result.detail.reason).to eq({ kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid'})
end
end
describe "clause" do
it "can match built-in attribute" do
user = { key: 'x', name: 'Bob' }
clause = { attribute: 'name', op: 'in', values: ['Bob'] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be true
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
end
it "can match custom attribute" do
user = { key: 'x', name: 'Bob', custom: { legs: 4 } }
clause = { attribute: 'legs', op: 'in', values: [4] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be true
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
end
it "returns false for missing attribute" do
user = { key: 'x', name: 'Bob' }
clause = { attribute: 'legs', op: 'in', values: [4] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be false
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
end
it "returns false for unknown operator" do
user = { key: 'x', name: 'Bob' }
clause = { attribute: 'name', op: 'unknown', values: [4] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be false
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
end
it "does not stop evaluating rules after clause with unknown operator" do
user = { key: 'x', name: 'Bob' }
clause0 = { attribute: 'name', op: 'unknown', values: [4] }
rule0 = { clauses: [ clause0 ], variation: 1 }
clause1 = { attribute: 'name', op: 'in', values: ['Bob'] }
rule1 = { clauses: [ clause1 ], variation: 1 }
flag = boolean_flag_with_rules([rule0, rule1])
- expect(evaluate(flag, user, features, logger).detail.value).to be true
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
end
it "can be negated" do
user = { key: 'x', name: 'Bob' }
clause = { attribute: 'name', op: 'in', values: ['Bob'], negate: true }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be false
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
end
it "retrieves segment from segment store for segmentMatch operator" do
segment = {
key: 'segkey',
@@ -436,27 +434,27 @@
features.upsert(LaunchDarkly::SEGMENTS, segment)
user = { key: 'userkey' }
clause = { attribute: '', op: 'segmentMatch', values: ['segkey'] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be true
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
end
it "falls through with no errors if referenced segment is not found" do
user = { key: 'userkey' }
clause = { attribute: '', op: 'segmentMatch', values: ['segkey'] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be false
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
end
it "can be negated" do
user = { key: 'x', name: 'Bob' }
clause = { attribute: 'name', op: 'in', values: ['Bob'] }
flag = boolean_flag_with_clauses([clause])
expect {
clause[:negate] = true
- }.to change {evaluate(flag, user, features, logger).detail.value}.from(true).to(false)
+ }.to change {evaluate(flag, user, features, logger, factory).detail.value}.from(true).to(false)
end
end
describe "operators" do
dateStr1 = "2017-12-06T00:00:00.000-07:00"
@@ -555,11 +553,11 @@
shouldBe = params[3]
it "should return #{shouldBe} for #{value1} #{op} #{value2}" do
user = { key: 'x', custom: { foo: value1 } }
clause = { attribute: 'foo', op: op, values: [value2] }
flag = boolean_flag_with_clauses([clause])
- expect(evaluate(flag, user, features, logger).detail.value).to be shouldBe
+ expect(evaluate(flag, user, features, logger, factory).detail.value).to be shouldBe
end
end
end
describe "bucket_user" do
@@ -646,10 +644,10 @@
describe 'segment matching' do
def test_segment_match(segment)
features.upsert(LaunchDarkly::SEGMENTS, segment)
clause = make_segment_match_clause(segment)
flag = boolean_flag_with_clauses([clause])
- evaluate(flag, user, features, logger).detail.value
+ evaluate(flag, user, features, logger, factory).detail.value
end
it 'explicitly includes user' do
segment = make_segment('segkey')
segment[:included] = [ user[:key] ]