prefab-cloud-ruby 0.23.7 → 0.24.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.
- checksums.yaml +4 -4
- data/README.md +10 -8
- data/VERSION +1 -1
- data/lib/prefab/client.rb +55 -21
- data/lib/prefab/config_client.rb +13 -25
- data/lib/prefab/config_resolver.rb +28 -28
- data/lib/prefab/config_value_unwrapper.rb +8 -5
- data/lib/prefab/context.rb +119 -0
- data/lib/prefab/criteria_evaluator.rb +23 -17
- data/lib/prefab/feature_flag_client.rb +7 -7
- data/lib/prefab/local_config_parser.rb +2 -17
- data/lib/prefab/logger_client.rb +3 -6
- data/lib/prefab/resolved_config_presenter.rb +84 -0
- data/lib/prefab/weighted_value_resolver.rb +4 -4
- data/lib/prefab-cloud-ruby.rb +6 -0
- data/lib/prefab_pb.rb +42 -41
- data/prefab-cloud-ruby.gemspec +6 -4
- data/test/.prefab.unit_tests.config.yaml +3 -2
- data/test/integration_test.rb +4 -8
- data/test/test_client.rb +28 -27
- data/test/test_config_resolver.rb +106 -54
- data/test/test_config_value_unwrapper.rb +15 -15
- data/test/test_context.rb +158 -0
- data/test/test_criteria_evaluator.rb +93 -78
- data/test/test_feature_flag_client.rb +14 -20
- data/test/test_helper.rb +1 -1
- data/test/test_integration.rb +30 -14
- data/test/test_local_config_parser.rb +6 -8
- data/test/test_log_path_collector.rb +4 -7
- data/test/test_logger.rb +12 -12
- metadata +5 -3
- data/lib/prefab_services_pb.rb +0 -80
@@ -9,10 +9,19 @@ class TestConfigResolver < Minitest::Test
|
|
9
9
|
SEGMENT_KEY = 'segment_key'
|
10
10
|
CONFIG_KEY = 'config_key'
|
11
11
|
DEFAULT_VALUE = 'default_value'
|
12
|
+
DESIRED_VALUE = 'desired_value'
|
12
13
|
IN_SEGMENT_VALUE = 'in_segment_value'
|
13
14
|
WRONG_ENV_VALUE = 'wrong_env_value'
|
14
15
|
NOT_IN_SEGMENT_VALUE = 'not_in_segment_value'
|
15
16
|
|
17
|
+
DEFAULT_ROW = Prefab::ConfigRow.new(
|
18
|
+
values: [
|
19
|
+
Prefab::ConditionalValue.new(
|
20
|
+
value: Prefab::ConfigValue.new(string: DEFAULT_VALUE)
|
21
|
+
)
|
22
|
+
]
|
23
|
+
)
|
24
|
+
|
16
25
|
def test_resolution
|
17
26
|
@loader = MockConfigLoader.new
|
18
27
|
|
@@ -20,13 +29,7 @@ class TestConfigResolver < Minitest::Test
|
|
20
29
|
'key' => { config: Prefab::Config.new(
|
21
30
|
key: 'key',
|
22
31
|
rows: [
|
23
|
-
|
24
|
-
values: [
|
25
|
-
Prefab::ConditionalValue.new(
|
26
|
-
value: Prefab::ConfigValue.new(string: 'value_no_env_default')
|
27
|
-
)
|
28
|
-
]
|
29
|
-
),
|
32
|
+
DEFAULT_ROW,
|
30
33
|
Prefab::ConfigRow.new(
|
31
34
|
project_env_id: TEST_ENV_ID,
|
32
35
|
values: [
|
@@ -114,31 +117,49 @@ class TestConfigResolver < Minitest::Test
|
|
114
117
|
|
115
118
|
@loader.stub :calc_config, loaded_values do
|
116
119
|
@resolverA = resolver_for_namespace('', @loader, project_env_id: PRODUCTION_ENV_ID)
|
117
|
-
|
120
|
+
assert_equal_context_and_jit DEFAULT_VALUE, @resolverA, 'key', {}, :string
|
118
121
|
|
119
122
|
## below here in the test env
|
120
123
|
@resolverA = resolver_for_namespace('', @loader)
|
121
|
-
|
124
|
+
assert_equal_context_and_jit 'value_none', @resolverA, 'key', {}, :string
|
122
125
|
|
123
126
|
@resolverA = resolver_for_namespace('projectA', @loader)
|
124
|
-
|
127
|
+
assert_equal_context_and_jit 'valueA', @resolverA, 'key', {}, :string
|
125
128
|
|
126
129
|
@resolverB = resolver_for_namespace('projectB', @loader)
|
127
|
-
|
130
|
+
assert_equal_context_and_jit 'valueB', @resolverB, 'key', {}, :string
|
128
131
|
|
129
132
|
@resolverBX = resolver_for_namespace('projectB.subprojectX', @loader)
|
130
|
-
|
133
|
+
assert_equal_context_and_jit 'projectB.subprojectX', @resolverBX, 'key', {}, :string
|
131
134
|
|
132
135
|
@resolverBX = resolver_for_namespace('projectB.subprojectX', @loader)
|
133
|
-
|
136
|
+
assert_equal_context_and_jit 'valueB2', @resolverBX, 'key2', {}, :string
|
134
137
|
|
135
|
-
@resolverUndefinedSubProject = resolver_for_namespace('projectB.subprojectX.subsubQ',
|
136
|
-
|
138
|
+
@resolverUndefinedSubProject = resolver_for_namespace('projectB.subprojectX.subsubQ',
|
139
|
+
@loader)
|
140
|
+
assert_equal_context_and_jit 'projectB.subprojectX', @resolverUndefinedSubProject, 'key',
|
141
|
+
{}, :string
|
137
142
|
|
138
143
|
@resolverBX = resolver_for_namespace('projectC', @loader)
|
139
|
-
|
144
|
+
assert_equal_context_and_jit 'value_none', @resolverBX, 'key', {}, :string
|
140
145
|
|
141
146
|
assert_nil @resolverBX.get('key_that_doesnt_exist', nil)
|
147
|
+
|
148
|
+
assert_equal @resolverBX.to_s.strip.split("\n").map(&:strip), [
|
149
|
+
'key | value_none | String | Match: | Source:',
|
150
|
+
'key2 | valueB2 | String | Match: | Source:'
|
151
|
+
]
|
152
|
+
|
153
|
+
assert_equal @resolverBX.presenter.to_h, {
|
154
|
+
'key' => Prefab::ResolvedConfigPresenter::ConfigRow.new('key', 'value_none', nil, nil),
|
155
|
+
'key2' => Prefab::ResolvedConfigPresenter::ConfigRow.new('key2', 'valueB2', nil, nil)
|
156
|
+
}
|
157
|
+
|
158
|
+
resolved_lines = []
|
159
|
+
@resolverBX.presenter.each do |key, row|
|
160
|
+
resolved_lines << [key, row.value]
|
161
|
+
end
|
162
|
+
assert_equal resolved_lines, [%w[key value_none], %w[key2 valueB2]]
|
142
163
|
end
|
143
164
|
end
|
144
165
|
|
@@ -155,7 +176,7 @@ class TestConfigResolver < Minitest::Test
|
|
155
176
|
Prefab::Criterion.new(
|
156
177
|
operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
|
157
178
|
value_to_match: string_list(['hotmail.com', 'gmail.com']),
|
158
|
-
property_name: 'email'
|
179
|
+
property_name: 'user.email'
|
159
180
|
)
|
160
181
|
]
|
161
182
|
),
|
@@ -222,8 +243,10 @@ class TestConfigResolver < Minitest::Test
|
|
222
243
|
resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
|
223
244
|
resolver.project_env_id = PRODUCTION_ENV_ID
|
224
245
|
|
225
|
-
|
226
|
-
|
246
|
+
assert_equal_context_and_jit DEFAULT_VALUE, resolver, CONFIG_KEY,
|
247
|
+
{ user: { email: 'test@something-else.com' } }, :string
|
248
|
+
assert_equal_context_and_jit IN_SEGMENT_VALUE, resolver, CONFIG_KEY,
|
249
|
+
{ user: { email: 'test@hotmail.com' } }, :string
|
227
250
|
end
|
228
251
|
end
|
229
252
|
|
@@ -240,7 +263,7 @@ class TestConfigResolver < Minitest::Test
|
|
240
263
|
Prefab::Criterion.new(
|
241
264
|
operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
|
242
265
|
value_to_match: string_list(['hotmail.com', 'gmail.com']),
|
243
|
-
property_name: 'email'
|
266
|
+
property_name: 'user.email'
|
244
267
|
)
|
245
268
|
]
|
246
269
|
),
|
@@ -289,77 +312,98 @@ class TestConfigResolver < Minitest::Test
|
|
289
312
|
options = Prefab::Options.new
|
290
313
|
resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
|
291
314
|
|
292
|
-
|
293
|
-
|
315
|
+
assert_equal_context_and_jit IN_SEGMENT_VALUE, resolver, CONFIG_KEY, { user: { email: 'test@hotmail.com' } },
|
316
|
+
:string
|
317
|
+
assert_equal_context_and_jit NOT_IN_SEGMENT_VALUE, resolver, CONFIG_KEY, { user: { email: 'test@something-else.com' } },
|
318
|
+
:string
|
294
319
|
end
|
295
320
|
end
|
296
321
|
|
297
|
-
def
|
298
|
-
|
299
|
-
|
300
|
-
key: SEGMENT_KEY,
|
322
|
+
def test_jit_context_merges_with_existing_context
|
323
|
+
config = Prefab::Config.new(
|
324
|
+
key: CONFIG_KEY,
|
301
325
|
rows: [
|
326
|
+
DEFAULT_ROW,
|
302
327
|
Prefab::ConfigRow.new(
|
328
|
+
project_env_id: TEST_ENV_ID,
|
303
329
|
values: [
|
304
330
|
Prefab::ConditionalValue.new(
|
305
|
-
value: Prefab::ConfigValue.new(bool: true),
|
306
331
|
criteria: [
|
307
332
|
Prefab::Criterion.new(
|
308
|
-
operator: Prefab::Criterion::CriterionOperator::
|
309
|
-
value_to_match: string_list([
|
310
|
-
property_name: '
|
333
|
+
operator: Prefab::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
334
|
+
value_to_match: string_list(%w[pro advanced]),
|
335
|
+
property_name: 'team.plan'
|
336
|
+
),
|
337
|
+
|
338
|
+
Prefab::Criterion.new(
|
339
|
+
operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
|
340
|
+
value_to_match: string_list(%w[@example.com]),
|
341
|
+
property_name: 'user.email'
|
311
342
|
)
|
312
|
-
]
|
313
|
-
|
314
|
-
|
343
|
+
],
|
344
|
+
value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
|
345
|
+
)
|
315
346
|
]
|
316
347
|
)
|
317
348
|
]
|
318
349
|
)
|
319
350
|
|
351
|
+
loader = MockConfigLoader.new
|
352
|
+
|
353
|
+
loader.stub :calc_config, { CONFIG_KEY => { config: config } } do
|
354
|
+
options = Prefab::Options.new
|
355
|
+
resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
|
356
|
+
resolver.project_env_id = TEST_ENV_ID
|
357
|
+
|
358
|
+
Prefab::Context.with_context({ user: { email: 'test@example.com' } }) do
|
359
|
+
assert_equal DEFAULT_VALUE, resolver.get(CONFIG_KEY).string
|
360
|
+
assert_equal DEFAULT_VALUE, resolver.get(CONFIG_KEY, { team: { plan: 'freebie' } }).string
|
361
|
+
assert_equal DESIRED_VALUE, resolver.get(CONFIG_KEY, { team: { plan: 'pro' } }).string
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def test_jit_can_clobber_existing_context
|
320
367
|
config = Prefab::Config.new(
|
321
368
|
key: CONFIG_KEY,
|
322
369
|
rows: [
|
370
|
+
DEFAULT_ROW,
|
323
371
|
Prefab::ConfigRow.new(
|
372
|
+
project_env_id: TEST_ENV_ID,
|
324
373
|
values: [
|
325
374
|
Prefab::ConditionalValue.new(
|
326
375
|
criteria: [
|
327
376
|
Prefab::Criterion.new(
|
328
|
-
operator: Prefab::Criterion::CriterionOperator::
|
329
|
-
value_to_match:
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
),
|
334
|
-
Prefab::ConditionalValue.new(
|
335
|
-
criteria: [
|
377
|
+
operator: Prefab::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
378
|
+
value_to_match: string_list(%w[pro advanced]),
|
379
|
+
property_name: 'team.plan'
|
380
|
+
),
|
381
|
+
|
336
382
|
Prefab::Criterion.new(
|
337
|
-
operator: Prefab::Criterion::CriterionOperator::
|
338
|
-
value_to_match:
|
383
|
+
operator: Prefab::Criterion::CriterionOperator::PROP_ENDS_WITH_ONE_OF,
|
384
|
+
value_to_match: string_list(%w[@example.com]),
|
385
|
+
property_name: 'user.email'
|
339
386
|
)
|
340
387
|
],
|
341
|
-
value: Prefab::ConfigValue.new(string:
|
388
|
+
value: Prefab::ConfigValue.new(string: DESIRED_VALUE)
|
342
389
|
)
|
343
390
|
]
|
344
391
|
)
|
345
392
|
]
|
346
393
|
)
|
347
394
|
|
348
|
-
loaded_values = {
|
349
|
-
SEGMENT_KEY => { config: segment_config },
|
350
|
-
CONFIG_KEY => { config: config }
|
351
|
-
}
|
352
|
-
|
353
395
|
loader = MockConfigLoader.new
|
354
396
|
|
355
|
-
loader.stub :calc_config,
|
397
|
+
loader.stub :calc_config, { CONFIG_KEY => { config: config } } do
|
356
398
|
options = Prefab::Options.new
|
357
399
|
resolver = Prefab::ConfigResolver.new(MockBaseClient.new(options), loader)
|
400
|
+
resolver.project_env_id = TEST_ENV_ID
|
358
401
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
402
|
+
Prefab::Context.with_context({ user: { email: 'test@hotmail.com' }, team: { plan: 'pro' } }) do
|
403
|
+
assert_equal DEFAULT_VALUE, resolver.get(CONFIG_KEY).string
|
404
|
+
assert_equal DESIRED_VALUE, resolver.get(CONFIG_KEY, { user: { email: 'test@example.com' } }).string
|
405
|
+
assert_equal DEFAULT_VALUE, resolver.get(CONFIG_KEY, { team: { plan: 'freebie' } }).string
|
406
|
+
end
|
363
407
|
end
|
364
408
|
end
|
365
409
|
|
@@ -374,4 +418,12 @@ class TestConfigResolver < Minitest::Test
|
|
374
418
|
resolver.update
|
375
419
|
resolver
|
376
420
|
end
|
421
|
+
|
422
|
+
def assert_equal_context_and_jit(expected_value, resolver, key, properties, type)
|
423
|
+
assert_equal expected_value, resolver.get(key, properties).send(type)
|
424
|
+
|
425
|
+
Prefab::Context.with_context(properties) do
|
426
|
+
assert_equal expected_value, resolver.get(key).send(type)
|
427
|
+
end
|
428
|
+
end
|
377
429
|
end
|
@@ -41,34 +41,34 @@ class TestConfigValueUnwrapper < Minitest::Test
|
|
41
41
|
def test_unwrapping_weighted_values
|
42
42
|
# single value
|
43
43
|
config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1]]))
|
44
|
+
|
44
45
|
assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
|
45
46
|
|
46
47
|
# multiple values, evenly distributed
|
47
48
|
config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1], ['def', 1], ['ghi', 1]]))
|
48
|
-
assert_equal '
|
49
|
-
assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY,
|
50
|
-
assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY,
|
51
|
-
assert_equal '
|
49
|
+
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:000'))
|
50
|
+
assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:456'))
|
51
|
+
assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:789'))
|
52
|
+
assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:888'))
|
52
53
|
|
53
54
|
# multiple values, unevenly distributed
|
54
55
|
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,
|
56
|
-
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY,
|
57
|
-
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY,
|
58
|
-
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY,
|
59
|
-
|
60
|
-
assert_equal '
|
61
|
-
assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, lookup_properties('user:119'))
|
56
|
+
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:123'))
|
57
|
+
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:456'))
|
58
|
+
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:789'))
|
59
|
+
assert_equal 'def', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:012'))
|
60
|
+
assert_equal 'ghi', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:428'))
|
61
|
+
assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, context_with_key('user:548'))
|
62
62
|
end
|
63
63
|
|
64
64
|
private
|
65
65
|
|
66
|
-
def weighted_values(values_and_weights)
|
66
|
+
def weighted_values(values_and_weights, hash_by_property_name: 'user.key')
|
67
67
|
values = values_and_weights.map do |value, weight|
|
68
68
|
weighted_value(value, weight)
|
69
69
|
end
|
70
70
|
|
71
|
-
Prefab::WeightedValues.new(weighted_values: values)
|
71
|
+
Prefab::WeightedValues.new(weighted_values: values, hash_by_property_name: hash_by_property_name)
|
72
72
|
end
|
73
73
|
|
74
74
|
def weighted_value(string, weight)
|
@@ -77,7 +77,7 @@ class TestConfigValueUnwrapper < Minitest::Test
|
|
77
77
|
)
|
78
78
|
end
|
79
79
|
|
80
|
-
def
|
81
|
-
|
80
|
+
def context_with_key(key)
|
81
|
+
Prefab::Context.new(user: { key: key })
|
82
82
|
end
|
83
83
|
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class TestContext < Minitest::Test
|
6
|
+
EXAMPLE_PROPERTIES = { user: { key: 'some-user-key', name: 'Ted' }, team: { key: 'abc', plan: 'pro' } }.freeze
|
7
|
+
|
8
|
+
def setup
|
9
|
+
Prefab::Context.current = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_initialize_with_empty_context
|
13
|
+
context = Prefab::Context.new({})
|
14
|
+
assert_empty context.contexts
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_initialize_with_named_context
|
18
|
+
named_context = Prefab::Context::NamedContext.new('test', foo: 'bar')
|
19
|
+
context = Prefab::Context.new(named_context)
|
20
|
+
assert_equal 1, context.contexts.size
|
21
|
+
assert_equal named_context, context.contexts['test']
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_initialize_with_hash
|
25
|
+
context = Prefab::Context.new(test: { foo: 'bar' })
|
26
|
+
assert_equal 1, context.contexts.size
|
27
|
+
assert_equal 'bar', context.contexts['test'].get('foo')
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_initialize_with_multiple_hashes
|
31
|
+
context = Prefab::Context.new(test: { foo: 'bar' }, other: { foo: 'baz' })
|
32
|
+
assert_equal 2, context.contexts.size
|
33
|
+
assert_equal 'bar', context.contexts['test'].get('foo')
|
34
|
+
assert_equal 'baz', context.contexts['other'].get('foo')
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_initialize_with_invalid_hash
|
38
|
+
_, err = capture_io do
|
39
|
+
Prefab::Context.new({ foo: 'bar', baz: 'qux' })
|
40
|
+
end
|
41
|
+
|
42
|
+
assert_match '[DEPRECATION] Prefab contexts should be a hash with a key of the context name and a value of a hash',
|
43
|
+
err
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_initialize_with_invalid_argument
|
47
|
+
assert_raises(ArgumentError) { Prefab::Context.new([]) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_current
|
51
|
+
context = Prefab::Context.current
|
52
|
+
assert_instance_of Prefab::Context, context
|
53
|
+
assert_empty context.to_h
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_current_set
|
57
|
+
context = Prefab::Context.new(EXAMPLE_PROPERTIES)
|
58
|
+
Prefab::Context.current = context
|
59
|
+
assert_instance_of Prefab::Context, context
|
60
|
+
assert_equal stringify(EXAMPLE_PROPERTIES), context.to_h
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_merge_with_current
|
64
|
+
context = Prefab::Context.new(EXAMPLE_PROPERTIES)
|
65
|
+
Prefab::Context.current = context
|
66
|
+
assert_equal stringify(EXAMPLE_PROPERTIES), context.to_h
|
67
|
+
|
68
|
+
new_context = Prefab::Context.merge_with_current({ user: { key: 'brand-new', other: 'different' },
|
69
|
+
address: { city: 'New York' } })
|
70
|
+
assert_equal stringify({
|
71
|
+
# Note that the user's `name` from the original
|
72
|
+
# context is not included. This is because we don't _merge_ the new
|
73
|
+
# properties if they collide with an existing context name. We _replace_
|
74
|
+
# them.
|
75
|
+
user: { key: 'brand-new', other: 'different' },
|
76
|
+
team: EXAMPLE_PROPERTIES[:team],
|
77
|
+
address: { city: 'New York' }
|
78
|
+
}),
|
79
|
+
new_context.to_h
|
80
|
+
|
81
|
+
# the original/current context is unchanged
|
82
|
+
assert_equal stringify(EXAMPLE_PROPERTIES), Prefab::Context.current.to_h
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_with_context
|
86
|
+
Prefab::Context.with_context(EXAMPLE_PROPERTIES) do
|
87
|
+
context = Prefab::Context.current
|
88
|
+
assert_equal(stringify(EXAMPLE_PROPERTIES), context.to_h)
|
89
|
+
assert_equal('some-user-key', context['user.key'])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_with_context_nesting
|
94
|
+
Prefab::Context.with_context(EXAMPLE_PROPERTIES) do
|
95
|
+
Prefab::Context.with_context({ user: { key: 'abc', other: 'different' } }) do
|
96
|
+
context = Prefab::Context.current
|
97
|
+
assert_equal({ 'user' => { 'key' => 'abc', 'other' => 'different' } }, context.to_h)
|
98
|
+
end
|
99
|
+
|
100
|
+
context = Prefab::Context.current
|
101
|
+
assert_equal(stringify(EXAMPLE_PROPERTIES), context.to_h)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_setting
|
106
|
+
context = Prefab::Context.new({})
|
107
|
+
context.set('user', { key: 'value' })
|
108
|
+
context[:other] = { key: 'different', something: 'other' }
|
109
|
+
assert_equal(stringify({ user: { key: 'value' }, other: { key: 'different', something: 'other' } }), context.to_h)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_getting
|
113
|
+
context = Prefab::Context.new(EXAMPLE_PROPERTIES)
|
114
|
+
assert_equal('some-user-key', context.get('user.key'))
|
115
|
+
assert_equal('some-user-key', context['user.key'])
|
116
|
+
assert_equal('pro', context.get('team.plan'))
|
117
|
+
assert_equal('pro', context['team.plan'])
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_dot_notation_getting
|
121
|
+
context = Prefab::Context.new({ 'user' => { 'key' => 'value' } })
|
122
|
+
assert_equal('value', context.get('user.key'))
|
123
|
+
assert_equal('value', context['user.key'])
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_dot_notation_getting_with_symbols
|
127
|
+
context = Prefab::Context.new({ user: { key: 'value' } })
|
128
|
+
assert_equal('value', context.get('user.key'))
|
129
|
+
assert_equal('value', context['user.key'])
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_merge
|
133
|
+
context = Prefab::Context.new(EXAMPLE_PROPERTIES)
|
134
|
+
context.merge!(:other, { key: 'different' })
|
135
|
+
assert_equal(stringify(EXAMPLE_PROPERTIES.merge(other: { key: 'different' })), context.to_h)
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_clear
|
139
|
+
context = Prefab::Context.new(EXAMPLE_PROPERTIES)
|
140
|
+
context.clear
|
141
|
+
|
142
|
+
assert_empty context.to_h
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def stringify(hash)
|
148
|
+
hash.map { |k, v| [k.to_s, stringify_keys(v)] }.to_h
|
149
|
+
end
|
150
|
+
|
151
|
+
def stringify_keys(value)
|
152
|
+
if value.is_a?(Hash)
|
153
|
+
value.transform_keys(&:to_s)
|
154
|
+
else
|
155
|
+
value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|