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] ]