prefab-cloud-ruby 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ require 'prefab-cloud-ruby'
2
+ require 'rack'
3
+ require 'base64'
4
+ require 'json'
5
+
6
+ handler = Rack::Handler::Thin
7
+
8
+ #
9
+ # This is a very lightweight server that allows the compliance harness to excercise the prefab client
10
+ #
11
+ class RackApp
12
+ def call(env)
13
+ props = CGI::parse(env["QUERY_STRING"])
14
+ props = JSON.parse(Base64.decode64(props["props"][0]))
15
+
16
+ key = props["key"]
17
+ namespace = props["namespace"]
18
+ environment = props["environment"]
19
+ user_key = props["user_key"]
20
+ is_feature_flag = !props["feature_flag"].nil?
21
+
22
+ client = Prefab::Client.new(
23
+ api_key: "1-#{environment}-local_development_api_key-SDK", #sets environment
24
+ namespace: namespace,
25
+ )
26
+
27
+ puts "Key #{key}"
28
+ puts "User #{user_key}"
29
+ puts "Environment #{environment}"
30
+ puts "Namespace #{namespace}"
31
+ puts "Props! #{props}"
32
+ puts "is_feature_flag! #{is_feature_flag}"
33
+
34
+ if is_feature_flag
35
+ puts "EVALFF #{key} #{user_key}"
36
+ rtn = client.feature_flag_client.get(key, user_key, []).to_s
37
+ else
38
+ rtn = client.config_client.get(key).to_s
39
+ end
40
+ puts "return #{rtn}"
41
+
42
+ [200, { "Content-Type" => "text/plain" }, rtn]
43
+
44
+ rescue Exception => e
45
+ puts "ERROR #{e.message}"
46
+ puts e.backtrace
47
+ [500, { "Content-Type" => "text/plain" }, e.message]
48
+ end
49
+ end
50
+
51
+ handler.run RackApp.new
@@ -16,26 +16,26 @@ class TestConfigLoader < Minitest::Test
16
16
 
17
17
  def test_highwater
18
18
  assert_equal 0, @loader.highwater_mark
19
- @loader.set(Prefab::ConfigDelta.new(id: 1, key: "sample_int", value: Prefab::ConfigValue.new(int: 456)))
19
+ @loader.set(Prefab::Config.new(id: 1, key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 456))]))
20
20
  assert_equal 1, @loader.highwater_mark
21
21
 
22
- @loader.set(Prefab::ConfigDelta.new(id: 5, key: "sample_int", value: Prefab::ConfigValue.new(int: 456)))
22
+ @loader.set(Prefab::Config.new(id: 5, key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 456))]))
23
23
  assert_equal 5, @loader.highwater_mark
24
- @loader.set(Prefab::ConfigDelta.new(id: 2, key: "sample_int", value: Prefab::ConfigValue.new(int: 456)))
24
+ @loader.set(Prefab::Config.new(id: 2, key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 456))]))
25
25
  assert_equal 5, @loader.highwater_mark
26
26
  end
27
27
 
28
28
  def test_keeps_most_recent
29
29
  assert_equal 0, @loader.highwater_mark
30
- @loader.set(Prefab::ConfigDelta.new(id: 1, key: "sample_int", value: Prefab::ConfigValue.new(int: 1)))
30
+ @loader.set(Prefab::Config.new(id: 1, key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 1))]))
31
31
  assert_equal 1, @loader.highwater_mark
32
32
  should_be :int, 1, "sample_int"
33
33
 
34
- @loader.set(Prefab::ConfigDelta.new(id: 4, key: "sample_int", value: Prefab::ConfigValue.new(int: 4)))
34
+ @loader.set(Prefab::Config.new(id: 4, key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 4))]))
35
35
  assert_equal 4, @loader.highwater_mark
36
36
  should_be :int, 4, "sample_int"
37
37
 
