prefab-cloud-ruby 0.6.0 → 0.9.0

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