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
data/spec/polling_spec.rb DELETED
@@ -1,120 +0,0 @@
1
- require "spec_helper"
2
- require 'ostruct'
3
-
4
- describe LaunchDarkly::PollingProcessor do
5
- subject { LaunchDarkly::PollingProcessor }
6
- let(:requestor) { double() }
7
-
8
- def with_processor(store)
9
- config = LaunchDarkly::Config.new(feature_store: store, logger: $null_log)
10
- processor = subject.new(config, requestor)
11
- begin
12
- yield processor
13
- ensure
14
- processor.stop
15
- end
16
- end
17
-
18
- describe 'successful request' do
19
- flag = { key: 'flagkey', version: 1 }
20
- segment = { key: 'segkey', version: 1 }
21
- all_data = {
22
- LaunchDarkly::FEATURES => {
23
- flagkey: flag
24
- },
25
- LaunchDarkly::SEGMENTS => {
26
- segkey: segment
27
- }
28
- }
29
-
30
- it 'puts feature data in store' do
31
- allow(requestor).to receive(:request_all_data).and_return(all_data)
32
- store = LaunchDarkly::InMemoryFeatureStore.new
33
- with_processor(store) do |processor|
34
- ready = processor.start
35
- ready.wait
36
- expect(store.get(LaunchDarkly::FEATURES, "flagkey")).to eq(flag)
37
- expect(store.get(LaunchDarkly::SEGMENTS, "segkey")).to eq(segment)
38
- end
39
- end
40
-
41
- it 'sets initialized to true' do
42
- allow(requestor).to receive(:request_all_data).and_return(all_data)
43
- store = LaunchDarkly::InMemoryFeatureStore.new
44
- with_processor(store) do |processor|
45
- ready = processor.start
46
- ready.wait
47
- expect(processor.initialized?).to be true
48
- expect(store.initialized?).to be true
49
- end
50
- end
51
- end
52
-
53
- describe 'connection error' do
54
- it 'does not cause immediate failure, does not set initialized' do
55
- allow(requestor).to receive(:request_all_data).and_raise(StandardError.new("test error"))
56
- store = LaunchDarkly::InMemoryFeatureStore.new
57
- with_processor(store) do |processor|
58
- ready = processor.start
59
- finished = ready.wait(0.2)
60
- expect(finished).to be false
61
- expect(processor.initialized?).to be false
62
- expect(store.initialized?).to be false
63
- end
64
- end
65
- end
66
-
67
- describe 'HTTP errors' do
68
- def verify_unrecoverable_http_error(status)
69
- allow(requestor).to receive(:request_all_data).and_raise(LaunchDarkly::UnexpectedResponseError.new(status))
70
- with_processor(LaunchDarkly::InMemoryFeatureStore.new) do |processor|
71
- ready = processor.start
72
- finished = ready.wait(0.2)
73
- expect(finished).to be true
74
- expect(processor.initialized?).to be false
75
- end
76
- end
77
-
78
- def verify_recoverable_http_error(status)
79
- allow(requestor).to receive(:request_all_data).and_raise(LaunchDarkly::UnexpectedResponseError.new(status))
80
- with_processor(LaunchDarkly::InMemoryFeatureStore.new) do |processor|
81
- ready = processor.start
82
- finished = ready.wait(0.2)
83
- expect(finished).to be false
84
- expect(processor.initialized?).to be false
85
- end
86
- end
87
-
88
- it 'stops immediately for error 401' do
89
- verify_unrecoverable_http_error(401)
90
- end
91
-
92
- it 'stops immediately for error 403' do
93
- verify_unrecoverable_http_error(403)
94
- end
95
-
96
- it 'does not stop immediately for error 408' do
97
- verify_recoverable_http_error(408)
98
- end
99
-
100
- it 'does not stop immediately for error 429' do
101
- verify_recoverable_http_error(429)
102
- end
103
-
104
- it 'does not stop immediately for error 503' do
105
- verify_recoverable_http_error(503)
106
- end
107
- end
108
-
109
- describe 'stop' do
110
- it 'stops promptly rather than continuing to wait for poll interval' do
111
- with_processor(LaunchDarkly::InMemoryFeatureStore.new) do |processor|
112
- sleep(1) # somewhat arbitrary, but should ensure that it has started polling
113
- start_time = Time.now
114
- processor.stop
115
- end_time = Time.now
116
- expect(end_time - start_time).to be <(LaunchDarkly::Config.default_poll_interval - 5)
117
- end
118
- end
119
- end
120
- end
@@ -1,121 +0,0 @@
1
- require "feature_store_spec_base"
2
- require "connection_pool"
3
- require "json"
4
- require "redis"
5
- require "spec_helper"
6
-
7
-
8
- $my_prefix = 'testprefix'
9
-
10
- $base_opts = {
11
- prefix: $my_prefix,
12
- logger: $null_log
13
- }
14
-
15
- def create_redis_store(opts = {})
16
- LaunchDarkly::RedisFeatureStore.new($base_opts.merge(opts).merge({ expiration: 60 }))
17
- end
18
-
19
- def create_redis_store_uncached(opts = {})
20
- LaunchDarkly::RedisFeatureStore.new($base_opts.merge(opts).merge({ expiration: 0 }))
21
- end
22
-
23
- def clear_all_data
24
- client = Redis.new
25
- client.flushdb
26
- end
27
-
28
-
29
- describe LaunchDarkly::RedisFeatureStore do
30
- subject { LaunchDarkly::RedisFeatureStore }
31
-
32
- break if ENV['LD_SKIP_DATABASE_TESTS'] == '1'
33
-
34
- # These tests will all fail if there isn't a Redis instance running on the default port.
35
-
36
- context "real Redis with local cache" do
37
- include_examples "feature_store", method(:create_redis_store), method(:clear_all_data)
38
- end
39
-
40
- context "real Redis without local cache" do
41
- include_examples "feature_store", method(:create_redis_store_uncached), method(:clear_all_data)
42
- end
43
-
44
- def make_concurrent_modifier_test_hook(other_client, flag, start_version, end_version)
45
- test_hook = Object.new
46
- version_counter = start_version
47
- expect(test_hook).to receive(:before_update_transaction) { |base_key, key|
48
- if version_counter <= end_version
49
- new_flag = flag.clone
50
- new_flag[:version] = version_counter
51
- other_client.hset(base_key, key, new_flag.to_json)
52
- version_counter = version_counter + 1
53
- end
54
- }.at_least(:once)
55
- test_hook
56
- end
57
-
58
- it "handles upsert race condition against external client with lower version" do
59
- other_client = Redis.new({ url: "redis://localhost:6379" })
60
- flag = { key: "foo", version: 1 }
61
- test_hook = make_concurrent_modifier_test_hook(other_client, flag, 2, 4)
62
- store = create_redis_store({ test_hook: test_hook })
63
-
64
- begin
65
- store.init(LaunchDarkly::FEATURES => { flag[:key] => flag })
66
-
67
- my_ver = { key: "foo", version: 10 }
68
- store.upsert(LaunchDarkly::FEATURES, my_ver)
69
- result = store.get(LaunchDarkly::FEATURES, flag[:key])
70
- expect(result[:version]).to eq 10
71
- ensure
72
- other_client.close
73
- end
74
- end
75
-
76
- it "handles upsert race condition against external client with higher version" do
77
- other_client = Redis.new({ url: "redis://localhost:6379" })
78
- flag = { key: "foo", version: 1 }
79
- test_hook = make_concurrent_modifier_test_hook(other_client, flag, 3, 3)
80
- store = create_redis_store({ test_hook: test_hook })
81
-
82
- begin
83
- store.init(LaunchDarkly::FEATURES => { flag[:key] => flag })
84
-
85
- my_ver = { key: "foo", version: 2 }
86
- store.upsert(LaunchDarkly::FEATURES, my_ver)
87
- result = store.get(LaunchDarkly::FEATURES, flag[:key])
88
- expect(result[:version]).to eq 3
89
- ensure
90
- other_client.close
91
- end
92
- end
93
-
94
- it "shuts down a custom Redis pool by default" do
95
- unowned_pool = ConnectionPool.new(size: 1, timeout: 1) { Redis.new({ url: "redis://localhost:6379" }) }
96
- store = create_redis_store({ pool: unowned_pool })
97
-
98
- begin
99
- store.init(LaunchDarkly::FEATURES => { })
100
- store.stop
101
-
102
- expect { unowned_pool.with {} }.to raise_error(ConnectionPool::PoolShuttingDownError)
103
- ensure
104
- unowned_pool.shutdown { |conn| conn.close }
105
- end
106
- end
107
-
108
- it "doesn't shut down a custom Redis pool if pool_shutdown_on_close = false" do
109
- unowned_pool = ConnectionPool.new(size: 1, timeout: 1) { Redis.new({ url: "redis://localhost:6379" }) }
110
- store = create_redis_store({ pool: unowned_pool, pool_shutdown_on_close: false })
111
-
112
- begin
113
- store.init(LaunchDarkly::FEATURES => { })
114
- store.stop
115
-
116
- expect { unowned_pool.with {} }.not_to raise_error(ConnectionPool::PoolShuttingDownError)
117
- ensure
118
- unowned_pool.shutdown { |conn| conn.close }
119
- end
120
- end
121
- end
@@ -1,209 +0,0 @@
1
- require "http_util"
2
- require "spec_helper"
3
-
4
- $sdk_key = "secret"
5
-
6
- describe LaunchDarkly::Requestor do
7
- def with_requestor(base_uri, opts = {})
8
- r = LaunchDarkly::Requestor.new($sdk_key, LaunchDarkly::Config.new({ base_uri: base_uri }.merge(opts)))
9
- begin
10
- yield r
11
- ensure
12
- r.stop
13
- end
14
- end
15
-
16
- describe "request_all_flags" do
17
- it "uses expected URI and headers" do
18
- with_server do |server|
19
- with_requestor(server.base_uri.to_s) do |requestor|
20
- server.setup_ok_response("/", "{}")
21
- requestor.request_all_data()
22
- expect(server.requests.count).to eq 1
23
- expect(server.requests[0].unparsed_uri).to eq "/sdk/latest-all"
24
- expect(server.requests[0].header).to include({
25
- "authorization" => [ $sdk_key ],
26
- "user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ]
27
- })
28
- end
29
- end
30
- end
31
-
32
- it "parses response" do
33
- expected_data = { flags: { x: { key: "x" } } }
34
- with_server do |server|
35
- with_requestor(server.base_uri.to_s) do |requestor|
36
- server.setup_ok_response("/", expected_data.to_json)
37
- data = requestor.request_all_data()
38
- expect(data).to eq LaunchDarkly::Impl::Model.make_all_store_data(expected_data)
39
- end
40
- end
41
- end
42
-
43
- it "logs debug output" do
44
- logger = ::Logger.new($stdout)
45
- logger.level = ::Logger::DEBUG
46
- with_server do |server|
47
- with_requestor(server.base_uri.to_s, { logger: logger }) do |requestor|
48
- server.setup_ok_response("/", { flags: { x: { key: "y" } } }.to_json)
49
- expect do
50
- requestor.request_all_data()
51
- end.to output(/\[LDClient\] Got response from uri\:/).to_stdout_from_any_process
52
- end
53
- end
54
- end
55
-
56
- it "sends etag from previous response" do
57
- etag = "xyz"
58
- with_server do |server|
59
- with_requestor(server.base_uri.to_s) do |requestor|
60
- server.setup_response("/") do |req, res|
61
- res.status = 200
62
- res.body = "{}"
63
- res["ETag"] = etag
64
- end
65
- requestor.request_all_data()
66
- expect(server.requests.count).to eq 1
67
-
68
- requestor.request_all_data()
69
- expect(server.requests.count).to eq 2
70
- expect(server.requests[1].header).to include({ "if-none-match" => [ etag ] })
71
- end
72
- end
73
- end
74
-
75
- it "sends wrapper header if configured" do
76
- with_server do |server|
77
- with_requestor(server.base_uri.to_s, { wrapper_name: 'MyWrapper', wrapper_version: '1.0' }) do |requestor|
78
- server.setup_ok_response("/", "{}")
79
- requestor.request_all_data()
80
- expect(server.requests.count).to eq 1
81
- expect(server.requests[0].header).to include({
82
- "x-launchdarkly-wrapper" => [ "MyWrapper/1.0" ]
83
- })
84
- end
85
- end
86
- end
87
-
88
- it "can reuse cached data" do
89
- etag = "xyz"
90
- expected_data = { flags: { x: { key: "x" } } }
91
- with_server do |server|
92
- with_requestor(server.base_uri.to_s) do |requestor|
93
- server.setup_response("/") do |req, res|
94
- res.status = 200
95
- res.body = expected_data.to_json
96
- res["ETag"] = etag
97
- end
98
- requestor.request_all_data()
99
- expect(server.requests.count).to eq 1
100
-
101
- server.setup_response("/") do |req, res|
102
- res.status = 304
103
- end
104
- data = requestor.request_all_data()
105
- expect(server.requests.count).to eq 2
106
- expect(server.requests[1].header).to include({ "if-none-match" => [ etag ] })
107
- expect(data).to eq LaunchDarkly::Impl::Model.make_all_store_data(expected_data)
108
- end
109
- end
110
- end
111
-
112
- it "replaces cached data with new data" do
113
- etag1 = "abc"
114
- etag2 = "xyz"
115
- expected_data1 = { flags: { x: { key: "x" } } }
116
- expected_data2 = { flags: { y: { key: "y" } } }
117
- with_server do |server|
118
- with_requestor(server.base_uri.to_s) do |requestor|
119
- server.setup_response("/") do |req, res|
120
- res.status = 200
121
- res.body = expected_data1.to_json
122
- res["ETag"] = etag1
123
- end
124
- data = requestor.request_all_data()
125
- expect(data).to eq LaunchDarkly::Impl::Model.make_all_store_data(expected_data1)
126
- expect(server.requests.count).to eq 1
127
-
128
- server.setup_response("/") do |req, res|
129
- res.status = 304
130
- end
131
- data = requestor.request_all_data()
132
- expect(data).to eq LaunchDarkly::Impl::Model.make_all_store_data(expected_data1)
133
- expect(server.requests.count).to eq 2
134
- expect(server.requests[1].header).to include({ "if-none-match" => [ etag1 ] })
135
-
136
- server.setup_response("/") do |req, res|
137
- res.status = 200
138
- res.body = expected_data2.to_json
139
- res["ETag"] = etag2
140
- end
141
- data = requestor.request_all_data()
142
- expect(data).to eq LaunchDarkly::Impl::Model.make_all_store_data(expected_data2)
143
- expect(server.requests.count).to eq 3
144
- expect(server.requests[2].header).to include({ "if-none-match" => [ etag1 ] })
145
-
146
- server.setup_response("/") do |req, res|
147
- res.status = 304
148
- end
149
- data = requestor.request_all_data()
150
- expect(data).to eq LaunchDarkly::Impl::Model.make_all_store_data(expected_data2)
151
- expect(server.requests.count).to eq 4
152
- expect(server.requests[3].header).to include({ "if-none-match" => [ etag2 ] })
153
- end
154
- end
155
- end
156
-
157
- it "uses UTF-8 encoding by default" do
158
- content = '{"flags": {"flagkey": {"key": "flagkey", "variations": ["blue", "grėeń"]}}}'
159
- with_server do |server|
160
- server.setup_ok_response("/sdk/latest-all", content, "application/json")
161
- with_requestor(server.base_uri.to_s) do |requestor|
162
- data = requestor.request_all_data
163
- expect(data).to eq(LaunchDarkly::Impl::Model.make_all_store_data(JSON.parse(content, symbolize_names: true)))
164
- end
165
- end
166
- end
167
-
168
- it "detects other encodings from Content-Type" do
169
- content = '{"flags": {"flagkey": {"key": "flagkey", "variations": ["proszę", "dziękuję"]}}}'
170
- with_server do |server|
171
- server.setup_ok_response("/sdk/latest-all", content.encode(Encoding::ISO_8859_2),
172
- "text/plain; charset=ISO-8859-2")
173
- with_requestor(server.base_uri.to_s) do |requestor|
174
- data = requestor.request_all_data
175
- expect(data).to eq(LaunchDarkly::Impl::Model.make_all_store_data(JSON.parse(content, symbolize_names: true)))
176
- end
177
- end
178
- end
179
-
180
- it "throws exception for error status" do
181
- with_server do |server|
182
- with_requestor(server.base_uri.to_s) do |requestor|
183
- server.setup_response("/") do |req, res|
184
- res.status = 400
185
- end
186
- expect { requestor.request_all_data() }.to raise_error(LaunchDarkly::UnexpectedResponseError)
187
- end
188
- end
189
- end
190
-
191
- it "can use a proxy server" do
192
- expected_data = { flags: { flagkey: { key: "flagkey" } } }
193
- with_server do |server|
194
- server.setup_ok_response("/sdk/latest-all", expected_data.to_json, "application/json", { "etag" => "x" })
195
- with_server(StubProxyServer.new) do |proxy|
196
- begin
197
- ENV["http_proxy"] = proxy.base_uri.to_s
198
- with_requestor(server.base_uri.to_s) do |requestor|
199
- data = requestor.request_all_data
200
- expect(data).to eq(LaunchDarkly::Impl::Model.make_all_store_data(expected_data))
201
- end
202
- ensure
203
- ENV["http_proxy"] = nil
204
- end
205
- end
206
- end
207
- end
208
- end
209
- end
@@ -1,95 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.shared_examples "segment_store" do |create_store_method|
4
-
5
- let(:segment0) {
6
- {
7
- key: "test-segment",
8
- version: 11,
9
- salt: "718ea30a918a4eba8734b57ab1a93227",
10
- rules: []
11
- }
12
- }
13
- let(:key0) { segment0[:key].to_sym }
14
-
15
- let!(:store) do
16
- s = create_store_method.call()
17
- s.init({ key0 => segment0 })
18
- s
19
- end
20
-
21
- def new_version_plus(f, deltaVersion, attrs = {})
22
- f1 = f.clone
23
- f1[:version] = f[:version] + deltaVersion
24
- f1.update(attrs)
25
- f1
26
- end
27
-
28
-
29
- it "is initialized" do
30
- expect(store.initialized?).to eq true
31
- end
32
-
33
- it "can get existing feature with symbol key" do
34
- expect(store.get(key0)).to eq segment0
35
- end
36
-
37
- it "can get existing feature with string key" do
38
- expect(store.get(key0.to_s)).to eq segment0
39
- end
40
-
41
- it "gets nil for nonexisting feature" do
42
- expect(store.get('nope')).to be_nil
43
- end
44
-
45
- it "can get all features" do
46
- feature1 = segment0.clone
47
- feature1[:key] = "test-feature-flag1"
48
- feature1[:version] = 5
49
- feature1[:on] = false
50
- store.upsert(:"test-feature-flag1", feature1)
51
- expect(store.all).to eq ({ key0 => segment0, :"test-feature-flag1" => feature1 })
52
- end
53
-
54
- it "can add new feature" do
55
- feature1 = segment0.clone
56
- feature1[:key] = "test-feature-flag1"
57
- feature1[:version] = 5
58
- feature1[:on] = false
59
- store.upsert(:"test-feature-flag1", feature1)
60
- expect(store.get(:"test-feature-flag1")).to eq feature1
61
- end
62
-
63
- it "can update feature with newer version" do
64
- f1 = new_version_plus(segment0, 1, { on: !segment0[:on] })
65
- store.upsert(key0, f1)
66
- expect(store.get(key0)).to eq f1
67
- end
68
-
69
- it "cannot update feature with same version" do
70
- f1 = new_version_plus(segment0, 0, { on: !segment0[:on] })
71
- store.upsert(key0, f1)
72
- expect(store.get(key0)).to eq segment0
73
- end
74
-
75
- it "cannot update feature with older version" do
76
- f1 = new_version_plus(segment0, -1, { on: !segment0[:on] })
77
- store.upsert(key0, f1)
78
- expect(store.get(key0)).to eq segment0
79
- end
80
-
81
- it "can delete feature with newer version" do
82
- store.delete(key0, segment0[:version] + 1)
83
- expect(store.get(key0)).to be_nil
84
- end
85
-
86
- it "cannot delete feature with same version" do
87
- store.delete(key0, segment0[:version])
88
- expect(store.get(key0)).to eq segment0
89
- end
90
-
91
- it "cannot delete feature with older version" do
92
- store.delete(key0, segment0[:version] - 1)
93
- expect(store.get(key0)).to eq segment0
94
- end
95
- end
@@ -1,24 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe LaunchDarkly::SimpleLRUCacheSet do
4
- subject { LaunchDarkly::SimpleLRUCacheSet }
5
-
6
- it "retains values up to capacity" do
7
- lru = subject.new(3)
8
- expect(lru.add("a")).to be false
9
- expect(lru.add("b")).to be false
10
- expect(lru.add("c")).to be false
11
- expect(lru.add("a")).to be true
12
- expect(lru.add("b")).to be true
13
- expect(lru.add("c")).to be true
14
- end
15
-
16
- it "discards oldest value on overflow" do
17
- lru = subject.new(2)
18
- expect(lru.add("a")).to be false
19
- expect(lru.add("b")).to be false
20
- expect(lru.add("a")).to be true
21
- expect(lru.add("c")).to be false # b is discarded as oldest
22
- expect(lru.add("b")).to be false
23
- end
24
- end
data/spec/spec_helper.rb DELETED
@@ -1,9 +0,0 @@
1
- require "ldclient-rb"
2
-
3
- $null_log = ::Logger.new($stdout)
4
- $null_log.level = ::Logger::FATAL
5
-
6
- RSpec.configure do |config|
7
- config.before(:each) do
8
- end
9
- end
data/spec/store_spec.rb DELETED
@@ -1,10 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe LaunchDarkly::ThreadSafeMemoryStore do
4
- subject { LaunchDarkly::ThreadSafeMemoryStore }
5
- let(:store) { subject.new }
6
- it "can read and write" do
7
- store.write("key", "value")
8
- expect(store.read("key")).to eq "value"
9
- end
10
- end
data/spec/stream_spec.rb DELETED
@@ -1,45 +0,0 @@
1
- require "ld-eventsource"
2
- require "spec_helper"
3
-
4
- describe LaunchDarkly::StreamProcessor do
5
- subject { LaunchDarkly::StreamProcessor }
6
- let(:config) { LaunchDarkly::Config.new }
7
- let(:processor) { subject.new("sdk_key", config) }
8
-
9
- describe '#process_message' do
10
- let(:put_message) { SSE::StreamEvent.new(:put, '{"data":{"flags":{"asdf": {"key": "asdf"}},"segments":{"segkey": {"key": "segkey"}}}}') }
11
- let(:patch_flag_message) { SSE::StreamEvent.new(:patch, '{"path": "/flags/key", "data": {"key": "asdf", "version": 1}}') }
12
- let(:patch_seg_message) { SSE::StreamEvent.new(:patch, '{"path": "/segments/key", "data": {"key": "asdf", "version": 1}}') }
13
- let(:delete_flag_message) { SSE::StreamEvent.new(:delete, '{"path": "/flags/key", "version": 2}') }
14
- let(:delete_seg_message) { SSE::StreamEvent.new(:delete, '{"path": "/segments/key", "version": 2}') }
15
-
16
- it "will accept PUT methods" do
17
- processor.send(:process_message, put_message)
18
- expect(config.feature_store.get(LaunchDarkly::FEATURES, "asdf")).to eq(key: "asdf")
19
- expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "segkey")).to eq(key: "segkey")
20
- end
21
- it "will accept PATCH methods for flags" do
22
- processor.send(:process_message, patch_flag_message)
23
- expect(config.feature_store.get(LaunchDarkly::FEATURES, "asdf")).to eq(key: "asdf", version: 1)
24
- end
25
- it "will accept PATCH methods for segments" do
26
- processor.send(:process_message, patch_seg_message)
27
- expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "asdf")).to eq(key: "asdf", version: 1)
28
- end
29
- it "will accept DELETE methods for flags" do
30
- processor.send(:process_message, patch_flag_message)
31
- processor.send(:process_message, delete_flag_message)
32
- expect(config.feature_store.get(LaunchDarkly::FEATURES, "key")).to eq(nil)
33
- end
34
- it "will accept DELETE methods for segments" do
35
- processor.send(:process_message, patch_seg_message)
36
- processor.send(:process_message, delete_seg_message)
37
- expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "key")).to eq(nil)
38
- end
39
- it "will log a warning if the method is not recognized" do
40
- expect(processor.instance_variable_get(:@config).logger).to receive :warn
41
- processor.send(:process_message, SSE::StreamEvent.new(type: :get, data: "", id: nil))
42
- end
43
- end
44
- end
45
-