38
- @loader.set(Prefab::ConfigDelta.new(id: 2, key: "sample_int", value: Prefab::ConfigValue.new(int: 2)))
38
+ @loader.set(Prefab::Config.new(id: 2, key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 2))]))
39
39
  assert_equal 4, @loader.highwater_mark
40
40
  should_be :int, 4, "sample_int"
41
41
  end
@@ -43,37 +43,37 @@ class TestConfigLoader < Minitest::Test
43
43
  def test_api_precedence
44
44
  should_be :int, 123, "sample_int"
45
45
 
46
- @loader.set(Prefab::ConfigDelta.new(key: "sample_int", value: Prefab::ConfigValue.new(int: 456)))
46
+ @loader.set(Prefab::Config.new(key: "sample_int", rows: [Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(int: 456))]))
47
47
  should_be :int, 456, "sample_int"
48
48
  end
49
49
 
50
50
  def test_api_deltas
51
51
  val = Prefab::ConfigValue.new(int: 456)
52
- delta = Prefab::ConfigDelta.new(key: "sample_int", value: val)
53
- @loader.set(delta)
52
+ config = Prefab::Config.new(key: "sample_int", rows: [Prefab::ConfigRow.new(value: val)])
53
+ @loader.set(config)
54
54
 
55
- deltas = Prefab::ConfigDeltas.new
56
- deltas.deltas << delta
57
- assert_equal deltas, @loader.get_api_deltas
55
+ configs = Prefab::Configs.new
56
+ configs.configs << config
57
+ assert_equal configs, @loader.get_api_deltas
58
58
  end
59
59
 
60
60
  def test_loading_tombstones_removes_entries
61
61
  val = Prefab::ConfigValue.new(int: 456)
62
- delta = Prefab::ConfigDelta.new(key: "sample_int", value: val)
63
- @loader.set(delta)
62
+ config = Prefab::Config.new(key: "sample_int", rows: [Prefab::ConfigRow.new(value: val)])
63
+ @loader.set(config)
64
64
 
65
- delta = Prefab::ConfigDelta.new(key: "sample_int", value: nil)
66
- @loader.set(delta)
65
+ config = Prefab::Config.new(key: "sample_int", rows: [])
66
+ @loader.set(config)
67
67
 
68
- deltas = Prefab::ConfigDeltas.new
69
- assert_equal deltas, @loader.get_api_deltas
68
+ configs = Prefab::Configs.new
69
+ assert_equal configs, @loader.get_api_deltas
70
70
  end
71
71
 
72
72
  private
73
73
 
74
74
  def should_be(type, value, key)
75
- assert_equal type, @loader.calc_config[key].type
76
- assert_equal value, @loader.calc_config[key].send(type)
75
+ assert_equal type, @loader.calc_config[key].rows[0].value.type
76
+ assert_equal value, @loader.calc_config[key].rows[0].value.send(type)
77
77
  end
78
78
 
79
79
  end
@@ -6,17 +6,55 @@ class TestConfigResolver < Minitest::Test
6
6
  @loader = MockConfigLoader.new
7
7
 
