launchdarkly-server-sdk 6.2.3 → 6.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/ldclient-rb/config.rb +81 -4
  4. data/lib/ldclient-rb/evaluation_detail.rb +67 -8
  5. data/lib/ldclient-rb/file_data_source.rb +9 -300
  6. data/lib/ldclient-rb/impl/big_segments.rb +117 -0
  7. data/lib/ldclient-rb/impl/evaluator.rb +80 -28
  8. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +82 -18
  9. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
  10. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +84 -31
  11. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
  12. data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
  13. data/lib/ldclient-rb/impl/util.rb +4 -1
  14. data/lib/ldclient-rb/integrations/consul.rb +7 -0
  15. data/lib/ldclient-rb/integrations/dynamodb.rb +47 -2
  16. data/lib/ldclient-rb/integrations/file_data.rb +108 -0
  17. data/lib/ldclient-rb/integrations/redis.rb +41 -1
  18. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +438 -0
  19. data/lib/ldclient-rb/integrations/test_data.rb +209 -0
  20. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +5 -0
  21. data/lib/ldclient-rb/integrations.rb +2 -51
  22. data/lib/ldclient-rb/interfaces.rb +152 -2
  23. data/lib/ldclient-rb/ldclient.rb +21 -7
  24. data/lib/ldclient-rb/polling.rb +22 -41
  25. data/lib/ldclient-rb/util.rb +1 -1
  26. data/lib/ldclient-rb/version.rb +1 -1
  27. metadata +31 -132
  28. data/.circleci/config.yml +0 -40
  29. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
  30. data/.github/ISSUE_TEMPLATE/config.yml +0 -5
  31. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  32. data/.github/pull_request_template.md +0 -21
  33. data/.gitignore +0 -16
  34. data/.hound.yml +0 -2
  35. data/.ldrelease/build-docs.sh +0 -18
  36. data/.ldrelease/circleci/linux/execute.sh +0 -18
  37. data/.ldrelease/circleci/mac/execute.sh +0 -18
  38. data/.ldrelease/circleci/template/build.sh +0 -29
  39. data/.ldrelease/circleci/template/publish.sh +0 -23
  40. data/.ldrelease/circleci/template/set-gem-home.sh +0 -7
  41. data/.ldrelease/circleci/template/test.sh +0 -10
  42. data/.ldrelease/circleci/template/update-version.sh +0 -8
  43. data/.ldrelease/circleci/windows/execute.ps1 +0 -19
  44. data/.ldrelease/config.yml +0 -29
  45. data/.rspec +0 -2
  46. data/.rubocop.yml +0 -600
  47. data/.simplecov +0 -4
  48. data/CHANGELOG.md +0 -367
  49. data/CODEOWNERS +0 -1
  50. data/CONTRIBUTING.md +0 -37
  51. data/Gemfile +0 -3
  52. data/azure-pipelines.yml +0 -51
  53. data/docs/Makefile +0 -26
  54. data/docs/index.md +0 -9
  55. data/launchdarkly-server-sdk.gemspec +0 -45
  56. data/spec/config_spec.rb +0 -63
  57. data/spec/diagnostic_events_spec.rb +0 -165
  58. data/spec/evaluation_detail_spec.rb +0 -135
  59. data/spec/event_sender_spec.rb +0 -197
  60. data/spec/event_summarizer_spec.rb +0 -63
  61. data/spec/events_spec.rb +0 -607
  62. data/spec/expiring_cache_spec.rb +0 -76
  63. data/spec/feature_store_spec_base.rb +0 -213
  64. data/spec/file_data_source_spec.rb +0 -283
  65. data/spec/fixtures/feature.json +0 -37
  66. data/spec/fixtures/feature1.json +0 -36
  67. data/spec/fixtures/user.json +0 -9
  68. data/spec/flags_state_spec.rb +0 -81
  69. data/spec/http_util.rb +0 -132
  70. data/spec/impl/evaluator_bucketing_spec.rb +0 -216
  71. data/spec/impl/evaluator_clause_spec.rb +0 -55
  72. data/spec/impl/evaluator_operators_spec.rb +0 -141
  73. data/spec/impl/evaluator_rule_spec.rb +0 -128
  74. data/spec/impl/evaluator_segment_spec.rb +0 -125
  75. data/spec/impl/evaluator_spec.rb +0 -349
  76. data/spec/impl/evaluator_spec_base.rb +0 -75
  77. data/spec/impl/event_factory_spec.rb +0 -108
  78. data/spec/impl/model/serialization_spec.rb +0 -41
  79. data/spec/in_memory_feature_store_spec.rb +0 -12
  80. data/spec/integrations/consul_feature_store_spec.rb +0 -40
  81. data/spec/integrations/dynamodb_feature_store_spec.rb +0 -103
  82. data/spec/integrations/store_wrapper_spec.rb +0 -276
  83. data/spec/launchdarkly-server-sdk_spec.rb +0 -13
  84. data/spec/launchdarkly-server-sdk_spec_autoloadtest.rb +0 -9
  85. data/spec/ldclient_end_to_end_spec.rb +0 -157
  86. data/spec/ldclient_spec.rb +0 -635
  87. data/spec/newrelic_spec.rb +0 -5
  88. data/spec/polling_spec.rb +0 -120
  89. data/spec/redis_feature_store_spec.rb +0 -121
  90. data/spec/requestor_spec.rb +0 -209
  91. data/spec/segment_store_spec_base.rb +0 -95
  92. data/spec/simple_lru_cache_spec.rb +0 -24
  93. data/spec/spec_helper.rb +0 -9
  94. data/spec/store_spec.rb +0 -10
  95. data/spec/stream_spec.rb +0 -45
  96. data/spec/user_filter_spec.rb +0 -91
  97. data/spec/util_spec.rb +0 -17
  98. data/spec/version_spec.rb +0 -7
