prefab-cloud-ruby 0.20.0 → 0.21.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.
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