8
8
  loaded_values = {
9
- "projectA:key" => Prefab::ConfigValue.new(string: "valueA"),
10
- "key" => Prefab::ConfigValue.new(string: "value_none"),
11
- "projectB:key" => Prefab::ConfigValue.new(string: "valueB"),
12
- "projectB.subprojectX:key" => Prefab::ConfigValue.new(string: "projectB.subprojectX"),
13
- "projectB.subprojectY:key" => Prefab::ConfigValue.new(string: "projectB.subprojectY"),
14
- "projectB:key2" => Prefab::ConfigValue.new(string: "valueB2")
9
+ "key" => Prefab::Config.new(
10
+ key: "key",
11
+ rows: [
12
+ Prefab::ConfigRow.new(
13
+ value: Prefab::ConfigValue.new(string: "value_no_env_default"),
14
+ ),
15
+ Prefab::ConfigRow.new(
16
+ env_key: "test",
17
+ value: Prefab::ConfigValue.new(string: "value_none"),
18
+ ),
19
+ Prefab::ConfigRow.new(
20
+ env_key: "test",
21
+ namespace: "projectA",
22
+ value: Prefab::ConfigValue.new(string: "valueA"),
23
+ ),
24
+ Prefab::ConfigRow.new(
25
+ env_key: "test",
26
+ namespace: "projectB",
27
+ value: Prefab::ConfigValue.new(string: "valueB"),
28
+ ),
29
+ Prefab::ConfigRow.new(
30
+ env_key: "test",
31
+ namespace: "projectB.subprojectX",
32
+ value: Prefab::ConfigValue.new(string: "projectB.subprojectX"),
33
+ ),
34
+ Prefab::ConfigRow.new(
35
+ env_key: "test",
36
+ namespace: "projectB.subprojectY",
37
+ value: Prefab::ConfigValue.new(string: "projectB.subprojectY"),
38
+ ),
39
+
40
+ ]
41
+ ),
42
+ "key2" => Prefab::Config.new(
43
+ key: "key2",
44
+ rows: [
45
+ value: Prefab::ConfigValue.new(string: "valueB2"),
46
+ ]
47
+ )
15
48
  }
16
49
 
17
50
  @loader.stub :calc_config, loaded_values do
18
- @resolver = Prefab::ConfigResolver.new(MockBaseClient.new, @loader)
19
- assert_equal "value_none", @resolver.get("key")
51
+
52
+ @resolverA = resolver_for_namespace("", @loader, environment: "some_other_env")
53
+ assert_equal "value_no_env_default", @resolverA.get("key")
54
+
55
+ ## below here in the test env
56
+ @resolverA = resolver_for_namespace("", @loader)
57
+ assert_equal "value_none", @resolverA.get("key")
20
58
 
21
59
  @resolverA = resolver_for_namespace("projectA", @loader)
22
60
  assert_equal "valueA", @resolverA.get("key")
@@ -27,7 +65,10 @@ class TestConfigResolver < Minitest::Test
27
65
  @resolverBX = resolver_for_namespace("projectB.subprojectX", @loader)
28
66
  assert_equal "projectB.subprojectX", @resolverBX.get("key")
29
67
 
30
- @resolverUndefinedSubProject = resolver_for_namespace("projectB.subprojectX:subsubQ", @loader)
68
+ @resolverBX = resolver_for_namespace("projectB.subprojectX", @loader)
69
+ assert_equal "valueB2", @resolverBX.get("key2")
70
+
71
+ @resolverUndefinedSubProject = resolver_for_namespace("projectB.subprojectX.subsubQ", @loader)
31
72
  assert_equal "projectB.subprojectX", @resolverBX.get("key")
32
73
 
33
74
  @resolverBX = resolver_for_namespace("projectC", @loader)
@@ -40,30 +81,72 @@ class TestConfigResolver < Minitest::Test
40
81
  @loader = MockConfigLoader.new
41
82
  @loader.stub :calc_config, {} do
42
83
  resolver = Prefab::ConfigResolver.new(MockBaseClient.new, @loader)
