launchdarkly-server-sdk 6.1.1 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -5
- data/lib/ldclient-rb/config.rb +118 -4
- data/lib/ldclient-rb/evaluation_detail.rb +104 -14
- data/lib/ldclient-rb/events.rb +201 -107
- data/lib/ldclient-rb/file_data_source.rb +9 -300
- data/lib/ldclient-rb/flags_state.rb +23 -12
- data/lib/ldclient-rb/impl/big_segments.rb +117 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +1 -1
- data/lib/ldclient-rb/impl/evaluator.rb +116 -62
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +22 -9
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +53 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +1 -1
- data/lib/ldclient-rb/impl/event_summarizer.rb +63 -0
- data/lib/ldclient-rb/impl/event_types.rb +90 -0
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +82 -18
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +84 -31
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +177 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +7 -37
- data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
- data/lib/ldclient-rb/impl/util.rb +62 -1
- data/lib/ldclient-rb/integrations/consul.rb +8 -1
- data/lib/ldclient-rb/integrations/dynamodb.rb +48 -3
- data/lib/ldclient-rb/integrations/file_data.rb +108 -0
- data/lib/ldclient-rb/integrations/redis.rb +42 -2
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +438 -0
- data/lib/ldclient-rb/integrations/test_data.rb +209 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +5 -0
- data/lib/ldclient-rb/integrations.rb +2 -51
- data/lib/ldclient-rb/interfaces.rb +152 -2
- data/lib/ldclient-rb/ldclient.rb +131 -33
- data/lib/ldclient-rb/polling.rb +22 -41
- data/lib/ldclient-rb/requestor.rb +3 -3
- data/lib/ldclient-rb/stream.rb +4 -3
- data/lib/ldclient-rb/util.rb +10 -1
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +0 -1
- metadata +35 -132
- data/.circleci/config.yml +0 -40
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
- data/.github/ISSUE_TEMPLATE/config.yml +0 -5
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- data/.github/pull_request_template.md +0 -21
- data/.gitignore +0 -16
- data/.hound.yml +0 -2
- data/.ldrelease/build-docs.sh +0 -18
- data/.ldrelease/circleci/linux/execute.sh +0 -18
- data/.ldrelease/circleci/mac/execute.sh +0 -18
- data/.ldrelease/circleci/template/build.sh +0 -29
- data/.ldrelease/circleci/template/publish.sh +0 -23
- data/.ldrelease/circleci/template/set-gem-home.sh +0 -7
- data/.ldrelease/circleci/template/test.sh +0 -10
- data/.ldrelease/circleci/template/update-version.sh +0 -8
- data/.ldrelease/circleci/windows/execute.ps1 +0 -19
- data/.ldrelease/config.yml +0 -29
- data/.rspec +0 -2
- data/.rubocop.yml +0 -600
- data/.simplecov +0 -4
- data/CHANGELOG.md +0 -351
- data/CODEOWNERS +0 -1
- data/CONTRIBUTING.md +0 -37
- data/Gemfile +0 -3
- data/azure-pipelines.yml +0 -51
- data/docs/Makefile +0 -26
- data/docs/index.md +0 -9
- data/launchdarkly-server-sdk.gemspec +0 -45
- data/lib/ldclient-rb/event_summarizer.rb +0 -55
- data/lib/ldclient-rb/impl/event_factory.rb +0 -120
- data/spec/config_spec.rb +0 -63
- data/spec/diagnostic_events_spec.rb +0 -163
- data/spec/evaluation_detail_spec.rb +0 -135
- data/spec/event_sender_spec.rb +0 -197
- data/spec/event_summarizer_spec.rb +0 -63
- data/spec/events_spec.rb +0 -607
- data/spec/expiring_cache_spec.rb +0 -76
- data/spec/feature_store_spec_base.rb +0 -213
- data/spec/file_data_source_spec.rb +0 -283
- data/spec/fixtures/feature.json +0 -37
- data/spec/fixtures/feature1.json +0 -36
- data/spec/fixtures/user.json +0 -9
- data/spec/flags_state_spec.rb +0 -81
- data/spec/http_util.rb +0 -132
- data/spec/impl/evaluator_bucketing_spec.rb +0 -111
- data/spec/impl/evaluator_clause_spec.rb +0 -55
- data/spec/impl/evaluator_operators_spec.rb +0 -141
- data/spec/impl/evaluator_rule_spec.rb +0 -96
- data/spec/impl/evaluator_segment_spec.rb +0 -125
- data/spec/impl/evaluator_spec.rb +0 -305
- data/spec/impl/evaluator_spec_base.rb +0 -75
- data/spec/impl/model/serialization_spec.rb +0 -41
- data/spec/in_memory_feature_store_spec.rb +0 -12
- data/spec/integrations/consul_feature_store_spec.rb +0 -40
- data/spec/integrations/dynamodb_feature_store_spec.rb +0 -103
- data/spec/integrations/store_wrapper_spec.rb +0 -276
- data/spec/launchdarkly-server-sdk_spec.rb +0 -13
- data/spec/launchdarkly-server-sdk_spec_autoloadtest.rb +0 -9
- data/spec/ldclient_end_to_end_spec.rb +0 -157
- data/spec/ldclient_spec.rb +0 -643
- data/spec/newrelic_spec.rb +0 -5
- data/spec/polling_spec.rb +0 -120
- data/spec/redis_feature_store_spec.rb +0 -121
- data/spec/requestor_spec.rb +0 -196
- data/spec/segment_store_spec_base.rb +0 -95
- data/spec/simple_lru_cache_spec.rb +0 -24
- data/spec/spec_helper.rb +0 -9
- data/spec/store_spec.rb +0 -10
- data/spec/stream_spec.rb +0 -45
- data/spec/user_filter_spec.rb +0 -91
- data/spec/util_spec.rb +0 -17
- data/spec/version_spec.rb +0 -7
@@ -1,120 +0,0 @@
|
|
1
|
-
|
2
|
-
module LaunchDarkly
|
3
|
-
module Impl
|
4
|
-
# Event constructors are centralized here to avoid mistakes and repetitive logic.
|
5
|
-
# The LDClient owns two instances of EventFactory: one that always embeds evaluation reasons
|
6
|
-
# in the events (for when variation_detail is called) and one that doesn't.
|
7
|
-
#
|
8
|
-
# Note that these methods do not set the "creationDate" property, because in the Ruby client,
|
9
|
-
# that is done by EventProcessor.add_event().
|
10
|
-
class EventFactory
|
11
|
-
def initialize(with_reasons)
|
12
|
-
@with_reasons = with_reasons
|
13
|
-
end
|
14
|
-
|
15
|
-
def new_eval_event(flag, user, detail, default_value, prereq_of_flag = nil)
|
16
|
-
add_experiment_data = is_experiment(flag, detail.reason)
|
17
|
-
e = {
|
18
|
-
kind: 'feature',
|
19
|
-
key: flag[:key],
|
20
|
-
user: user,
|
21
|
-
variation: detail.variation_index,
|
22
|
-
value: detail.value,
|
23
|
-
default: default_value,
|
24
|
-
version: flag[:version]
|
25
|
-
}
|
26
|
-
# the following properties are handled separately so we don't waste bandwidth on unused keys
|
27
|
-
e[:trackEvents] = true if add_experiment_data || flag[:trackEvents]
|
28
|
-
e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
|
29
|
-
e[:prereqOf] = prereq_of_flag[:key] if !prereq_of_flag.nil?
|
30
|
-
e[:reason] = detail.reason if add_experiment_data || @with_reasons
|
31
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
32
|
-
e
|
33
|
-
end
|
34
|
-
|
35
|
-
def new_default_event(flag, user, default_value, reason)
|
36
|
-
e = {
|
37
|
-
kind: 'feature',
|
38
|
-
key: flag[:key],
|
39
|
-
user: user,
|
40
|
-
value: default_value,
|
41
|
-
default: default_value,
|
42
|
-
version: flag[:version]
|
43
|
-
}
|
44
|
-
e[:trackEvents] = true if flag[:trackEvents]
|
45
|
-
e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
|
46
|
-
e[:reason] = reason if @with_reasons
|
47
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
48
|
-
e
|
49
|
-
end
|
50
|
-
|
51
|
-
def new_unknown_flag_event(key, user, default_value, reason)
|
52
|
-
e = {
|
53
|
-
kind: 'feature',
|
54
|
-
key: key,
|
55
|
-
user: user,
|
56
|
-
value: default_value,
|
57
|
-
default: default_value
|
58
|
-
}
|
59
|
-
e[:reason] = reason if @with_reasons
|
60
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
61
|
-
e
|
62
|
-
end
|
63
|
-
|
64
|
-
def new_identify_event(user)
|
65
|
-
{
|
66
|
-
kind: 'identify',
|
67
|
-
key: user[:key],
|
68
|
-
user: user
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
def new_alias_event(current_context, previous_context)
|
73
|
-
{
|
74
|
-
kind: 'alias',
|
75
|
-
key: current_context[:key],
|
76
|
-
contextKind: context_to_context_kind(current_context),
|
77
|
-
previousKey: previous_context[:key],
|
78
|
-
previousContextKind: context_to_context_kind(previous_context)
|
79
|
-
}
|
80
|
-
end
|
81
|
-
|
82
|
-
def new_custom_event(event_name, user, data, metric_value)
|
83
|
-
e = {
|
84
|
-
kind: 'custom',
|
85
|
-
key: event_name,
|
86
|
-
user: user
|
87
|
-
}
|
88
|
-
e[:data] = data if !data.nil?
|
89
|
-
e[:metricValue] = metric_value if !metric_value.nil?
|
90
|
-
e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
|
91
|
-
e
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def context_to_context_kind(user)
|
97
|
-
if !user.nil? && user[:anonymous]
|
98
|
-
return "anonymousUser"
|
99
|
-
else
|
100
|
-
return "user"
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def is_experiment(flag, reason)
|
105
|
-
return false if !reason
|
106
|
-
case reason[:kind]
|
107
|
-
when 'RULE_MATCH'
|
108
|
-
index = reason[:ruleIndex]
|
109
|
-
if !index.nil?
|
110
|
-
rules = flag[:rules] || []
|
111
|
-
return index >= 0 && index < rules.length && rules[index][:trackEvents]
|
112
|
-
end
|
113
|
-
when 'FALLTHROUGH'
|
114
|
-
return !!flag[:trackEventsFallthrough]
|
115
|
-
end
|
116
|
-
false
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
data/spec/config_spec.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe LaunchDarkly::Config do
|
4
|
-
subject { LaunchDarkly::Config }
|
5
|
-
describe ".initialize" do
|
6
|
-
it "can be initialized with default settings" do
|
7
|
-
expect(subject).to receive(:default_capacity).and_return 1234
|
8
|
-
expect(subject.new.capacity).to eq 1234
|
9
|
-
end
|
10
|
-
it "accepts custom arguments" do
|
11
|
-
expect(subject).to_not receive(:default_capacity)
|
12
|
-
expect(subject.new(capacity: 50).capacity).to eq 50
|
13
|
-
end
|
14
|
-
it "will chomp base_url and stream_uri" do
|
15
|
-
uri = "https://test.launchdarkly.com"
|
16
|
-
config = subject.new(base_uri: uri + "/")
|
17
|
-
expect(config.base_uri).to eq uri
|
18
|
-
end
|
19
|
-
end
|
20
|
-
describe "@base_uri" do
|
21
|
-
it "can be read" do
|
22
|
-
expect(subject.new.base_uri).to eq subject.default_base_uri
|
23
|
-
end
|
24
|
-
end
|
25
|
-
describe "@events_uri" do
|
26
|
-
it "can be read" do
|
27
|
-
expect(subject.new.events_uri).to eq subject.default_events_uri
|
28
|
-
end
|
29
|
-
end
|
30
|
-
describe "@stream_uri" do
|
31
|
-
it "can be read" do
|
32
|
-
expect(subject.new.stream_uri).to eq subject.default_stream_uri
|
33
|
-
end
|
34
|
-
end
|
35
|
-
describe ".default_cache_store" do
|
36
|
-
it "uses Rails cache if it is available" do
|
37
|
-
rails = instance_double("Rails", cache: :cache)
|
38
|
-
stub_const("Rails", rails)
|
39
|
-
expect(subject.default_cache_store).to eq :cache
|
40
|
-
end
|
41
|
-
it "uses memory store if Rails is not available" do
|
42
|
-
expect(subject.default_cache_store).to be_an_instance_of LaunchDarkly::ThreadSafeMemoryStore
|
43
|
-
end
|
44
|
-
end
|
45
|
-
describe ".default_logger" do
|
46
|
-
it "uses Rails logger if it is available" do
|
47
|
-
rails = instance_double("Rails", logger: :logger)
|
48
|
-
stub_const("Rails", rails)
|
49
|
-
expect(subject.default_logger).to eq :logger
|
50
|
-
end
|
51
|
-
it "Uses logger if Rails is not available" do
|
52
|
-
expect(subject.default_logger).to be_an_instance_of Logger
|
53
|
-
end
|
54
|
-
end
|
55
|
-
describe ".poll_interval" do
|
56
|
-
it "can be set to greater than the default" do
|
57
|
-
expect(subject.new(poll_interval: 31).poll_interval).to eq 31
|
58
|
-
end
|
59
|
-
it "cannot be set to less than the default" do
|
60
|
-
expect(subject.new(poll_interval: 29).poll_interval).to eq 30
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,163 +0,0 @@
|
|
1
|
-
require "ldclient-rb/impl/diagnostic_events"
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
module LaunchDarkly
|
6
|
-
module Impl
|
7
|
-
describe DiagnosticAccumulator do
|
8
|
-
subject { DiagnosticAccumulator }
|
9
|
-
|
10
|
-
let(:sdk_key) { "sdk_key" }
|
11
|
-
let(:default_id) { subject.create_diagnostic_id("my-key") }
|
12
|
-
let(:default_acc) { subject.new(default_id) }
|
13
|
-
|
14
|
-
it "creates unique ID with SDK key suffix" do
|
15
|
-
id1 = subject.create_diagnostic_id("1234567890")
|
16
|
-
expect(id1[:sdkKeySuffix]).to eq "567890"
|
17
|
-
expect(id1[:diagnosticId]).not_to be_nil
|
18
|
-
|
19
|
-
id2 = subject.create_diagnostic_id("1234567890")
|
20
|
-
expect(id2[:diagnosticId]).not_to eq id1[:diagnosticId]
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "init event" do
|
24
|
-
def expected_default_config
|
25
|
-
{
|
26
|
-
allAttributesPrivate: false,
|
27
|
-
connectTimeoutMillis: Config.default_connect_timeout * 1000,
|
28
|
-
customBaseURI: false,
|
29
|
-
customEventsURI: false,
|
30
|
-
customStreamURI: false,
|
31
|
-
diagnosticRecordingIntervalMillis: Config.default_diagnostic_recording_interval * 1000,
|
32
|
-
eventsCapacity: Config.default_capacity,
|
33
|
-
eventsFlushIntervalMillis: Config.default_flush_interval * 1000,
|
34
|
-
inlineUsersInEvents: false,
|
35
|
-
pollingIntervalMillis: Config.default_poll_interval * 1000,
|
36
|
-
socketTimeoutMillis: Config.default_read_timeout * 1000,
|
37
|
-
streamingDisabled: false,
|
38
|
-
userKeysCapacity: Config.default_user_keys_capacity,
|
39
|
-
userKeysFlushIntervalMillis: Config.default_user_keys_flush_interval * 1000,
|
40
|
-
usingProxy: false,
|
41
|
-
usingRelayDaemon: false
|
42
|
-
}
|
43
|
-
end
|
44
|
-
|
45
|
-
it "has basic fields" do
|
46
|
-
event = default_acc.create_init_event(Config.new)
|
47
|
-
expect(event[:kind]).to eq 'diagnostic-init'
|
48
|
-
expect(event[:creationDate]).not_to be_nil
|
49
|
-
expect(event[:id]).to eq default_id
|
50
|
-
end
|
51
|
-
|
52
|
-
it "can have default config data" do
|
53
|
-
event = default_acc.create_init_event(Config.new)
|
54
|
-
expect(event[:configuration]).to eq expected_default_config
|
55
|
-
end
|
56
|
-
|
57
|
-
it "can have custom config data" do
|
58
|
-
changes_and_expected = [
|
59
|
-
[ { all_attributes_private: true }, { allAttributesPrivate: true } ],
|
60
|
-
[ { connect_timeout: 46 }, { connectTimeoutMillis: 46000 } ],
|
61
|
-
[ { base_uri: 'http://custom' }, { customBaseURI: true } ],
|
62
|
-
[ { events_uri: 'http://custom' }, { customEventsURI: true } ],
|
63
|
-
[ { stream_uri: 'http://custom' }, { customStreamURI: true } ],
|
64
|
-
[ { diagnostic_recording_interval: 9999 }, { diagnosticRecordingIntervalMillis: 9999000 } ],
|
65
|
-
[ { capacity: 4000 }, { eventsCapacity: 4000 } ],
|
66
|
-
[ { flush_interval: 46 }, { eventsFlushIntervalMillis: 46000 } ],
|
67
|
-
[ { inline_users_in_events: true }, { inlineUsersInEvents: true } ],
|
68
|
-
[ { poll_interval: 999 }, { pollingIntervalMillis: 999000 } ],
|
69
|
-
[ { read_timeout: 46 }, { socketTimeoutMillis: 46000 } ],
|
70
|
-
[ { stream: false }, { streamingDisabled: true } ],
|
71
|
-
[ { user_keys_capacity: 999 }, { userKeysCapacity: 999 } ],
|
72
|
-
[ { user_keys_flush_interval: 999 }, { userKeysFlushIntervalMillis: 999000 } ],
|
73
|
-
[ { use_ldd: true }, { usingRelayDaemon: true } ]
|
74
|
-
]
|
75
|
-
changes_and_expected.each do |config_values, expected_values|
|
76
|
-
config = Config.new(config_values)
|
77
|
-
event = default_acc.create_init_event(config)
|
78
|
-
expect(event[:configuration]).to eq expected_default_config.merge(expected_values)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
it "detects proxy" do
|
83
|
-
begin
|
84
|
-
ENV["http_proxy"] = 'http://my-proxy'
|
85
|
-
event = default_acc.create_init_event(Config.new)
|
86
|
-
expect(event[:configuration][:usingProxy]).to be true
|
87
|
-
ensure
|
88
|
-
ENV["http_proxy"] = nil
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
it "has expected SDK data" do
|
93
|
-
event = default_acc.create_init_event(Config.new)
|
94
|
-
expect(event[:sdk]).to eq ({
|
95
|
-
name: 'ruby-server-sdk',
|
96
|
-
version: LaunchDarkly::VERSION
|
97
|
-
})
|
98
|
-
end
|
99
|
-
|
100
|
-
it "has expected SDK data with wrapper" do
|
101
|
-
event = default_acc.create_init_event(Config.new(wrapper_name: 'my-wrapper', wrapper_version: '2.0'))
|
102
|
-
expect(event[:sdk]).to eq ({
|
103
|
-
name: 'ruby-server-sdk',
|
104
|
-
version: LaunchDarkly::VERSION,
|
105
|
-
wrapperName: 'my-wrapper',
|
106
|
-
wrapperVersion: '2.0'
|
107
|
-
})
|
108
|
-
end
|
109
|
-
|
110
|
-
it "has expected platform data" do
|
111
|
-
event = default_acc.create_init_event(Config.new)
|
112
|
-
expect(event[:platform]).to include ({
|
113
|
-
name: 'ruby'
|
114
|
-
})
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe "periodic event" do
|
119
|
-
it "has correct default values" do
|
120
|
-
acc = subject.new(default_id)
|
121
|
-
event = acc.create_periodic_event_and_reset(2, 3, 4)
|
122
|
-
expect(event).to include({
|
123
|
-
kind: 'diagnostic',
|
124
|
-
id: default_id,
|
125
|
-
droppedEvents: 2,
|
126
|
-
deduplicatedUsers: 3,
|
127
|
-
eventsInLastBatch: 4,
|
128
|
-
streamInits: []
|
129
|
-
})
|
130
|
-
expect(event[:creationDate]).not_to be_nil
|
131
|
-
expect(event[:dataSinceDate]).not_to be_nil
|
132
|
-
end
|
133
|
-
|
134
|
-
it "can add stream init" do
|
135
|
-
acc = subject.new(default_id)
|
136
|
-
acc.record_stream_init(1000, false, 2000)
|
137
|
-
event = acc.create_periodic_event_and_reset(0, 0, 0)
|
138
|
-
expect(event[:streamInits]).to eq [{ timestamp: 1000, failed: false, durationMillis: 2000 }]
|
139
|
-
end
|
140
|
-
|
141
|
-
it "resets fields after creating event" do
|
142
|
-
acc = subject.new(default_id)
|
143
|
-
acc.record_stream_init(1000, false, 2000)
|
144
|
-
event1 = acc.create_periodic_event_and_reset(2, 3, 4)
|
145
|
-
event2 = acc.create_periodic_event_and_reset(5, 6, 7)
|
146
|
-
expect(event1).to include ({
|
147
|
-
droppedEvents: 2,
|
148
|
-
deduplicatedUsers: 3,
|
149
|
-
eventsInLastBatch: 4,
|
150
|
-
streamInits: [{ timestamp: 1000, failed: false, durationMillis: 2000 }]
|
151
|
-
})
|
152
|
-
expect(event2).to include ({
|
153
|
-
dataSinceDate: event1[:creationDate],
|
154
|
-
droppedEvents: 5,
|
155
|
-
deduplicatedUsers: 6,
|
156
|
-
eventsInLastBatch: 7,
|
157
|
-
streamInits: []
|
158
|
-
})
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
module LaunchDarkly
|
4
|
-
describe "EvaluationDetail" do
|
5
|
-
subject { EvaluationDetail }
|
6
|
-
|
7
|
-
it "sets properties" do
|
8
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off).value).to eq "x"
|
9
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off).variation_index).to eq 0
|
10
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off).reason).to eq EvaluationReason::off
|
11
|
-
end
|
12
|
-
|
13
|
-
it "checks parameter types" do
|
14
|
-
expect { EvaluationDetail.new(nil, nil, EvaluationReason::off) }.not_to raise_error
|
15
|
-
expect { EvaluationDetail.new(nil, 0, EvaluationReason::off) }.not_to raise_error
|
16
|
-
expect { EvaluationDetail.new(nil, "x", EvaluationReason::off) }.to raise_error(ArgumentError)
|
17
|
-
expect { EvaluationDetail.new(nil, 0, { kind: "OFF" }) }.to raise_error(ArgumentError)
|
18
|
-
expect { EvaluationDetail.new(nil, 0, nil) }.to raise_error(ArgumentError)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "equality test" do
|
22
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).to eq EvaluationDetail.new("x", 0, EvaluationReason::off)
|
23
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).not_to eq EvaluationDetail.new("y", 0, EvaluationReason::off)
|
24
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).not_to eq EvaluationDetail.new("x", 1, EvaluationReason::off)
|
25
|
-
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).not_to eq EvaluationDetail.new("x", 0, EvaluationReason::fallthrough)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "EvaluationReason" do
|
30
|
-
subject { EvaluationReason }
|
31
|
-
|
32
|
-
values = [
|
33
|
-
[ EvaluationReason::off, EvaluationReason::OFF, { "kind" => "OFF" }, "OFF", nil ],
|
34
|
-
[ EvaluationReason::fallthrough, EvaluationReason::FALLTHROUGH,
|
35
|
-
{ "kind" => "FALLTHROUGH" }, "FALLTHROUGH", nil ],
|
36
|
-
[ EvaluationReason::target_match, EvaluationReason::TARGET_MATCH,
|
37
|
-
{ "kind" => "TARGET_MATCH" }, "TARGET_MATCH", nil ],
|
38
|
-
[ EvaluationReason::rule_match(1, "x"), EvaluationReason::RULE_MATCH,
|
39
|
-
{ "kind" => "RULE_MATCH", "ruleIndex" => 1, "ruleId" => "x" }, "RULE_MATCH(1,x)",
|
40
|
-
[ EvaluationReason::rule_match(2, "x"), EvaluationReason::rule_match(1, "y") ] ],
|
41
|
-
[ EvaluationReason::prerequisite_failed("x"), EvaluationReason::PREREQUISITE_FAILED,
|
42
|
-
{ "kind" => "PREREQUISITE_FAILED", "prerequisiteKey" => "x" }, "PREREQUISITE_FAILED(x)" ],
|
43
|
-
[ EvaluationReason::error(EvaluationReason::ERROR_FLAG_NOT_FOUND), EvaluationReason::ERROR,
|
44
|
-
{ "kind" => "ERROR", "errorKind" => "FLAG_NOT_FOUND" }, "ERROR(FLAG_NOT_FOUND)" ]
|
45
|
-
]
|
46
|
-
values.each_index do |i|
|
47
|
-
params = values[i]
|
48
|
-
reason = params[0]
|
49
|
-
kind = params[1]
|
50
|
-
json_rep = params[2]
|
51
|
-
brief_str = params[3]
|
52
|
-
unequal_values = params[4]
|
53
|
-
|
54
|
-
describe "reason #{reason.kind}" do
|
55
|
-
it "has correct kind" do
|
56
|
-
expect(reason.kind).to eq kind
|
57
|
-
end
|
58
|
-
|
59
|
-
it "equality to self" do
|
60
|
-
expect(reason).to eq reason
|
61
|
-
end
|
62
|
-
|
63
|
-
it "inequality to others" do
|
64
|
-
values.each_index do |j|
|
65
|
-
if i != j
|
66
|
-
expect(reason).not_to eq values[j][0]
|
67
|
-
end
|
68
|
-
end
|
69
|
-
if !unequal_values.nil?
|
70
|
-
unequal_values.each do |v|
|
71
|
-
expect(reason).not_to eq v
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
it "JSON representation" do
|
77
|
-
expect(JSON.parse(reason.as_json.to_json)).to eq json_rep
|
78
|
-
expect(JSON.parse(reason.to_json)).to eq json_rep
|
79
|
-
end
|
80
|
-
|
81
|
-
it "brief representation" do
|
82
|
-
expect(reason.inspect).to eq brief_str
|
83
|
-
expect(reason.to_s).to eq brief_str
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
it "reuses singleton reasons" do
|
89
|
-
expect(EvaluationReason::off).to be EvaluationReason::off
|
90
|
-
expect(EvaluationReason::fallthrough).to be EvaluationReason::fallthrough
|
91
|
-
expect(EvaluationReason::target_match).to be EvaluationReason::target_match
|
92
|
-
expect(EvaluationReason::rule_match(1, 'x')).not_to be EvaluationReason::rule_match(1, 'x')
|
93
|
-
expect(EvaluationReason::prerequisite_failed('x')).not_to be EvaluationReason::prerequisite_failed('x')
|
94
|
-
errors = [ EvaluationReason::ERROR_CLIENT_NOT_READY, EvaluationReason::ERROR_FLAG_NOT_FOUND,
|
95
|
-
EvaluationReason::ERROR_MALFORMED_FLAG, EvaluationReason::ERROR_USER_NOT_SPECIFIED, EvaluationReason::ERROR_EXCEPTION ]
|
96
|
-
errors.each do |e|
|
97
|
-
expect(EvaluationReason::error(e)).to be EvaluationReason::error(e)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
it "supports [] with JSON property names" do
|
102
|
-
expect(EvaluationReason::off[:kind]).to eq "OFF"
|
103
|
-
expect(EvaluationReason::off[:ruleIndex]).to be nil
|
104
|
-
expect(EvaluationReason::off[:ruleId]).to be nil
|
105
|
-
expect(EvaluationReason::off[:prerequisiteKey]).to be nil
|
106
|
-
expect(EvaluationReason::off[:errorKind]).to be nil
|
107
|
-
expect(EvaluationReason::rule_match(1, "x")[:ruleIndex]).to eq 1
|
108
|
-
expect(EvaluationReason::rule_match(1, "x")[:ruleId]).to eq "x"
|
109
|
-
expect(EvaluationReason::prerequisite_failed("x")[:prerequisiteKey]).to eq "x"
|
110
|
-
expect(EvaluationReason::error(EvaluationReason::ERROR_FLAG_NOT_FOUND)[:errorKind]).to eq "FLAG_NOT_FOUND"
|
111
|
-
end
|
112
|
-
|
113
|
-
it "freezes string properties" do
|
114
|
-
rm = EvaluationReason::rule_match(1, "x")
|
115
|
-
expect { rm.rule_id.upcase! }.to raise_error(RuntimeError)
|
116
|
-
pf = EvaluationReason::prerequisite_failed("x")
|
117
|
-
expect { pf.prerequisite_key.upcase! }.to raise_error(RuntimeError)
|
118
|
-
end
|
119
|
-
|
120
|
-
it "checks parameter types" do
|
121
|
-
expect { EvaluationReason::rule_match(nil, "x") }.to raise_error(ArgumentError)
|
122
|
-
expect { EvaluationReason::rule_match(true, "x") }.to raise_error(ArgumentError)
|
123
|
-
expect { EvaluationReason::rule_match(1, nil) }.not_to raise_error # we allow nil rule_id for backward compatibility
|
124
|
-
expect { EvaluationReason::rule_match(1, 9) }.to raise_error(ArgumentError)
|
125
|
-
expect { EvaluationReason::prerequisite_failed(nil) }.to raise_error(ArgumentError)
|
126
|
-
expect { EvaluationReason::prerequisite_failed(9) }.to raise_error(ArgumentError)
|
127
|
-
expect { EvaluationReason::error(nil) }.to raise_error(ArgumentError)
|
128
|
-
expect { EvaluationReason::error(9) }.to raise_error(ArgumentError)
|
129
|
-
end
|
130
|
-
|
131
|
-
it "does not allow direct access to constructor" do
|
132
|
-
expect { EvaluationReason.new(:off, nil, nil, nil, nil) }.to raise_error(NoMethodError)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
data/spec/event_sender_spec.rb
DELETED
@@ -1,197 +0,0 @@
|
|
1
|
-
require "ldclient-rb/impl/event_sender"
|
2
|
-
|
3
|
-
require "http_util"
|
4
|
-
require "spec_helper"
|
5
|
-
|
6
|
-
require "time"
|
7
|
-
|
8
|
-
module LaunchDarkly
|
9
|
-
module Impl
|
10
|
-
describe EventSender do
|
11
|
-
subject { EventSender }
|
12
|
-
|
13
|
-
let(:sdk_key) { "sdk_key" }
|
14
|
-
let(:fake_data) { '{"things":[]}' }
|
15
|
-
|
16
|
-
def make_sender(server)
|
17
|
-
subject.new(sdk_key, Config.new(events_uri: server.base_uri.to_s, logger: $null_log), nil, 0.1)
|
18
|
-
end
|
19
|
-
|
20
|
-
def with_sender_and_server
|
21
|
-
with_server do |server|
|
22
|
-
yield make_sender(server), server
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
it "sends analytics event data" do
|
27
|
-
with_sender_and_server do |es, server|
|
28
|
-
server.setup_ok_response("/bulk", "")
|
29
|
-
|
30
|
-
result = es.send_event_data(fake_data, "", false)
|
31
|
-
|
32
|
-
expect(result.success).to be true
|
33
|
-
expect(result.must_shutdown).to be false
|
34
|
-
expect(result.time_from_server).not_to be_nil
|
35
|
-
|
36
|
-
req = server.await_request
|
37
|
-
expect(req.body).to eq fake_data
|
38
|
-
expect(req.header).to include({
|
39
|
-
"authorization" => [ sdk_key ],
|
40
|
-
"content-type" => [ "application/json" ],
|
41
|
-
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
42
|
-
"x-launchdarkly-event-schema" => [ "3" ],
|
43
|
-
"connection" => [ "Keep-Alive" ]
|
44
|
-
})
|
45
|
-
expect(req.header['x-launchdarkly-payload-id']).not_to eq []
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
it "can use a socket factory" do
|
50
|
-
with_server do |server|
|
51
|
-
server.setup_ok_response("/bulk", "")
|
52
|
-
|
53
|
-
config = Config.new(events_uri: "http://events.com/bulk", socket_factory: SocketFactoryFromHash.new({"events.com" => server.port}), logger: $null_log)
|
54
|
-
es = subject.new(sdk_key, config, nil, 0.1)
|
55
|
-
|
56
|
-
result = es.send_event_data(fake_data, "", false)
|
57
|
-
|
58
|
-
expect(result.success).to be true
|
59
|
-
req = server.await_request
|
60
|
-
expect(req.body).to eq fake_data
|
61
|
-
expect(req.host).to eq "events.com"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
it "generates a new payload ID for each payload" do
|
66
|
-
with_sender_and_server do |es, server|
|
67
|
-
server.setup_ok_response("/bulk", "")
|
68
|
-
|
69
|
-
result1 = es.send_event_data(fake_data, "", false)
|
70
|
-
result2 = es.send_event_data(fake_data, "", false)
|
71
|
-
expect(result1.success).to be true
|
72
|
-
expect(result2.success).to be true
|
73
|
-
|
74
|
-
req1, body1 = server.await_request_with_body
|
75
|
-
req2, body2 = server.await_request_with_body
|
76
|
-
expect(body1).to eq fake_data
|
77
|
-
expect(body2).to eq fake_data
|
78
|
-
expect(req1.header['x-launchdarkly-payload-id']).not_to eq req2.header['x-launchdarkly-payload-id']
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
it "sends diagnostic event data" do
|
83
|
-
with_sender_and_server do |es, server|
|
84
|
-
server.setup_ok_response("/diagnostic", "")
|
85
|
-
|
86
|
-
result = es.send_event_data(fake_data, "", true)
|
87
|
-
|
88
|
-
expect(result.success).to be true
|
89
|
-
expect(result.must_shutdown).to be false
|
90
|
-
expect(result.time_from_server).not_to be_nil
|
91
|
-
|
92
|
-
req, body = server.await_request_with_body
|
93
|
-
expect(body).to eq fake_data
|
94
|
-
expect(req.header).to include({
|
95
|
-
"authorization" => [ sdk_key ],
|
96
|
-
"content-type" => [ "application/json" ],
|
97
|
-
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
98
|
-
"connection" => [ "Keep-Alive" ]
|
99
|
-
})
|
100
|
-
expect(req.header['x-launchdarkly-event-schema']).to eq []
|
101
|
-
expect(req.header['x-launchdarkly-payload-id']).to eq []
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
it "can use a proxy server" do
|
106
|
-
with_server do |server|
|
107
|
-
server.setup_ok_response("/bulk", "")
|
108
|
-
|
109
|
-
with_server(StubProxyServer.new) do |proxy|
|
110
|
-
begin
|
111
|
-
ENV["http_proxy"] = proxy.base_uri.to_s
|
112
|
-
|
113
|
-
es = make_sender(server)
|
114
|
-
|
115
|
-
result = es.send_event_data(fake_data, "", false)
|
116
|
-
|
117
|
-
expect(result.success).to be true
|
118
|
-
|
119
|
-
req, body = server.await_request_with_body
|
120
|
-
expect(body).to eq fake_data
|
121
|
-
ensure
|
122
|
-
ENV["http_proxy"] = nil
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
[400, 408, 429, 500].each do |status|
|
129
|
-
it "handles recoverable error #{status}" do
|
130
|
-
with_sender_and_server do |es, server|
|
131
|
-
req_count = 0
|
132
|
-
server.setup_response("/bulk") do |req, res|
|
133
|
-
req_count = req_count + 1
|
134
|
-
res.status = req_count == 2 ? 200 : status
|
135
|
-
end
|
136
|
-
|
137
|
-
result = es.send_event_data(fake_data, "", false)
|
138
|
-
|
139
|
-
expect(result.success).to be true
|
140
|
-
expect(result.must_shutdown).to be false
|
141
|
-
expect(result.time_from_server).not_to be_nil
|
142
|
-
|
143
|
-
expect(server.requests.count).to eq 2
|
144
|
-
req1, body1 = server.await_request_with_body
|
145
|
-
req2, body2 = server.await_request_with_body
|
146
|
-
expect(body1).to eq fake_data
|
147
|
-
expect(body2).to eq fake_data
|
148
|
-
expect(req1.header['x-launchdarkly-payload-id']).to eq req2.header['x-launchdarkly-payload-id']
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
[400, 408, 429, 500].each do |status|
|
154
|
-
it "only retries error #{status} once" do
|
155
|
-
with_sender_and_server do |es, server|
|
156
|
-
req_count = 0
|
157
|
-
server.setup_response("/bulk") do |req, res|
|
158
|
-
req_count = req_count + 1
|
159
|
-
res.status = req_count == 3 ? 200 : status
|
160
|
-
end
|
161
|
-
|
162
|
-
result = es.send_event_data(fake_data, "", false)
|
163
|
-
|
164
|
-
expect(result.success).to be false
|
165
|
-
expect(result.must_shutdown).to be false
|
166
|
-
expect(result.time_from_server).to be_nil
|
167
|
-
|
168
|
-
expect(server.requests.count).to eq 2
|
169
|
-
req1, body1 = server.await_request_with_body
|
170
|
-
req2, body2 = server.await_request_with_body
|
171
|
-
expect(body1).to eq fake_data
|
172
|
-
expect(body2).to eq fake_data
|
173
|
-
expect(req1.header['x-launchdarkly-payload-id']).to eq req2.header['x-launchdarkly-payload-id']
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
[401, 403].each do |status|
|
179
|
-
it "gives up after unrecoverable error #{status}" do
|
180
|
-
with_sender_and_server do |es, server|
|
181
|
-
server.setup_response("/bulk") do |req, res|
|
182
|
-
res.status = status
|
183
|
-
end
|
184
|
-
|
185
|
-
result = es.send_event_data(fake_data, "", false)
|
186
|
-
|
187
|
-
expect(result.success).to be false
|
188
|
-
expect(result.must_shutdown).to be true
|
189
|
-
expect(result.time_from_server).to be_nil
|
190
|
-
|
191
|
-
expect(server.requests.count).to eq 1
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|