ldclient-rb 2.5.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
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
@@ -3,20 +3,23 @@ require 'ostruct'
3
3
 
4
4
  describe LaunchDarkly::InMemoryFeatureStore do
5
5
  subject { LaunchDarkly::InMemoryFeatureStore }
6
+
7
+ include LaunchDarkly
8
+
6
9
  let(:store) { subject.new }
7
10
  let(:key) { :asdf }
8
- let(:feature) { { value: "qwer", version: 0 } }
11
+ let(:feature) { { key: "asdf", value: "qwer", version: 0 } }
9
12
 
10
13
  describe '#all' do
11
14
  it "will get all keys" do
12
- store.upsert(key, feature)
13
- data = store.all
15
+ store.upsert(LaunchDarkly::FEATURES, feature)
16
+ data = store.all(LaunchDarkly::FEATURES)
14
17
  expect(data).to eq(key => feature)
15
18
  end
16
19
  it "will not get deleted keys" do
17
- store.upsert(key, feature)
18
- store.delete(key, 1)
19
- data = store.all
20
+ store.upsert(LaunchDarkly::FEATURES, feature)
21
+ store.delete(LaunchDarkly::FEATURES, key, 1)
22
+ data = store.all(LaunchDarkly::FEATURES)
20
23
  expect(data).to eq({})
21
24
  end
22
25
  end
@@ -37,21 +40,33 @@ describe LaunchDarkly::StreamProcessor do
37
40
  let(:processor) { subject.new("sdk_key", config, requestor) }
38
41
 
39
42
  describe '#process_message' do
40
- let(:put_message) { OpenStruct.new({data: '{"key": {"value": "asdf"}}'}) }
41
- let(:patch_message) { OpenStruct.new({data: '{"path": "akey", "data": {"value": "asdf", "version": 1}}'}) }
42
- let(:delete_message) { OpenStruct.new({data: '{"path": "akey", "version": 2}'}) }
43
+ let(:put_message) { OpenStruct.new({data: '{"data":{"flags":{"asdf": {"key": "asdf"}},"segments":{"segkey": {"key": "segkey"}}}}'}) }
44
+ let(:patch_flag_message) { OpenStruct.new({data: '{"path": "/flags/key", "data": {"key": "asdf", "version": 1}}'}) }
45
+ let(:patch_seg_message) { OpenStruct.new({data: '{"path": "/segments/key", "data": {"key": "asdf", "version": 1}}'}) }
46
+ let(:delete_flag_message) { OpenStruct.new({data: '{"path": "/flags/key", "version": 2}'}) }
47
+ let(:delete_seg_message) { OpenStruct.new({data: '{"path": "/segments/key", "version": 2}'}) }
43
48
  it "will accept PUT methods" do
44
49
  processor.send(:process_message, put_message, LaunchDarkly::PUT)
45
- expect(processor.instance_variable_get(:@store).get("key")).to eq(value: "asdf")
50
+ expect(config.feature_store.get(LaunchDarkly::FEATURES, "asdf")).to eq(key: "asdf")
51
+ expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "segkey")).to eq(key: "segkey")
52
+ end
53
+ it "will accept PATCH methods for flags" do
54
+ processor.send(:process_message, patch_flag_message, LaunchDarkly::PATCH)
55
+ expect(config.feature_store.get(LaunchDarkly::FEATURES, "asdf")).to eq(key: "asdf", version: 1)
56
+ end
57
+ it "will accept PATCH methods for segments" do
58
+ processor.send(:process_message, patch_seg_message, LaunchDarkly::PATCH)
59
+ expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "asdf")).to eq(key: "asdf", version: 1)
46
60
  end
47
- it "will accept PATCH methods" do
48
- processor.send(:process_message, patch_message, LaunchDarkly::PATCH)
49
- expect(processor.instance_variable_get(:@store).get("key")).to eq(value: "asdf", version: 1)
61
+ it "will accept DELETE methods for flags" do
62
+ processor.send(:process_message, patch_flag_message, LaunchDarkly::PATCH)
63
+ processor.send(:process_message, delete_flag_message, LaunchDarkly::DELETE)
64
+ expect(config.feature_store.get(LaunchDarkly::FEATURES, "key")).to eq(nil)
50
65
  end