43
- assert resolver.send(:starts_with_ns?, "", "a")
44
- assert resolver.send(:starts_with_ns?, "a", "a")
45
- assert resolver.send(:starts_with_ns?, "a", "a.b")
46
- assert !resolver.send(:starts_with_ns?, "a.b", "a")
47
-
48
- assert resolver.send(:starts_with_ns?, "corp", "corp.proj.proja")
49
- assert resolver.send(:starts_with_ns?, "corp.proj", "corp.proj.proja")
50
- assert resolver.send(:starts_with_ns?, "corp.proj.proja", "corp.proj.proja")
51
- assert !resolver.send(:starts_with_ns?, "corp.proj.projb", "corp.proj.proja")
52
-
53
- # corp:a:b is not a real delimited namespace
54
- assert !resolver.send(:starts_with_ns?, "corp", "corp:a:b")
55
- assert resolver.send(:starts_with_ns?, "foo", "foo.baz")
56
- assert resolver.send(:starts_with_ns?, "foo.baz", "foo.baz")
57
- assert !resolver.send(:starts_with_ns?, "foo.baz", "foo.bazz")
84
+ assert_equal [true, 0], resolver.send(:starts_with_ns?, "", "a")
85
+ assert_equal [true, 1], resolver.send(:starts_with_ns?, "a", "a")
86
+ assert_equal [true, 1], resolver.send(:starts_with_ns?, "a", "a.b")
87
+ assert_equal [false, 2], resolver.send(:starts_with_ns?, "a.b", "a")
88
+
89
+ assert_equal [true, 1], resolver.send(:starts_with_ns?, "corp", "corp.proj.proja")
90
+ assert_equal [true, 2], resolver.send(:starts_with_ns?, "corp.proj", "corp.proj.proja")
91
+ assert_equal [true, 3], resolver.send(:starts_with_ns?, "corp.proj.proja", "corp.proj.proja")
92
+ assert_equal [false, 3], resolver.send(:starts_with_ns?, "corp.proj.projb", "corp.proj.proja")
93
+
94
+ # corp_equal [true, 1:,a:b is not a real delimited namespace[0
95
+ assert_equal [false, 1], resolver.send(:starts_with_ns?, "corp", "corp:a:b")
96
+ assert_equal [true, 1], resolver.send(:starts_with_ns?, "foo", "foo.baz")
97
+ assert_equal [true, 2], resolver.send(:starts_with_ns?, "foo.baz", "foo.baz")
98
+ assert_equal [false, 2], resolver.send(:starts_with_ns?, "foo.baz", "foo.bazz")
99
+ end
100
+ end
101
+
102
+ def test_special_ff_variant_copying
103
+ @loader = MockConfigLoader.new
104
+ loaded_values = {
105
+ "ff" => Prefab::Config.new(
106
+ key: "ff",
107
+ variants: [
108
+ Prefab::FeatureFlagVariant.new(string: "inactive"),
109
+ Prefab::FeatureFlagVariant.new(string: "default"),
110
+ Prefab::FeatureFlagVariant.new(string: "env"),
111
+ ],
112
+ rows: [
113
+ { value: Prefab::ConfigValue.new(feature_flag: Prefab::FeatureFlag.new(
114
+ inactive_variant_idx: 0,
115
+ default: Prefab::VariantDistribution.new(variant_idx: 1)
116
+ )) },
117
+ { env_key: "test",
118
+ value: Prefab::ConfigValue.new(feature_flag: Prefab::FeatureFlag.new(
119
+ inactive_variant_idx: 0,
120
+ default: Prefab::VariantDistribution.new(variant_idx: 2)
121
+ )) }
122
+ ]
123
+ )
124
+ }
125
+ @loader.stub :calc_config, loaded_values do
126
+ resolver = Prefab::ConfigResolver.new(MockBaseClient.new, @loader)
127
+ config = resolver.get_config("ff")
128
+ assert_equal 3, config.variants.size
129
+ assert_equal %w(inactive default env), config.variants.map(&:string)
58
130
  end
59
131
  end
60
132
 
61
133
  # colons are not allowed in keys, but verify behavior anyway
62
- def test_keys_with_colons
134
+ def test_key_and_namespaces_with_colons
63
135
  @loader = MockConfigLoader.new
136
+
64
137
  loaded_values = {
65
- "Key:With:Colons" => Prefab::ConfigValue.new(string: "value"),
66
- "proj:apikey" => Prefab::ConfigValue.new(string: "v2")
138
+ "Key:With:Colons" => Prefab::Config.new(
139
+ key: "Key:With:Colons",
140
+ rows: [Prefab::ConfigRow.new(
141
+ value: Prefab::ConfigValue.new(string: "value")
142
+ )]
143
+ ),
144
+ "proj:apikey" => Prefab::Config.new(
145
+ key: "proj:apikey",
146
+ rows: [Prefab::ConfigRow.new(
147
+ value: Prefab::ConfigValue.new(string: "v2")
148
+ )]
149
+ )
67
150
  }
