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