51
- it "will accept DELETE methods" do
52
- processor.send(:process_message, patch_message, LaunchDarkly::PATCH)
53
- processor.send(:process_message, delete_message, LaunchDarkly::DELETE)
54
- expect(processor.instance_variable_get(:@store).get("key")).to eq(nil)
66
+ it "will accept DELETE methods for segments" do
67
+ processor.send(:process_message, patch_seg_message, LaunchDarkly::PATCH)
68
+ processor.send(:process_message, delete_seg_message, LaunchDarkly::DELETE)
69
+ expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "key")).to eq(nil)
55
70
  end
56
71
  it "will log a warning if the method is not recognized" do
57
72
  expect(processor.instance_variable_get(:@config).logger).to receive :warn
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ldclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LaunchDarkly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-13 00:00:00.000000000 Z
11
+ date: 2018-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -323,12 +323,12 @@ files:
323
323
  - lib/ldclient-rb/evaluation.rb
324
324
  - lib/ldclient-rb/event_serializer.rb
325
325
  - lib/ldclient-rb/events.rb
326
- - lib/ldclient-rb/feature_store.rb
326
+ - lib/ldclient-rb/in_memory_store.rb
327
327
  - lib/ldclient-rb/ldclient.rb
328
328
  - lib/ldclient-rb/memoized_value.rb
329
329
  - lib/ldclient-rb/newrelic.rb
330
330
  - lib/ldclient-rb/polling.rb
331
- - lib/ldclient-rb/redis_feature_store.rb
331
+ - lib/ldclient-rb/redis_store.rb
332
332
  - lib/ldclient-rb/requestor.rb
333
333
  - lib/ldclient-rb/stream.rb
334
334
  - lib/ldclient-rb/version.rb
@@ -347,6 +347,7 @@ files:
347
347
  - spec/newrelic_spec.rb
348
348
  - spec/redis_feature_store_spec.rb
349
349
  - spec/requestor_spec.rb
350
+ - spec/segment_store_spec_base.rb
350
351
  - spec/spec_helper.rb
351
352
  - spec/store_spec.rb
352
353
  - spec/stream_spec.rb
@@ -390,6 +391,7 @@ test_files:
390
391
  - spec/newrelic_spec.rb
391
392
  - spec/redis_feature_store_spec.rb
392
393
  - spec/requestor_spec.rb
394
+ - spec/segment_store_spec_base.rb
393
395
  - spec/spec_helper.rb
394
396
  - spec/store_spec.rb
395
397
  - spec/stream_spec.rb
@@ -1,63 +0,0 @@
1
- require "concurrent/atomics"
2
-
3
- module LaunchDarkly
4
- class InMemoryFeatureStore
5
- def initialize
6
- @features = Hash.new
7
- @lock = Concurrent::ReadWriteLock.new
8
- @initialized = Concurrent::AtomicBoolean.new(false)
9
- end
10
-
11
- def get(key)
12
- @lock.with_read_lock do
13
- f = @features[key.to_sym]
14
- (f.nil? || f[:deleted]) ? nil : f
15
- end
16
- end
17
-
18
- def all
19
- @lock.with_read_lock do
20
- @features.select { |_k, f| not f[:deleted] }
21
- end
22
- end
23
-
24
- def delete(key, version)
25
- @lock.with_write_lock do
26
- old = @features[key.to_sym]
27
-
28
- if !old.nil? && old[:version] < version
29
- old[:deleted] = true
30
- old[:version] = version
31
- @features[key.to_sym] = old
32
- elsif old.nil?
33
- @features[key.to_sym] = { deleted: true, version: version }
34
- end
35
- end
36
- end
37
-
38
- def init(fs)
39
- @lock.with_write_lock do
40
- @features.replace(fs)
41
- @initialized.make_true
42
- end
43
- end
44
-
45
- def upsert(key, feature)
46
- @lock.with_write_lock do
47
- old = @features[key.to_sym]
48
-
49
- if old.nil? || old[:version] < feature[:version]
50
- @features[key.to_sym] = feature
51
- end
52
- end
53
- end
54
-
55
- def initialized?
56
- @initialized.value
57
- end
58
-
59
- def stop
60
- # nothing to do
61
- end
62
- end
63
- end