68
151
 
69
152
  @loader.stub :calc_config, loaded_values do
@@ -72,37 +155,36 @@ class TestConfigResolver < Minitest::Test
72
155
  assert_nil r.get("apikey")
73
156
 
74
157
  r = resolver_for_namespace("proj", @loader)
75
- assert_equal "v2", r.get("apikey")
158
+ assert_nil r.get("apikey")
76
159
 
77
160
  r = resolver_for_namespace("", @loader)
78
161
  assert_nil r.get("apikey")
79
162
 
80
-
81
163
  @resolverKeyWith = resolver_for_namespace("Ket:With", @loader)
82
164
  assert_nil @resolverKeyWith.get("Colons")
83
165
  assert_nil @resolverKeyWith.get("With:Colons")
84
- assert_nil @resolverKeyWith.get("Key:With:Colons")
166
+ assert_equal "value", @resolverKeyWith.get("Key:With:Colons")
85
167
 
86
168
  @resolverKeyWithExtra = resolver_for_namespace("Key:With:Extra", @loader)
87
169
  puts @resolverKeyWithExtra.to_s
88
170
  assert_nil @resolverKeyWithExtra.get("Colons")
89
171
  assert_nil @resolverKeyWithExtra.get("With:Colons")
90
- assert_nil @resolverKeyWithExtra.get("Key:With:Colons")
172
+ assert_equal "value", @resolverKeyWithExtra.get("Key:With:Colons")
91
173
 
92
174
  @resolverKey = resolver_for_namespace("Key", @loader)
93
- assert_equal "value", @resolverKey.get("With:Colons")
175
+ assert_nil @resolverKey.get("With:Colons")
94
176
  assert_nil @resolverKey.get("Colons")
95
- assert_nil @resolverKey.get("Key:With:Colons")
177
+ assert_equal "value", @resolverKey.get("Key:With:Colons")
96
178
 
97
179
  @resolverWithProperlySegmentedNamespace = resolver_for_namespace("Key.With.Extra", @loader)
98
180
  assert_nil @resolverWithProperlySegmentedNamespace.get("Colons")
99
- assert_equal "value", @resolverWithProperlySegmentedNamespace.get("With:Colons")
100
- assert_nil @resolverWithProperlySegmentedNamespace.get("Key:With:Colons")
181
+ assert_nil @resolverWithProperlySegmentedNamespace.get("With:Colons")
182
+ assert_equal "value", @resolverWithProperlySegmentedNamespace.get("Key:With:Colons")
101
183
  end
102
184
  end
103
185
 
104
- def resolver_for_namespace(namespace, loader)
105
- Prefab::ConfigResolver.new(MockBaseClient.new(namespace: namespace), loader)
186
+ def resolver_for_namespace(namespace, loader, environment: "test")
187
+ Prefab::ConfigResolver.new(MockBaseClient.new(namespace: namespace, environment: environment), loader)
106
188
  end
107
189
 
108
190
  end
@@ -2,63 +2,219 @@ require 'test_helper'
2
2
 
3
3
  class TestFeatureFlagClient < Minitest::Test
4
4
 
5
+ def setup
6
+ super
7
+ @mock_base_client = MockBaseClient.new
8
+ @client = Prefab::FeatureFlagClient.new(@mock_base_client)
9
+ Prefab::FeatureFlagClient.send(:public, :is_on?) #publicize for testing
10
+ Prefab::FeatureFlagClient.send(:public, :segment_match?) #publicize for testing
11
+ end
12
+
5
13
  def test_pct
6
- client = Prefab::FeatureFlagClient.new(MockBaseClient.new)
7
- Prefab::FeatureFlagClient.send(:public, :is_on?)
8
14
  feature = "FlagName"
