launchdarkly-server-sdk 6.2.3 → 6.3.1

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