@@ -1,635 +0,0 @@
1
- require "spec_helper"
2
-
3
-
4
- describe LaunchDarkly::LDClient do
5
- subject { LaunchDarkly::LDClient }
6
- let(:offline_config) { LaunchDarkly::Config.new({offline: true}) }
7
- let(:offline_client) do
8
- subject.new("secret", offline_config)
9
- end
10
- let(:null_data) { LaunchDarkly::NullUpdateProcessor.new }
11
- let(:logger) { double().as_null_object }
12
- let(:config) { LaunchDarkly::Config.new({ send_events: false, data_source: null_data, logger: logger }) }
13
- let(:client) do
14
- subject.new("secret", config)
15
- end
16
- let(:feature) do
17
- data = File.read(File.join("spec", "fixtures", "feature.json"))
18
- JSON.parse(data, symbolize_names: true)
19
- end
20
- let(:user) do
21
- {
22
- key: "user@test.com",
23
- custom: {
24
- groups: [ "microsoft", "google" ]
25
- }
26
- }
27
- end
28
- let(:user_anonymous) do
29
- {
30
- key: "anonymous@test.com",
31
- anonymous: true
32
- }
33
- end
34
- let(:numeric_key_user) do
35
- {
36
- key: 33,
37
- custom: {
38
- groups: [ "microsoft", "google" ]
39
- }
40
- }
41
- end
42
- let(:sanitized_numeric_key_user) do
43
- {
44
- key: "33",
45
- custom: {
46
- groups: [ "microsoft", "google" ]
47
- }
48
- }
49
- end
50
- let(:user_without_key) do
51
- { name: "Keyless Joe" }
52
- end
53
-
54
- def event_processor
55
- client.instance_variable_get(:@event_processor)
56
- end
57
-
58
- describe "constructor requirement of non-nil sdk key" do
59
- it "is not enforced when offline" do
60
- subject.new(nil, offline_config)
61
- end
62
-
63
- it "is not enforced if use_ldd is true and send_events is false" do
64
- subject.new(nil, LaunchDarkly::Config.new({ use_ldd: true, send_events: false }))
65
- end
66
-
67
- it "is not enforced if using file data and send_events is false" do
68
- source = LaunchDarkly::FileDataSource.factory({})
69
- subject.new(nil, LaunchDarkly::Config.new({ data_source: source, send_events: false }))
70
- end
71
-
72
- it "is enforced in streaming mode even if send_events is false" do
73
- expect {
74
- subject.new(nil, LaunchDarkly::Config.new({ send_events: false }))
75
- }.to raise_error(ArgumentError)
76
- end
77
-
78
- it "is enforced in polling mode even if send_events is false" do
79
- expect {
80
- subject.new(nil, LaunchDarkly::Config.new({ stream: false, send_events: false }))
81
- }.to raise_error(ArgumentError)
82
- end
83
-
84
- it "is enforced if use_ldd is true and send_events is true" do
85
- expect {
86
- subject.new(nil, LaunchDarkly::Config.new({ use_ldd: true }))
87
- }.to raise_error(ArgumentError)
88
- end
89
-
90
- it "is enforced if using file data and send_events is true" do
91
- source = LaunchDarkly::FileDataSource.factory({})
92
- expect {
93
- subject.new(nil, LaunchDarkly::Config.new({ data_source: source }))
94
- }.to raise_error(ArgumentError)
95
- end
96
- end
97
-
98
- describe '#variation' do
99
- feature_with_value = { key: "key", on: false, offVariation: 0, variations: ["value"], version: 100,
100
- trackEvents: true, debugEventsUntilDate: 1000 }
101
-
102
- it "returns the default value if the client is offline" do
103
- result = offline_client.variation("doesntmatter", user, "default")
104
- expect(result).to eq "default"
105
- end
106
-
107
- it "returns the default value for an unknown feature" do
108
- expect(client.variation("badkey", user, "default")).to eq "default"
109
- end
110
-
111
- it "queues a feature request event for an unknown feature" do
112
- expect(event_processor).to receive(:add_event).with(hash_including(
113
- kind: "feature", key: "badkey", user: user, value: "default", default: "default"
114
- ))
115
- client.variation("badkey", user, "default")
116
- end
117
-
118
- it "returns the value for an existing feature" do
119
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
120
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
121
- expect(client.variation("key", user, "default")).to eq "value"
122
- end
123
-
124
- it "returns the default value if a feature evaluates to nil" do
125
- empty_feature = { key: "key", on: false, offVariation: nil }
126
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
127
- config.feature_store.upsert(LaunchDarkly::FEATURES, empty_feature)
128
- expect(client.variation("key", user, "default")).to eq "default"
129
- end
130
-
131
- it "queues a feature request event for an existing feature" do
132
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
133
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
134
- expect(event_processor).to receive(:add_event).with(hash_including(
135
- kind: "feature",
136
- key: "key",
137
- version: 100,
138
- user: user,
139
- variation: 0,
140
- value: "value",
141
- default: "default",
142
- trackEvents: true,
143
- debugEventsUntilDate: 1000
144
- ))
145
- client.variation("key", user, "default")
146
- end
147
-
148
- it "does not send an event if user is nil" do
149
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
150
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
151
- expect(event_processor).not_to receive(:add_event)
152
- expect(logger).to receive(:error)
153
- client.variation("key", nil, "default")
154
- end
155
-
156
- it "queues a feature event for an existing feature when user is anonymous" do
157
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
158
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
159
- expect(event_processor).to receive(:add_event).with(hash_including(
160
- kind: "feature",
161
- key: "key",
162
- version: 100,
163
- contextKind: "anonymousUser",
164
- user: user_anonymous,
165
- variation: 0,
166
- value: "value",
167
- default: "default",
168
- trackEvents: true,
169
- debugEventsUntilDate: 1000
170
- ))
171
- client.variation("key", user_anonymous, "default")
172
- end
173
-
174
- it "does not queue a feature event for an existing feature when user key is nil" do
175
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
176
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
177
- bad_user = { name: "Bob" }
178
- expect(event_processor).not_to receive(:add_event)
179
- expect(logger).to receive(:warn)
180
- client.variation("key", bad_user, "default")
181
- end
182
-
183
- it "sets trackEvents and reason if trackEvents is set for matched rule" do
184
- flag = {
185
- key: 'flag',
186
- on: true,
187
- variations: [ 'value' ],
188
- version: 100,
189
- rules: [
190
- clauses: [
191
- { attribute: 'key', op: 'in', values: [ user[:key] ] }
192
- ],
193
- variation: 0,
194
- id: 'id',
195
- trackEvents: true
196
- ]
197
- }
198
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
199
- config.feature_store.upsert(LaunchDarkly::FEATURES, flag)
200
- expect(event_processor).to receive(:add_event).with(hash_including(
201
- kind: 'feature',
202
- key: 'flag',
203
- version: 100,
204
- user: user,
205
- value: 'value',
206
- default: 'default',
207
- trackEvents: true,
208
- reason: LaunchDarkly::EvaluationReason::rule_match(0, 'id')
209
- ))
210
- client.variation('flag', user, 'default')
211
- end
212
-
213
- it "sets trackEvents and reason if trackEventsFallthrough is set and we fell through" do
214
- flag = {
215
- key: 'flag',
216
- on: true,
217
- variations: [ 'value' ],
218
- fallthrough: { variation: 0 },
219
- version: 100,
220
- rules: [],
221
- trackEventsFallthrough: true
222
- }
223
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
224
- config.feature_store.upsert(LaunchDarkly::FEATURES, flag)
225
- expect(event_processor).to receive(:add_event).with(hash_including(
226
- kind: 'feature',
227
- key: 'flag',
228
- version: 100,
229
- user: user,
230
- value: 'value',
231
- default: 'default',
232
- trackEvents: true,
233
- reason: LaunchDarkly::EvaluationReason::fallthrough
234
- ))
235
- client.variation('flag', user, 'default')
236
- end
237
- end
238
-
239
- describe '#variation_detail' do
240
- feature_with_value = { key: "key", on: false, offVariation: 0, variations: ["value"], version: 100,
241
- trackEvents: true, debugEventsUntilDate: 1000 }
242
-
243
- it "returns the default value if the client is offline" do
244
- result = offline_client.variation_detail("doesntmatter", user, "default")
245
- expected = LaunchDarkly::EvaluationDetail.new("default", nil,
246
- LaunchDarkly::EvaluationReason::error(LaunchDarkly::EvaluationReason::ERROR_CLIENT_NOT_READY))
247
- expect(result).to eq expected
248
- end
249
-
250
- it "returns the default value for an unknown feature" do
251
- result = client.variation_detail("badkey", user, "default")
252
- expected = LaunchDarkly::EvaluationDetail.new("default", nil,
253
- LaunchDarkly::EvaluationReason::error(LaunchDarkly::EvaluationReason::ERROR_FLAG_NOT_FOUND))
254
- expect(result).to eq expected
255
- end
256
-
257
- it "queues a feature request event for an unknown feature" do
258
- expect(event_processor).to receive(:add_event).with(hash_including(
259
- kind: "feature", key: "badkey", user: user, value: "default", default: "default",
260
- reason: LaunchDarkly::EvaluationReason::error(LaunchDarkly::EvaluationReason::ERROR_FLAG_NOT_FOUND)
261
- ))
262
- client.variation_detail("badkey", user, "default")
263
- end
264
-
265
- it "returns a value for an existing feature" do
266
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
267
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
268
- result = client.variation_detail("key", user, "default")
269
- expected = LaunchDarkly::EvaluationDetail.new("value", 0, LaunchDarkly::EvaluationReason::off)
270
- expect(result).to eq expected
271
- end
272
-
273
- it "returns the default value if a feature evaluates to nil" do
274
- empty_feature = { key: "key", on: false, offVariation: nil }
275
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
276
- config.feature_store.upsert(LaunchDarkly::FEATURES, empty_feature)
277
- result = client.variation_detail("key", user, "default")
278
- expected = LaunchDarkly::EvaluationDetail.new("default", nil, LaunchDarkly::EvaluationReason::off)
279
- expect(result).to eq expected
280
- expect(result.default_value?).to be true
281
- end
282
-
283
- it "queues a feature request event for an existing feature" do
284
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
285
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
286
- expect(event_processor).to receive(:add_event).with(hash_including(
287
- kind: "feature",
288
- key: "key",
289
- version: 100,
290
- user: user,
291
- variation: 0,
292
- value: "value",
293
- default: "default",
294
- trackEvents: true,
295
- debugEventsUntilDate: 1000,
296
- reason: LaunchDarkly::EvaluationReason::off
297
- ))
298
- client.variation_detail("key", user, "default")
299
- end
300
-
301
- it "does not send an event if user is nil" do
302
- config.feature_store.init({ LaunchDarkly::FEATURES => {} })
303
- config.feature_store.upsert(LaunchDarkly::FEATURES, feature_with_value)
304
- expect(event_processor).not_to receive(:add_event)
305
- expect(logger).to receive(:error)
306
- client.variation_detail("key", nil, "default")
307
- end
308
- end
309
-
310
- describe '#all_flags' do
311
- let(:flag1) { { key: "key1", offVariation: 0, variations: [ 'value1' ] } }
312
- let(:flag2) { { key: "key2", offVariation: 0, variations: [ 'value2' ] } }
313
-
314
- it "returns flag values" do
315
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
316
-
317
- result = client.all_flags({ key: 'userkey' })
318
- expect(result).to eq({ 'key1' => 'value1', 'key2' => 'value2' })
319
- end
320
-
321
- it "returns empty map for nil user" do
322
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
323
-
324
- result = client.all_flags(nil)
325
- expect(result).to eq({})
326
- end
327
-
328
- it "returns empty map for nil user key" do
329
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
330
-
331
- result = client.all_flags({})
332
- expect(result).to eq({})
333
- end
334
-
335
- it "returns empty map if offline" do
336
- offline_config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
337
-
338
- result = offline_client.all_flags(nil)
339
- expect(result).to eq({})
340
- end
341
- end
342
-
343
- describe '#all_flags_state' do
344
- let(:flag1) { { key: "key1", version: 100, offVariation: 0, variations: [ 'value1' ], trackEvents: false } }
345
- let(:flag2) { { key: "key2", version: 200, offVariation: 1, variations: [ 'x', 'value2' ], trackEvents: true, debugEventsUntilDate: 1000 } }
346
-
347
- it "returns flags state" do
348
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
349
-
350
- state = client.all_flags_state({ key: 'userkey' })
351
- expect(state.valid?).to be true
352
-
353
- values = state.values_map
354
- expect(values).to eq({ 'key1' => 'value1', 'key2' => 'value2' })
355
-
356
- result = state.as_json
357
- expect(result).to eq({
358
- 'key1' => 'value1',
359
- 'key2' => 'value2',
360
- '$flagsState' => {
361
- 'key1' => {
362
- :variation => 0,
363
- :version => 100
364
- },
365
- 'key2' => {
366
- :variation => 1,
367
- :version => 200,
368
- :trackEvents => true,
369
- :debugEventsUntilDate => 1000
370
- }
371
- },
372
- '$valid' => true
373
- })
374
- end
375
-
376
- it "can be filtered for only client-side flags" do
377
- flag1 = { key: "server-side-1", offVariation: 0, variations: [ 'a' ], clientSide: false }
378
- flag2 = { key: "server-side-2", offVariation: 0, variations: [ 'b' ], clientSide: false }
379
- flag3 = { key: "client-side-1", offVariation: 0, variations: [ 'value1' ], clientSide: true }
380
- flag4 = { key: "client-side-2", offVariation: 0, variations: [ 'value2' ], clientSide: true }
381
- config.feature_store.init({ LaunchDarkly::FEATURES => {
382
- flag1[:key] => flag1, flag2[:key] => flag2, flag3[:key] => flag3, flag4[:key] => flag4
383
- }})
384
-
385
- state = client.all_flags_state({ key: 'userkey' }, client_side_only: true)
386
- expect(state.valid?).to be true
387
-
388
- values = state.values_map
389
- expect(values).to eq({ 'client-side-1' => 'value1', 'client-side-2' => 'value2' })
390
- end
391
-
392
- it "can omit details for untracked flags" do
393
- future_time = (Time.now.to_f * 1000).to_i + 100000
394
- flag1 = { key: "key1", version: 100, offVariation: 0, variations: [ 'value1' ], trackEvents: false }
395
- flag2 = { key: "key2", version: 200, offVariation: 1, variations: [ 'x', 'value2' ], trackEvents: true }
396
- flag3 = { key: "key3", version: 300, offVariation: 1, variations: [ 'x', 'value3' ], debugEventsUntilDate: future_time }
397
-
398
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2, 'key3' => flag3 } })
399
-
400
- state = client.all_flags_state({ key: 'userkey' }, { details_only_for_tracked_flags: true })
401
- expect(state.valid?).to be true
402
-
403
- values = state.values_map
404
- expect(values).to eq({ 'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3' })
405
-
406
- result = state.as_json
407
- expect(result).to eq({
408
- 'key1' => 'value1',
409
- 'key2' => 'value2',
410
- 'key3' => 'value3',
411
- '$flagsState' => {
412
- 'key1' => {
413
- :variation => 0
414
- },
415
- 'key2' => {
416
- :variation => 1,
417
- :version => 200,
418
- :trackEvents => true
419
- },
420
- 'key3' => {
421
- :variation => 1,
422
- :version => 300,
423
- :debugEventsUntilDate => future_time
424
- }
425
- },
426
- '$valid' => true
427
- })
428
- end
429
-
430
- it "returns empty state for nil user" do
431
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
432
-
433
- state = client.all_flags_state(nil)
434
- expect(state.valid?).to be false
435
- expect(state.values_map).to eq({})
436
- end
437
-
438
- it "returns empty state for nil user key" do
439
- config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
440
-
441
- state = client.all_flags_state({})
442
- expect(state.valid?).to be false
443
- expect(state.values_map).to eq({})
444
- end
445
-
446
- it "returns empty state if offline" do
447
- offline_config.feature_store.init({ LaunchDarkly::FEATURES => { 'key1' => flag1, 'key2' => flag2 } })
448
-
449
- state = offline_client.all_flags_state({ key: 'userkey' })
450
- expect(state.valid?).to be false
451
- expect(state.values_map).to eq({})
452
- end
453
- end
454
-
455
- describe '#secure_mode_hash' do
456
- it "will return the expected value for a known message and secret" do
457
- result = client.secure_mode_hash({key: :Message})
458
- expect(result).to eq "aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597"
459
- end
460
- end
461
-
462
- describe '#track' do
463
- it "queues up an custom event" do
464
- expect(event_processor).to receive(:add_event).with(hash_including(kind: "custom", key: "custom_event_name", user: user, data: 42))
465
- client.track("custom_event_name", user, 42)
466
- end
467
-
468
- it "can include a metric value" do
469
- expect(event_processor).to receive(:add_event).with(hash_including(
470
- kind: "custom", key: "custom_event_name", user: user, metricValue: 1.5))
471
- client.track("custom_event_name", user, nil, 1.5)
472
- end
473
-
474
- it "includes contextKind with anonymous user" do
475
- expect(event_processor).to receive(:add_event).with(hash_including(
476
- kind: "custom", key: "custom_event_name", user: user_anonymous, metricValue: 2.2, contextKind: "anonymousUser"))
477
- client.track("custom_event_name", user_anonymous, nil, 2.2)
478
- end
479
-
480
- it "sanitizes the user in the event" do
481
- expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
482
- client.track("custom_event_name", numeric_key_user, nil)
483
- end
484
-
485
- it "does not send an event, and logs a warning, if user is nil" do
486
- expect(event_processor).not_to receive(:add_event)
487
- expect(logger).to receive(:warn)
488
- client.track("custom_event_name", nil, nil)
489
- end
490
-
491
- it "does not send an event, and logs a warning, if user key is nil" do
492
- expect(event_processor).not_to receive(:add_event)
493
- expect(logger).to receive(:warn)
494
- client.track("custom_event_name", user_without_key, nil)
495
- end
496
- end
497
-
498
- describe '#alias' do
499
- it "queues up an alias event" do
500
- expect(event_processor).to receive(:add_event).with(hash_including(
501
- kind: "alias", key: user[:key], contextKind: "user", previousKey: user_anonymous[:key], previousContextKind: "anonymousUser"))
502
- client.alias(user, user_anonymous)
503
- end
504
-
505
- it "does not send an event, and logs a warning, if user is nil" do
506
- expect(event_processor).not_to receive(:add_event)
507
- expect(logger).to receive(:warn)
508
- client.alias(nil, nil)
509
- end
510
-
511
- it "does not send an event, and logs a warning, if user key is nil" do
512
- expect(event_processor).not_to receive(:add_event)
513
- expect(logger).to receive(:warn)
514
- client.alias(user_without_key, user_without_key)
515
- end
516
- end
517
-
518
- describe '#identify' do
519
- it "queues up an identify event" do
520
- expect(event_processor).to receive(:add_event).with(hash_including(kind: "identify", key: user[:key], user: user))
521
- client.identify(user)
522
- end
523
-
524
- it "does not send an event, and logs a warning, if user is nil" do
525
- expect(event_processor).not_to receive(:add_event)
526
- expect(logger).to receive(:warn)
527
- client.identify(nil)
528
- end
529
-
530
- it "does not send an event, and logs a warning, if user key is nil" do
531
- expect(event_processor).not_to receive(:add_event)
532
- expect(logger).to receive(:warn)
533
- client.identify(user_without_key)
534
- end
535
- end
536
-
537
- describe 'with send_events: false' do
538
- let(:config) { LaunchDarkly::Config.new({offline: true, send_events: false, data_source: null_data}) }
539
- let(:client) { subject.new("secret", config) }
540
-
541
- it "uses a NullEventProcessor" do
542
- ep = client.instance_variable_get(:@event_processor)
543
- expect(ep).to be_a(LaunchDarkly::NullEventProcessor)
544
- end
545
- end
546
-
547
- describe 'with send_events: true' do
548
- let(:config_with_events) { LaunchDarkly::Config.new({offline: false, send_events: true, diagnostic_opt_out: true, data_source: null_data}) }
549
- let(:client_with_events) { subject.new("secret", config_with_events) }
550
-
551
- it "does not use a NullEventProcessor" do
552
- ep = client_with_events.instance_variable_get(:@event_processor)
553
- expect(ep).not_to be_a(LaunchDarkly::NullEventProcessor)
554
- end
555
- end
556
-
557
- describe "feature store data ordering" do
558
- let(:dependency_ordering_test_data) {
559
- {
560
- LaunchDarkly::FEATURES => {
561
- a: { key: "a", prerequisites: [ { key: "b" }, { key: "c" } ] },
562
- b: { key: "b", prerequisites: [ { key: "c" }, { key: "e" } ] },
563
- c: { key: "c" },
564
- d: { key: "d" },
565
- e: { key: "e" },
566
- f: { key: "f" }
567
- },
568
- LaunchDarkly::SEGMENTS => {
569
- o: { key: "o" }
570
- }
571
- }
572
- }
573
-
574
- class FakeFeatureStore
575
- attr_reader :received_data
576
-
577
- def init(all_data)
578
- @received_data = all_data
579
- end
580
- end
581
-
582
- class FakeUpdateProcessor
583
- def initialize(store, data)
584
- @store = store
585
- @data = data
586
- end
587
-
588
- def start
589
- @store.init(@data)
590
- ev = Concurrent::Event.new
591
- ev.set
592
- ev
593
- end
594
-
595
- def stop
596
- end
597
-
598
- def initialized?
599
- true
600
- end
601
- end
602
-
603
- it "passes data set to feature store in correct order on init" do
604
- store = FakeFeatureStore.new
605
- data_source_factory = lambda { |sdk_key, config| FakeUpdateProcessor.new(config.feature_store,
606
- dependency_ordering_test_data) }
607
- config = LaunchDarkly::Config.new(send_events: false, feature_store: store, data_source: data_source_factory)
608
- client = subject.new("secret", config)
609
-
610
- data = store.received_data
611
- expect(data).not_to be_nil
612
- expect(data.count).to eq(2)
613
-
614
- # Segments should always come first
615
- expect(data.keys[0]).to be(LaunchDarkly::SEGMENTS)
616
- expect(data.values[0].count).to eq(dependency_ordering_test_data[LaunchDarkly::SEGMENTS].count)
617
-
618
- # Features should be ordered so that a flag always appears after its prerequisites, if any
619
- expect(data.keys[1]).to be(LaunchDarkly::FEATURES)
620
- flags_map = data.values[1]
621
- flags_list = flags_map.values
622
- expect(flags_list.count).to eq(dependency_ordering_test_data[LaunchDarkly::FEATURES].count)
623
- flags_list.each_with_index do |item, item_index|
624
- (item[:prerequisites] || []).each do |prereq|
625
- prereq = flags_map[prereq[:key].to_sym]
626
- prereq_index = flags_list.index(prereq)
627
- if prereq_index > item_index
628
- all_keys = (flags_list.map { |f| f[:key] }).join(", ")
629
- raise "#{item[:key]} depends on #{prereq[:key]}, but #{item[:key]} was listed first; keys in order are [#{all_keys}]"
630
- end
631
- end
632
- end
633
- end
634
- end
635
- end
@@ -1,5 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe LaunchDarkly::LDNewRelic do
4
- subject { LaunchDarkly::LDNewRelic }
5
- end