9
- flag = Prefab::FeatureFlag.new( pct: 0.5)
15
+
16
+ variants = [
17
+ Prefab::FeatureFlagVariant.new(bool: false),
18
+ Prefab::FeatureFlagVariant.new(bool: true)
19
+ ]
20
+ flag = Prefab::FeatureFlag.new(
21
+ active: true,
22
+ inactive_variant_idx: 0,
23
+ default: Prefab::VariantDistribution.new(variant_weights:
24
+ Prefab::VariantWeights.new(weights: [
25
+ Prefab::VariantWeight.new(weight: 500,
26
+ variant_idx: 1),
27
+ Prefab::VariantWeight.new(weight: 500,
28
+ variant_idx: 0),
29
+ ]
30
+ )
31
+ )
32
+ )
10
33
 
11
34
  assert_equal false,
12
- client.is_on?(feature, "hashes high", [], flag)
35
+ @client.evaluate(feature, "hashes high", [], flag, variants)
36
+ assert_equal true,
37
+ @client.evaluate(feature, "hashes low", [], flag, variants)
38
+ end
13
39
 
40
+ def test_basic_active_inactive
41
+ feature = "FlagName"
42
+ variants = [
43
+ Prefab::FeatureFlagVariant.new(bool: false),
44
+ Prefab::FeatureFlagVariant.new(bool: true)
45
+ ]
46
+ flag = Prefab::FeatureFlag.new(
47
+ active: true,
48
+ inactive_variant_idx: 0,
49
+ default: Prefab::VariantDistribution.new(variant_idx: 1)
50
+ )
51
+ assert_equal true,
52
+ @client.evaluate(feature, "hashes high", [], flag, variants)
14
53
  assert_equal true,
15
- client.is_on?(feature, "hashes low", [], flag)
54
+ @client.evaluate(feature, "hashes low", [], flag, variants)
55
+
56
+ variants = [
57
+ Prefab::FeatureFlagVariant.new(bool: false),
58
+ Prefab::FeatureFlagVariant.new(bool: true)
59
+ ]
60
+ flag = Prefab::FeatureFlag.new(
61
+ active: false,
62
+ inactive_variant_idx: 0,
63
+ default: Prefab::VariantDistribution.new(variant_idx: 1)
64
+ )
65
+ assert_equal false,
66
+ @client.evaluate(feature, "hashes high", [], flag, variants)
67
+ assert_equal false,
68
+ @client.evaluate(feature, "hashes low", [], flag, variants)
16
69
  end
17
70
 
71
+ def test_user_targets
18
72
 
19
- def test_off
20
- client = Prefab::FeatureFlagClient.new(MockBaseClient.new)
21
- Prefab::FeatureFlagClient.send(:public, :is_on?)
22
73
  feature = "FlagName"
23
- flag = Prefab::FeatureFlag.new(pct: 0)
74
+ variants = [
75
+ Prefab::FeatureFlagVariant.new(string: "inactive"),
76
+ Prefab::FeatureFlagVariant.new(string: "user target"),
77
+ Prefab::FeatureFlagVariant.new(string: "default"),
78
+ ]
79
+ flag = Prefab::FeatureFlag.new(
80
+ active: true,
81
+ inactive_variant_idx: 0,
82
+ user_targets: [
83
+ variant_idx: 1,
84
+ identifiers: ["user:1", "user:3"]
85
+ ],
86
+ default: Prefab::VariantDistribution.new(variant_idx: 2)
87
+ )
24
88
 
