prefab-cloud-ruby 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc.sample +3 -0
  3. data/.github/workflows/ruby.yml +4 -0
  4. data/.gitmodules +3 -0
  5. data/Gemfile +12 -12
  6. data/Gemfile.lock +16 -14
  7. data/README.md +1 -1
  8. data/Rakefile +13 -14
  9. data/VERSION +1 -1
  10. data/lib/prefab/auth_interceptor.rb +2 -1
  11. data/lib/prefab/cancellable_interceptor.rb +8 -7
  12. data/lib/prefab/client.rb +33 -24
  13. data/lib/prefab/config_client.rb +55 -66
  14. data/lib/prefab/config_loader.rb +7 -114
  15. data/lib/prefab/config_resolver.rb +27 -57
  16. data/lib/prefab/config_value_unwrapper.rb +23 -0
  17. data/lib/prefab/criteria_evaluator.rb +96 -0
  18. data/lib/prefab/errors/invalid_api_key_error.rb +1 -1
  19. data/lib/prefab/feature_flag_client.rb +13 -145
  20. data/lib/prefab/internal_logger.rb +6 -5
  21. data/lib/prefab/local_config_parser.rb +110 -0
  22. data/lib/prefab/logger_client.rb +26 -31
  23. data/lib/prefab/murmer3.rb +3 -4
  24. data/lib/prefab/noop_cache.rb +5 -7
  25. data/lib/prefab/noop_stats.rb +2 -3
  26. data/lib/prefab/options.rb +11 -9
  27. data/lib/prefab/ratelimit_client.rb +11 -13
  28. data/lib/prefab/sse_logger.rb +3 -2
  29. data/lib/prefab/weighted_value_resolver.rb +42 -0
  30. data/lib/prefab/yaml_config_parser.rb +32 -0
  31. data/lib/prefab-cloud-ruby.rb +7 -2
  32. data/lib/prefab_pb.rb +49 -43
  33. data/lib/prefab_services_pb.rb +0 -1
  34. data/prefab-cloud-ruby.gemspec +28 -19
  35. data/test/.prefab.unit_tests.config.yaml +3 -2
  36. data/test/integration_test.rb +98 -0
  37. data/test/integration_test_helpers.rb +37 -0
  38. data/test/test_client.rb +32 -31
  39. data/test/test_config_client.rb +21 -20
  40. data/test/test_config_loader.rb +48 -37
  41. data/test/test_config_resolver.rb +312 -135
  42. data/test/test_config_value_unwrapper.rb +83 -0
  43. data/test/test_criteria_evaluator.rb +533 -0
  44. data/test/test_feature_flag_client.rb +35 -347
  45. data/test/test_helper.rb +18 -14
  46. data/test/test_integration.rb +33 -0
  47. data/test/test_local_config_parser.rb +78 -0
  48. data/test/test_logger.rb +47 -46
  49. data/test/test_weighted_value_resolver.rb +65 -0
  50. metadata +24 -27
  51. data/lib/prefab/config_helper.rb +0 -31
  52. data/run_test_harness_server.sh +0 -8
  53. data/test/harness_server.rb +0 -64
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestConfigValueUnwrapper < Minitest::Test
6
+ CONFIG_KEY = 'config_key'
7
+
8
+ def test_unwrapping_int
9
+ config_value = Prefab::ConfigValue.new(int: 123)
10
+ assert_equal 123, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
11
+ end
12
+
13
+ def test_unwrapping_string
14
+ config_value = Prefab::ConfigValue.new(string: 'abc')
15
+ assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
16
+ end
17
+
18
+ def test_unwrapping_double
19
+ config_value = Prefab::ConfigValue.new(double: 1.23)
20
+ assert_equal 1.23, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
21
+ end
22
+
23
+ def test_unwrapping_bool
24
+ config_value = Prefab::ConfigValue.new(bool: true)
25
+ assert_equal true, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
26
+
27
+ config_value = Prefab::ConfigValue.new(bool: false)
28
+ assert_equal false, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
29
+ end
30
+
31
+ def test_unwrapping_log_level
32
+ config_value = Prefab::ConfigValue.new(log_level: :INFO)
33
+ assert_equal :INFO, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
34
+ end
35
+
36
+ def test_unwrapping_string_list
37
+ config_value = Prefab::ConfigValue.new(string_list: Prefab::StringList.new(values: %w[a b c]))
38
+ assert_equal %w[a b c], Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
39
+ end
40
+
41
+ def test_unwrapping_weighted_values
42
+ # single value
43
+ config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1]]))
44
+ assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
45
+
46
+ # multiple values, evenly distributed
47
+ config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1], ['def', 1], ['ghi', 1]]))
48
+ assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:123'))
49
+ assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:456'))
50
+ assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:789'))
51
+ assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:012'))
52
+
53
+ # multiple values, unevenly distributed
54
+ config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1], ['def', 99], ['ghi', 1]]))
55
+ assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:123'))
56
+ assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:456'))
57
+ assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:789'))
58
+ assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:012'))
59
+
60
+ assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:103'))
61
+ assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:119'))
62
+ end
63
+
64
+ private
65
+
66
+ def weighted_values(values_and_weights)
67
+ values = values_and_weights.map do |value, weight|
68
+ weighted_value(value, weight)
69
+ end
70
+
71
+ Prefab::WeightedValues.new(weighted_values: values)
72
+ end
73
+
74
+ def weighted_value(string, weight)
75
+ Prefab::WeightedValue.new(
76
+ value: Prefab::ConfigValue.new(string: string), weight: weight
77
+ )
78
+ end
79
+
80
+ def lookup_properties(lookup_key)
81
+ { Prefab::CriteriaEvaluator::LOOKUP_KEY => lookup_key }
82
+ end
83
+ end
@@ -0,0 +1,533 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestCriteriaEvaluator < Minitest::Test
6
+ PROJECT_ENV_ID = 1
7
+ TEST_ENV_ID = 2
8
+ KEY = 'key'
9
+ DEFAULT_VALUE = 'default_value'
10
+ DESIRED_VALUE = 'desired_value'
11
+ WRONG_ENV_VALUE = 'wrong_env_value'
12
+
13
+ DEFAULT_ROW = Prefab::ConfigRow.new(
14
+ values: [
15
+ Prefab::ConditionalValue.new(
16
+ value: Prefab::ConfigValue.new(string: DEFAULT_VALUE)
17
+ )
18
+ ]
19
+ )
20
+
21
+ def test_always_true
22
+ config = Prefab::Config.new(
23
+ key: KEY,
24
+ rows: [
25
+ DEFAULT_ROW,
26
+ Prefab::ConfigRow.new(
27
+ project_env_id: PROJECT_ENV_ID,
28
+ values: [
29
+ Prefab::ConditionalValue.new(
30
+ criteria: [
31
+ Prefab::Criterion.new(operator: Prefab::Criterion::CriterionOperator::ALWAYS_TRUE)
32
+ ],
33
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
34
+ )
35
+ ]
36
+ )
37
+ ]
38
+ )
39
+
40
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
41
+
42
+ assert_equal DESIRED_VALUE, evaluator.evaluate({}).string
43
+ end
44
+
45
+ def test_lookup_key_in
46
+ config = Prefab::Config.new(
47
+ key: KEY,
48
+ rows: [
49
+ DEFAULT_ROW,
50
+ Prefab::ConfigRow.new(
51
+ project_env_id: PROJECT_ENV_ID,
52
+ values: [
53
+ Prefab::ConditionalValue.new(
54
+ criteria: [
55
+ Prefab::Criterion.new(
56
+ operator: Prefab::Criterion::CriterionOperator::LOOKUP_KEY_IN,
57
+ value_to_match: string_list(%w[ok fine]),
58
+ property_name: Prefab::CriteriaEvaluator::LOOKUP_KEY
59
+ )
60
+ ],
61
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
62
+ )
63
+ ]
64
+ )
65
+ ]
66
+ )
67
+
68
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
69
+
70
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
71
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ Prefab::CriteriaEvaluator::LOOKUP_KEY => 'wrong' }).string
72
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ Prefab::CriteriaEvaluator::LOOKUP_KEY => 'ok' }).string
73
+ end
74
+
75
+ def test_lookup_key_not_in
76
+ config = Prefab::Config.new(
77
+ key: KEY,
78
+ rows: [
79
+ DEFAULT_ROW,
80
+ Prefab::ConfigRow.new(
81
+ project_env_id: PROJECT_ENV_ID,
82
+ values: [
83
+ Prefab::ConditionalValue.new(
84
+ criteria: [
85
+ Prefab::Criterion.new(
86
+ operator: Prefab::Criterion::CriterionOperator::LOOKUP_KEY_NOT_IN,
87
+ value_to_match: string_list(%w[wrong bad]),
88
+ property_name: Prefab::CriteriaEvaluator::LOOKUP_KEY
89
+ )
90
+ ],
91
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
92
+ )
93
+ ]
94
+ )
95
+ ]
96
+ )
97
+
98
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
99
+
100
+ assert_equal DESIRED_VALUE, evaluator.evaluate({}).string
101
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ Prefab::CriteriaEvaluator::LOOKUP_KEY => 'ok' }).string
102
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ Prefab::CriteriaEvaluator::LOOKUP_KEY => 'wrong' }).string
103
+ end
104
+
105
+ def test_prop_is_one_of
106
+ config = Prefab::Config.new(
107
+ key: KEY,
108
+ rows: [
109
+ DEFAULT_ROW,
110
+ Prefab::ConfigRow.new(
111
+ project_env_id: PROJECT_ENV_ID,
112
+ values: [
113
+ Prefab::ConditionalValue.new(
114
+ criteria: [
115
+ Prefab::Criterion.new(
116
+ operator: Prefab::Criterion::CriterionOperator::PROP_IS_ONE_OF,
117
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
118
+ property_name: 'email_suffix'
119
+ )
120
+ ],
121
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
122
+ )
123
+ ]
124
+ )
125
+ ]
126
+ )
127
+
128
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
129
+
130
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
131
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email_suffix: 'prefab.cloud' }).string
132
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email_suffix: 'hotmail.com' }).string
133
+ end
134
+
135
+ def test_prop_is_not_one_of
136
+ config = Prefab::Config.new(
137
+ key: KEY,
138
+ rows: [
139
+ DEFAULT_ROW,
140
+ Prefab::ConfigRow.new(
141
+ project_env_id: PROJECT_ENV_ID,
142
+ values: [
143
+ Prefab::ConditionalValue.new(
144
+ criteria: [
145
+ Prefab::Criterion.new(
146
+ operator: Prefab::Criterion::CriterionOperator::PROP_IS_NOT_ONE_OF,
147
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
148
+ property_name: 'email_suffix'
149
+ )
150
+ ],
151
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
152
+ )
153
+ ]
154
+ )
155
+ ]
156
+ )
157
+
158
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
159
+
160
+ assert_equal DESIRED_VALUE, evaluator.evaluate({}).string
161
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email_suffix: 'prefab.cloud' }).string
162
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email_suffix: 'hotmail.com' }).string
163
+ end
164
+
165
+ def test_prop_ends_with_one_of
166
+ config = Prefab::Config.new(
167
+ key: KEY,
168
+ rows: [
169
+ DEFAULT_ROW,
170
+ Prefab::ConfigRow.new(
171
+ project_env_id: PROJECT_ENV_ID,
172
+ values: [
173
+ Prefab::ConditionalValue.new(
174
+ criteria: [
175
+ Prefab::Criterion.new(
176
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
177
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
178
+ property_name: 'email'
179
+ )
180
+ ],
181
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
182
+ )
183
+ ]
184
+ )
185
+ ]
186
+ )
187
+
188
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
189
+
190
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
191
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud' }).string
192
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@hotmail.com' }).string
193
+ end
194
+
195
+ def test_prop_does_not_end_with_one_of
196
+ config = Prefab::Config.new(
197
+ key: KEY,
198
+ rows: [
199
+ DEFAULT_ROW,
200
+ Prefab::ConfigRow.new(
201
+ project_env_id: PROJECT_ENV_ID,
202
+ values: [
203
+ Prefab::ConditionalValue.new(
204
+ criteria: [
205
+ Prefab::Criterion.new(
206
+ operator: Prefab::Criterion::CriterionOperator::PROP_DOES_NOT_END_WITH_ONE_OF,
207
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
208
+ property_name: 'email'
209
+ )
210
+ ],
211
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
212
+ )
213
+ ]
214
+ )
215
+ ]
216
+ )
217
+
218
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID, resolver: nil, base_client: nil)
219
+
220
+ assert_equal DESIRED_VALUE, evaluator.evaluate({}).string
221
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud' }).string
222
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@hotmail.com' }).string
223
+ end
224
+
225
+ def test_in_seg
226
+ segment_key = 'segment_key'
227
+
228
+ segment_config = Prefab::Config.new(
229
+ config_type: Prefab::ConfigType::SEGMENT,
230
+ key: segment_key,
231
+ rows: [
232
+ Prefab::ConfigRow.new(
233
+ values: [
234
+ Prefab::ConditionalValue.new(
235
+ value: Prefab::ConfigValue.new(bool: true),
236
+ criteria: [
237
+ Prefab::Criterion.new(
238
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
239
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
240
+ property_name: 'email'
241
+ )
242
+ ]
243
+ ),
244
+ Prefab::ConditionalValue.new(
245
+ value: Prefab::ConfigValue.new(bool: false)
246
+ )
247
+ ]
248
+ )
249
+ ]
250
+ )
251
+
252
+ config = Prefab::Config.new(
253
+ key: KEY,
254
+ rows: [
255
+ DEFAULT_ROW,
256
+
257
+ # wrong env
258
+ Prefab::ConfigRow.new(
259
+ project_env_id: TEST_ENV_ID,
260
+ values: [
261
+ Prefab::ConditionalValue.new(
262
+ criteria: [
263
+ Prefab::Criterion.new(
264
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
265
+ value_to_match: Prefab::ConfigValue.new(string: segment_key)
266
+ )
267
+ ],
268
+ value: Prefab::ConfigValue.new(string: WRONG_ENV_VALUE)
269
+ )
270
+ ]
271
+ ),
272
+
273
+ # correct env
274
+ Prefab::ConfigRow.new(
275
+ project_env_id: PROJECT_ENV_ID,
276
+ values: [
277
+ Prefab::ConditionalValue.new(
278
+ criteria: [
279
+ Prefab::Criterion.new(
280
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
281
+ value_to_match: Prefab::ConfigValue.new(string: segment_key)
282
+ )
283
+ ],
284
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
285
+ )
286
+ ]
287
+ )
288
+ ]
289
+ )
290
+
291
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID,
292
+ base_client: nil,
293
+ resolver: resolver_fake({ segment_key => segment_config }))
294
+
295
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
296
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud' }).string
297
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@hotmail.com' }).string
298
+ end
299
+
300
+ def test_not_in_seg
301
+ segment_key = 'segment_key'
302
+
303
+ segment_config = Prefab::Config.new(
304
+ config_type: Prefab::ConfigType::SEGMENT,
305
+ key: segment_key,
306
+ rows: [
307
+ Prefab::ConfigRow.new(
308
+ values: [
309
+ Prefab::ConditionalValue.new(
310
+ value: Prefab::ConfigValue.new(bool: true),
311
+ criteria: [
312
+ Prefab::Criterion.new(
313
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
314
+ value_to_match: string_list(['hotmail.com', 'gmail.com']),
315
+ property_name: 'email'
316
+ )
317
+ ]
318
+ ),
319
+ Prefab::ConditionalValue.new(
320
+ value: Prefab::ConfigValue.new(bool: false)
321
+ )
322
+ ]
323
+ )
324
+ ]
325
+ )
326
+
327
+ config = Prefab::Config.new(
328
+ key: KEY,
329
+ rows: [
330
+ DEFAULT_ROW,
331
+ Prefab::ConfigRow.new(
332
+ project_env_id: PROJECT_ENV_ID,
333
+ values: [
334
+ Prefab::ConditionalValue.new(
335
+ criteria: [
336
+ Prefab::Criterion.new(
337
+ operator: Prefab::Criterion::CriterionOperator::NOT_IN_SEG,
338
+ value_to_match: Prefab::ConfigValue.new(string: segment_key)
339
+ )
340
+ ],
341
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
342
+ )
343
+ ]
344
+ )
345
+ ]
346
+ )
347
+
348
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID,
349
+ base_client: nil,
350
+ resolver: resolver_fake({ segment_key => segment_config }))
351
+
352
+ assert_equal DESIRED_VALUE, evaluator.evaluate({}).string
353
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud' }).string
354
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@hotmail.com' }).string
355
+ end
356
+
357
+ def test_multiple_conditions_in_one_value
358
+ segment_key = 'segment_key'
359
+
360
+ segment_config = Prefab::Config.new(
361
+ config_type: Prefab::ConfigType::SEGMENT,
362
+ key: segment_key,
363
+ rows: [
364
+ Prefab::ConfigRow.new(
365
+ values: [
366
+ Prefab::ConditionalValue.new(
367
+ value: Prefab::ConfigValue.new(bool: true),
368
+ criteria: [
369
+ Prefab::Criterion.new(
370
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
371
+ value_to_match: string_list(['prefab.cloud', 'gmail.com']),
372
+ property_name: 'email'
373
+ ),
374
+
375
+ Prefab::Criterion.new(
376
+ operator: Prefab::Criterion::CriterionOperator::PROP_IS_ONE_OF,
377
+ value_to_match: Prefab::ConfigValue.new(bool: true),
378
+ property_name: 'admin'
379
+ )
380
+ ]
381
+ ),
382
+ Prefab::ConditionalValue.new(
383
+ value: Prefab::ConfigValue.new(bool: false)
384
+ )
385
+ ]
386
+ )
387
+ ]
388
+ )
389
+
390
+ config = Prefab::Config.new(
391
+ key: KEY,
392
+ rows: [
393
+ DEFAULT_ROW,
394
+ Prefab::ConfigRow.new(
395
+ project_env_id: PROJECT_ENV_ID,
396
+ values: [
397
+ Prefab::ConditionalValue.new(
398
+ criteria: [
399
+ Prefab::Criterion.new(
400
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
401
+ value_to_match: Prefab::ConfigValue.new(string: segment_key)
402
+ ),
403
+
404
+ Prefab::Criterion.new(
405
+ operator: Prefab::Criterion::CriterionOperator::PROP_IS_NOT_ONE_OF,
406
+ value_to_match: Prefab::ConfigValue.new(bool: true),
407
+ property_name: 'deleted'
408
+ )
409
+ ],
410
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
411
+ )
412
+ ]
413
+ )
414
+ ]
415
+ )
416
+
417
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID,
418
+ base_client: nil,
419
+ resolver: resolver_fake({ segment_key => segment_config }))
420
+
421
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
422
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud' }).string
423
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud', admin: true }).string
424
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud', admin: true, deleted: true }).string
425
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@gmail.com' }).string
426
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@gmail.com', admin: true }).string
427
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@gmail.com', admin: true, deleted: true }).string
428
+ end
429
+
430
+ def test_multiple_conditions_in_multiple_values
431
+ segment_key = 'segment_key'
432
+
433
+ segment_config = Prefab::Config.new(
434
+ config_type: Prefab::ConfigType::SEGMENT,
435
+ key: segment_key,
436
+ rows: [
437
+ Prefab::ConfigRow.new(
438
+ values: [
439
+ Prefab::ConditionalValue.new(
440
+ value: Prefab::ConfigValue.new(bool: true),
441
+ criteria: [
442
+ Prefab::Criterion.new(
443
+ operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
444
+ value_to_match: string_list(['prefab.cloud', 'gmail.com']),
445
+ property_name: 'email'
446
+ )
447
+ ]
448
+ ),
449
+ Prefab::ConditionalValue.new(
450
+ value: Prefab::ConfigValue.new(bool: true),
451
+ criteria: [
452
+ Prefab::Criterion.new(
453
+ operator: Prefab::Criterion::CriterionOperator::PROP_IS_ONE_OF,
454
+ value_to_match: Prefab::ConfigValue.new(bool: true),
455
+ property_name: 'admin'
456
+ )
457
+ ]
458
+ ),
459
+ Prefab::ConditionalValue.new(
460
+ value: Prefab::ConfigValue.new(bool: false)
461
+ )
462
+ ]
463
+ )
464
+ ]
465
+ )
466
+
467
+ config = Prefab::Config.new(
468
+ key: KEY,
469
+ rows: [
470
+ DEFAULT_ROW,
471
+ Prefab::ConfigRow.new(
472
+ project_env_id: PROJECT_ENV_ID,
473
+ values: [
474
+ Prefab::ConditionalValue.new(
475
+ criteria: [
476
+ Prefab::Criterion.new(
477
+ operator: Prefab::Criterion::CriterionOperator::IN_SEG,
478
+ value_to_match: Prefab::ConfigValue.new(string: segment_key)
479
+ ),
480
+
481
+ Prefab::Criterion.new(
482
+ operator: Prefab::Criterion::CriterionOperator::PROP_IS_NOT_ONE_OF,
483
+ value_to_match: Prefab::ConfigValue.new(bool: true),
484
+ property_name: 'deleted'
485
+ )
486
+ ],
487
+ value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
488
+ )
489
+ ]
490
+ )
491
+ ]
492
+ )
493
+
494
+ evaluator = Prefab::CriteriaEvaluator.new(config, project_env_id: PROJECT_ENV_ID,
495
+ base_client: nil,
496
+ resolver: resolver_fake({ segment_key => segment_config }))
497
+
498
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({}).string
499
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud' }).string
500
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ admin: true }).string
501
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud', admin: true }).string
502
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@prefab.cloud', admin: true, deleted: true }).string
503
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@gmail.com' }).string
504
+ assert_equal DESIRED_VALUE, evaluator.evaluate({ email: 'example@gmail.com', admin: true }).string
505
+ assert_equal DEFAULT_VALUE, evaluator.evaluate({ email: 'example@gmail.com', admin: true, deleted: true }).string
506
+ end
507
+
508
+ private
509
+
510
+ def string_list(values)
511
+ Prefab::ConfigValue.new(string_list: Prefab::StringList.new(values: values))
512
+ end
513
+
514
+ class FakeResolver
515
+ def initialize(config)
516
+ @config = config
517
+ end
518
+
519
+ def raw(key)
520
+ @config[key]
521
+ end
522
+
523
+ def get(key, _lookup_key, properties = {})
524
+ # This only gets called for segments, so we don't need to pass in a resolver
525
+ Prefab::CriteriaEvaluator.new(@config[key], project_env_id: nil, resolver: nil,
526
+ base_client: nil).evaluate(properties)
527
+ end
528
+ end
529
+
530
+ def resolver_fake(config)
531
+ FakeResolver.new(config)
532
+ end
533
+ end