25
- assert_equal false,
26
- client.is_on?(feature, "hashes high", [], flag)
89
+ assert_equal "user target",
90
+ @client.evaluate(feature, "user:1", [], flag, variants)
91
+ assert_equal "default",
92
+ @client.evaluate(feature, "user:2", [], flag, variants)
93
+ assert_equal "user target",
94
+ @client.evaluate(feature, "user:3", [], flag, variants)
95
+ end
96
+
97
+ def test_inclusion_rule
98
+ feature = "FlagName"
99
+ variants = [
100
+ Prefab::FeatureFlagVariant.new(string: "inactive"),
101
+ Prefab::FeatureFlagVariant.new(string: "rule target"),
102
+ Prefab::FeatureFlagVariant.new(string: "default"),
103
+ ]
104
+ flag = Prefab::FeatureFlag.new(
105
+ active: true,
106
+ inactive_variant_idx: 0,
107
+ rules: [Prefab::Rule.new(
108
+ distribution: Prefab::VariantDistribution.new(variant_idx: 1),
109
+ criteria: Prefab::Criteria.new(
110
+ operator: "IN",
111
+ values: ["user:1"]
112
+ )
113
+ )],
114
+ default: Prefab::VariantDistribution.new(variant_idx: 2)
115
+ )
116
+
117
+ assert_equal "rule target",
118
+ @client.evaluate(feature, "user:1", [], flag, variants)
119
+ assert_equal "default",
120
+ @client.evaluate(feature, "user:2", [], flag, variants)
27
121
 
28
- assert_equal false,
29
- client.is_on?(feature, "hashes low", [], flag)
30
122
  end
31
123
 
124
+ def test_segment_match?
125
+ segment = Prefab::Segment.new(
126
+ name: "Beta Group",
127
+ includes: ["user:1", "user:5"],
128
+ excludes: ["user:1", "user:2"]
129
+ )
130
+ assert_equal false, @client.segment_match?(segment, "user:0", {})
131
+ assert_equal false, @client.segment_match?(segment, "user:1", {})
132
+ assert_equal false, @client.segment_match?(segment, "user:2", {})
133
+ assert_equal true, @client.segment_match?(segment, "user:5", {})
134
+ end
135
+
136
+ def test_segments
137
+ segment_key = "prefab-segment-beta-group"
138
+ @mock_base_client.mock_this_config(segment_key,
139
+ Prefab::Segment.new(
140
+ name: "Beta Group",
141
+ includes: ["user:1"]
142
+ )
143
+ )
32
144
 
33
- def test_on
34
- client = Prefab::FeatureFlagClient.new(MockBaseClient.new)
35
- Prefab::FeatureFlagClient.send(:public, :is_on?)
36
145
  feature = "FlagName"
37
- flag = Prefab::FeatureFlag.new(pct: 1)
146
+ variants = [
147
+ Prefab::FeatureFlagVariant.new(string: "inactive"),
148
+ Prefab::FeatureFlagVariant.new(string: "rule target"),
149
+ Prefab::FeatureFlagVariant.new(string: "default"),
150
+ ]
151
+ flag = Prefab::FeatureFlag.new(
152
+ active: true,
153
+ inactive_variant_idx: 0,
154
+ rules: [Prefab::Rule.new(
155
+ distribution: Prefab::VariantDistribution.new(variant_idx: 1),
156
+ criteria: Prefab::Criteria.new(
157
+ operator: "IN_SEG",
158
+ values: [segment_key]
159
+ )
160
+ )],
161
+ default: Prefab::VariantDistribution.new(variant_idx: 2)
162
+ )
38
163
 
39
- assert_equal true,
40
- client.is_on?(feature, "hashes high", [], flag)
164
+ assert_equal "rule target",
165
+ @client.evaluate(feature, "user:1", [], flag, variants)
166
+ assert_equal "default",
167
+ @client.evaluate(feature, "user:2", [], flag, variants)
41
168
 
42
- assert_equal true,
43
- client.is_on?(feature, "hashes low", [], flag)
44
169
  end
45
170
 
46
- def test_whitelist
47
- client = Prefab::FeatureFlagClient.new(MockBaseClient.new)
48
- Prefab::FeatureFlagClient.send(:public, :is_on?)
171
+ def test_in_multiple_segments_has_or_behavior
172
+ segment_key_one = "prefab-segment-segment-1"
173
+ @mock_base_client.mock_this_config(segment_key_one,
174
+ Prefab::Segment.new(
175
+ name: "Segment-1",
176
+ includes: ["user:1", "user:2"],
177
+ excludes: ["user:3"]
178
+ )
179
+ )
180
+ segment_key_two = "prefab-segment-segment-2"
181
+ @mock_base_client.mock_this_config(segment_key_two,
182
+ Prefab::Segment.new(
183
+ name: "Segment-2",
184
+ includes: ["user:3", "user:4"],
185
+ excludes: ["user:2"]
186
+ )
187
+ )
188
+
49
189
  feature = "FlagName"
50
- flag = Prefab::FeatureFlag.new(pct: 0, whitelisted: ["beta", "user:1", "user:3"])
190
+ variants = [
191
+ Prefab::FeatureFlagVariant.new(string: "inactive"),
192
+ Prefab::FeatureFlagVariant.new(string: "rule target"),
193
+ Prefab::FeatureFlagVariant.new(string: "default"),
194
+ ]
195
+ flag = Prefab::FeatureFlag.new(
196
+ active: true,
197
+ inactive_variant_idx: 0,
198
+ rules: [Prefab::Rule.new(
199
+ distribution: Prefab::VariantDistribution.new(variant_idx: 1),
200
+ criteria: Prefab::Criteria.new(
201
+ operator: "IN_SEG",
202
+ values: [segment_key_one, segment_key_two]
203
+ )
204
+ )],
205
+ default: Prefab::VariantDistribution.new(variant_idx: 2)
206
+ )
51
207
 
52
- assert_equal false,
53
- client.is_on?(feature, "anything", [], flag)
54
- assert_equal true,
55
- client.is_on?(feature, "anything", ["beta"], flag)
56
- assert_equal true,
57
- client.is_on?(feature, "anything", ["alpha", "beta"], flag)
58
- assert_equal true,
59
- client.is_on?(feature, "anything", ["alpha", "user:1"], flag)
60
- assert_equal false,
61
- client.is_on?(feature, "anything", ["alpha", "user:2"], flag)
208
+ assert_equal "rule target",
209
+ @client.evaluate(feature, "user:1", [], flag, variants)
210
+ assert_equal "rule target",
211
+ @client.evaluate(feature, "user:2", [], flag, variants), "matches segment 1"
212
+ assert_equal "rule target",
213
+ @client.evaluate(feature, "user:3", [], flag, variants)
214
+ assert_equal "rule target",
215
+ @client.evaluate(feature, "user:4", [], flag, variants)
216
+ assert_equal "default",
217
+ @client.evaluate(feature, "user:5", [], flag, variants)
62
218
 
63
219
  end
64
220
  end
data/test/test_helper.rb CHANGED
@@ -2,19 +2,34 @@ require 'minitest/autorun'
2
2
  require 'prefab-cloud-ruby'
3
3
 
4
4
  class MockBaseClient
5
- attr_reader :namespace, :logger
5
+ attr_reader :namespace, :logger, :environment
6
6
 
7
- def initialize(namespace: "")
7
+ def initialize(environment: "test", namespace: "")
8
+ @environment = environment
8
9
  @namespace = namespace
9
10
  @logger = Logger.new($stdout)
11
+ @config_values = {}
10
12
  end
11
13
 
12
- def account_id
14
+ def project_id
13
15
  1
14
16
  end
15
17
 
16
18
  def log_internal level, message
17
19
  end
20
+
21
+ def mock_this_config key, config_value
22
+ @config_values[key] = config_value
23
+ end
24
+
25
+ def get(key)
26
+ @config_values[key]
27
+ end
28
+
29
+ def get_config(key)
30
+ puts "HELLOOOO"
31
+ @config_values[key]
32
+ end
18
33
  end
19
34
 
20
35
  class MockConfigLoader