prefab-cloud-ruby 0.24.4 → 0.24.6

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/VERSION +1 -1
  4. data/compile_protos.sh +7 -0
  5. data/lib/prefab/client.rb +17 -4
  6. data/lib/prefab/config_client.rb +10 -10
  7. data/lib/prefab/config_value_unwrapper.rb +20 -9
  8. data/lib/prefab/context.rb +39 -7
  9. data/lib/prefab/context_shape_aggregator.rb +1 -1
  10. data/lib/prefab/criteria_evaluator.rb +24 -16
  11. data/lib/prefab/evaluated_keys_aggregator.rb +1 -1
  12. data/lib/prefab/evaluation.rb +48 -0
  13. data/lib/prefab/evaluation_summary_aggregator.rb +85 -0
  14. data/lib/prefab/example_contexts_aggregator.rb +76 -0
  15. data/lib/prefab/exponential_backoff.rb +5 -0
  16. data/lib/prefab/http_connection.rb +5 -1
  17. data/lib/prefab/log_path_aggregator.rb +1 -1
  18. data/lib/prefab/logger_client.rb +12 -13
  19. data/lib/prefab/options.rb +27 -14
  20. data/lib/prefab/periodic_sync.rb +30 -13
  21. data/lib/prefab/rate_limit_cache.rb +41 -0
  22. data/lib/prefab/resolved_config_presenter.rb +2 -4
  23. data/lib/prefab/weighted_value_resolver.rb +1 -1
  24. data/lib/prefab-cloud-ruby.rb +6 -3
  25. data/lib/prefab_pb.rb +11 -1
  26. data/prefab-cloud-ruby.gemspec +14 -5
  27. data/test/support/common_helpers.rb +105 -0
  28. data/test/support/mock_base_client.rb +44 -0
  29. data/test/support/mock_config_client.rb +19 -0
  30. data/test/support/mock_config_loader.rb +1 -0
  31. data/test/test_client.rb +257 -2
  32. data/test/test_config_resolver.rb +25 -24
  33. data/test/test_config_value_unwrapper.rb +22 -32
  34. data/test/test_context_shape_aggregator.rb +0 -1
  35. data/test/test_criteria_evaluator.rb +179 -133
  36. data/test/test_evaluation_summary_aggregator.rb +162 -0
  37. data/test/test_example_contexts_aggregator.rb +238 -0
  38. data/test/test_helper.rb +5 -131
  39. data/test/test_local_config_parser.rb +2 -2
  40. data/test/test_logger.rb +5 -5
  41. data/test/test_options.rb +8 -0
  42. data/test/test_rate_limit_cache.rb +44 -0
  43. data/test/test_weighted_value_resolver.rb +13 -7
  44. metadata +13 -4
  45. data/lib/prefab/evaluated_configs_aggregator.rb +0 -60
  46. data/test/test_evaluated_configs_aggregator.rb +0 -254
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'timecop'
5
+
6
+ class TestEvaluationSummaryAggregator < Minitest::Test
7
+ EFFECTIVELY_NEVER = 99_999 # we sync manually
8
+
9
+ EXAMPLE_VALUE_1 = PrefabProto::ConfigValue.new(bool: true)
10
+ EXAMPLE_VALUE_2 = PrefabProto::ConfigValue.new(bool: false)
11
+
12
+ EXAMPLE_COUNTER = {
13
+ config_id: 1,
14
+ selected_index: 2,
15
+ config_row_index: 3,
16
+ conditional_value_index: 4,
17
+ weighted_value_index: 5,
18
+ seleced_value: EXAMPLE_VALUE_1
19
+ }.freeze
20
+
21
+ def test_increments_counts
22
+ aggregator = Prefab::EvaluationSummaryAggregator.new(client: MockBaseClient.new, max_keys: 10,
23
+ sync_interval: EFFECTIVELY_NEVER)
24
+
25
+ aggregator.record(config_key: 'foo', config_type: 'bar', counter: EXAMPLE_COUNTER)
26
+
27
+ assert_equal 1, aggregator.data[%w[foo bar]][EXAMPLE_COUNTER]
28
+
29
+ 2.times { aggregator.record(config_key: 'foo', config_type: 'bar', counter: EXAMPLE_COUNTER) }
30
+ assert_equal 3, aggregator.data[%w[foo bar]][EXAMPLE_COUNTER]
31
+
32
+ another_counter = EXAMPLE_COUNTER.merge(selected_index: EXAMPLE_COUNTER[:selected_index] + 1)
33
+
34
+ aggregator.record(config_key: 'foo', config_type: 'bar', counter: another_counter)
35
+ assert_equal 3, aggregator.data[%w[foo bar]][EXAMPLE_COUNTER]
36
+ assert_equal 1, aggregator.data[%w[foo bar]][another_counter]
37
+ end
38
+
39
+ def test_prepare_data
40
+ aggregator = Prefab::EvaluationSummaryAggregator.new(client: MockBaseClient.new, max_keys: 10,
41
+ sync_interval: EFFECTIVELY_NEVER)
42
+
43
+ expected = {
44
+ ['config-1', :CONFIG] => {
45
+ { config_id: 1, selected_index: 2, config_row_index: 3, conditional_value_index: 4,
46
+ weighted_value_index: 5, selected_value: EXAMPLE_VALUE_1 } => 3,
47
+ { config_id: 1, selected_index: 3, config_row_index: 7, conditional_value_index: 8,
48
+ weighted_value_index: 10, selected_value: EXAMPLE_VALUE_2 } => 1
49
+ },
50
+ ['config-2', :FEATURE_FLAG] => {
51
+ { config_id: 2, selected_index: 3, config_row_index: 5, conditional_value_index: 7,
52
+ weighted_value_index: 6, selected_value: EXAMPLE_VALUE_1 } => 9
53
+ }
54
+ }
55
+
56
+ add_example_data(aggregator)
57
+ assert_equal expected, aggregator.prepare_data
58
+ assert aggregator.data.empty?
59
+ end
60
+
61
+ def test_sync
62
+ awhile_ago = Time.now - 60
63
+ now = Time.now
64
+
65
+ client = MockBaseClient.new
66
+
67
+ aggregator = nil
68
+
69
+ Timecop.freeze(awhile_ago) do
70
+ # start the aggregator in the past
71
+ aggregator = Prefab::EvaluationSummaryAggregator.new(client: client, max_keys: 10,
72
+ sync_interval: EFFECTIVELY_NEVER)
73
+ end
74
+
75
+ add_example_data(aggregator)
76
+
77
+ expected_post = PrefabProto::TelemetryEvents.new(
78
+ instance_hash: client.instance_hash,
79
+ events: [
80
+ PrefabProto::TelemetryEvent.new(
81
+ summaries:
82
+
83
+ PrefabProto::ConfigEvaluationSummaries.new(
84
+ start: awhile_ago.to_i * 1000,
85
+ end: now.to_i * 1000,
86
+ summaries: [
87
+ PrefabProto::ConfigEvaluationSummary.new(
88
+ key: 'config-1',
89
+ type: :CONFIG,
90
+ counters: [
91
+ PrefabProto::ConfigEvaluationCounter.new(
92
+ config_id: 1,
93
+ selected_index: 2,
94
+ config_row_index: 3,
95
+ conditional_value_index: 4,
96
+ weighted_value_index: 5,
97
+ selected_value: EXAMPLE_VALUE_1,
98
+ count: 3
99
+ ),
100
+ PrefabProto::ConfigEvaluationCounter.new(
101
+ config_id: 1,
102
+ selected_index: 3,
103
+ config_row_index: 7,
104
+ conditional_value_index: 8,
105
+ weighted_value_index: 10,
106
+ selected_value: EXAMPLE_VALUE_2,
107
+ count: 1
108
+ )
109
+ ]
110
+ ),
111
+ PrefabProto::ConfigEvaluationSummary.new(
112
+ key: 'config-2',
113
+ type: :FEATURE_FLAG,
114
+ counters: [
115
+ PrefabProto::ConfigEvaluationCounter.new(
116
+ config_id: 2,
117
+ selected_index: 3,
118
+ config_row_index: 5,
119
+ conditional_value_index: 7,
120
+ weighted_value_index: 6,
121
+ selected_value: EXAMPLE_VALUE_1,
122
+ count: 9
123
+ )
124
+ ]
125
+ )
126
+ ]
127
+ )
128
+ )
129
+ ]
130
+ )
131
+
132
+ requests = wait_for_post_requests(client) do
133
+ Timecop.freeze(now) do
134
+ aggregator.sync
135
+ end
136
+ end
137
+
138
+ assert_equal [[
139
+ '/api/v1/telemetry',
140
+ expected_post
141
+ ]], requests
142
+ end
143
+
144
+ private
145
+
146
+ def add_example_data(aggregator)
147
+ data = {
148
+ ['config-1', :CONFIG] => {
149
+ { config_id: 1, selected_index: 2, config_row_index: 3, conditional_value_index: 4,
150
+ weighted_value_index: 5, selected_value: EXAMPLE_VALUE_1 } => 3,
151
+ { config_id: 1, selected_index: 3, config_row_index: 7, conditional_value_index: 8,
152
+ weighted_value_index: 10, selected_value: EXAMPLE_VALUE_2 } => 1
153
+ },
154
+ ['config-2', :FEATURE_FLAG] => {
155
+ { config_id: 2, selected_index: 3, config_row_index: 5, conditional_value_index: 7,
156
+ weighted_value_index: 6, selected_value: EXAMPLE_VALUE_1 } => 9
157
+ }
158
+ }
159
+
160
+ aggregator.instance_variable_set('@data', data)
161
+ end
162
+ end
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'timecop'
5
+
6
+ class TestExampleContextsAggregator < Minitest::Test
7
+ EFFECTIVELY_NEVER = 99_999 # we sync manually
8
+
9
+ def test_record
10
+ aggregator = Prefab::ExampleContextsAggregator.new(client: MockBaseClient.new, max_contexts: 2,
11
+ sync_interval: EFFECTIVELY_NEVER)
12
+
13
+ context = Prefab::Context.new(
14
+ user: { key: 'abc' },
15
+ device: { key: 'def', mobile: true }
16
+ )
17
+
18
+ aggregator.record(context)
19
+ assert_equal [context], aggregator.data
20
+
21
+ # This doesn't get updated because we already have a context for this user/device
22
+ aggregator.record(context)
23
+ assert_equal [context], aggregator.data
24
+
25
+ new_context = Prefab::Context.new(
26
+ user: { key: 'ghi', admin: true },
27
+ team: { key: '999' }
28
+ )
29
+
30
+ aggregator.record(new_context)
31
+ assert_equal [context, new_context], aggregator.data
32
+
33
+ # this doesn't get recorded because we're at max_contexts
34
+ aggregator.record(Prefab::Context.new(user: { key: 'new' }))
35
+ assert_equal [context, new_context], aggregator.data
36
+ end
37
+
38
+ def test_prepare_data
39
+ aggregator = Prefab::ExampleContextsAggregator.new(client: MockBaseClient.new, max_contexts: 10,
40
+ sync_interval: EFFECTIVELY_NEVER)
41
+
42
+ context = Prefab::Context.new(
43
+ user: { key: 'abc' },
44
+ device: { key: 'def', mobile: true }
45
+ )
46
+
47
+ aggregator.record(context)
48
+
49
+ assert_equal [context], aggregator.prepare_data
50
+ assert aggregator.data.empty?
51
+ end
52
+
53
+ def test_record_with_expiry
54
+ aggregator = Prefab::ExampleContextsAggregator.new(client: MockBaseClient.new, max_contexts: 10,
55
+ sync_interval: EFFECTIVELY_NEVER)
56
+
57
+ context = Prefab::Context.new(
58
+ user: { key: 'abc' },
59
+ device: { key: 'def', mobile: true }
60
+ )
61
+
62
+ aggregator.record(context)
63
+
64
+ assert_equal [context], aggregator.data
65
+
66
+ Timecop.travel(Time.now + (60 * 60) - 1) do
67
+ aggregator.record(context)
68
+
69
+ # This doesn't get updated because we already have a context for this user/device in the timeframe
70
+ assert_equal [context], aggregator.data
71
+ end
72
+
73
+ Timecop.travel(Time.now + ((60 * 60) + 1)) do
74
+ # this is new because we've passed the expiry
75
+ aggregator.record(context)
76
+
77
+ assert_equal [context, context], aggregator.data
78
+ end
79
+ end
80
+
81
+ def test_sync
82
+ now = Time.now
83
+
84
+ client = MockBaseClient.new
85
+
86
+ aggregator = Prefab::ExampleContextsAggregator.new(client: client, max_contexts: 10,
87
+ sync_interval: EFFECTIVELY_NEVER)
88
+
89
+ context = Prefab::Context.new(
90
+ user: { key: 'abc' },
91
+ device: { key: 'def', mobile: true }
92
+ )
93
+ aggregator.record(context)
94
+
95
+ # This is the same as above so we shouldn't get anything new
96
+ aggregator.record(context)
97
+
98
+ aggregator.record(
99
+ Prefab::Context.new(
100
+ user: { key: 'ghi' },
101
+ device: { key: 'jkl', mobile: false }
102
+ )
103
+ )
104
+
105
+ aggregator.record(Prefab::Context.new(user: { key: 'kev', name: 'kevin', age: 48.5 }))
106
+
107
+ assert_equal 3, aggregator.cache.data.size
108
+
109
+ expected_post = PrefabProto::TelemetryEvents.new(
110
+ instance_hash: client.instance_hash,
111
+ events: [
112
+ PrefabProto::TelemetryEvent.new(
113
+ example_contexts: PrefabProto::ExampleContexts.new(
114
+ examples: [
115
+ PrefabProto::ExampleContext.new(
116
+ timestamp: now.utc.to_i * 1000,
117
+ contextSet: PrefabProto::ContextSet.new(
118
+ contexts: [
119
+ PrefabProto::Context.new(
120
+ type: 'user',
121
+ values: {
122
+ 'key' => PrefabProto::ConfigValue.new(string: 'abc')
123
+ }
124
+ ),
125
+ PrefabProto::Context.new(
126
+ type: 'device',
127
+ values: {
128
+ 'key' => PrefabProto::ConfigValue.new(string: 'def'),
129
+ 'mobile' => PrefabProto::ConfigValue.new(bool: true)
130
+ }
131
+ )
132
+ ]
133
+ )
134
+ ),
135
+
136
+ PrefabProto::ExampleContext.new(
137
+ timestamp: now.utc.to_i * 1000,
138
+ contextSet: PrefabProto::ContextSet.new(
139
+ contexts: [
140
+ PrefabProto::Context.new(
141
+ type: 'user',
142
+ values: {
143
+ 'key' => PrefabProto::ConfigValue.new(string: 'ghi')
144
+ }
145
+ ),
146
+ PrefabProto::Context.new(
147
+ type: 'device',
148
+ values: {
149
+ 'key' => PrefabProto::ConfigValue.new(string: 'jkl'),
150
+ 'mobile' => PrefabProto::ConfigValue.new(bool: false)
151
+ }
152
+ )
153
+ ]
154
+ )
155
+ ),
156
+
157
+ PrefabProto::ExampleContext.new(
158
+ timestamp: now.utc.to_i * 1000,
159
+ contextSet: PrefabProto::ContextSet.new(
160
+ contexts: [
161
+ PrefabProto::Context.new(
162
+ type: 'user',
163
+ values: {
164
+ 'key' => PrefabProto::ConfigValue.new(string: 'kev'),
165
+ 'name' => PrefabProto::ConfigValue.new(string: 'kevin'),
166
+ 'age' => PrefabProto::ConfigValue.new(double: 48.5)
167
+ }
168
+ )
169
+ ]
170
+ )
171
+ )
172
+ ]
173
+ )
174
+ )
175
+ ]
176
+ )
177
+
178
+ requests = wait_for_post_requests(client) do
179
+ Timecop.freeze(now + (60 * 60) - 1) do
180
+ aggregator.sync
181
+ end
182
+ end
183
+
184
+ assert_equal [[
185
+ '/api/v1/telemetry',
186
+ expected_post
187
+ ]], requests
188
+
189
+ # this hasn't changed because not enough time has passed
190
+ assert_equal 3, aggregator.cache.data.size
191
+
192
+ # a sync past the expiry should clear the cache
193
+ Timecop.freeze(now + (60 * 60) + 1) do
194
+ # we need a new piece of data for the sync to happen
195
+ aggregator.record(Prefab::Context.new(user: { key: 'bozo', name: 'Bozo', age: 99 }))
196
+
197
+ requests = wait_for_post_requests(client) do
198
+ aggregator.sync
199
+ end
200
+ end
201
+
202
+ expected_post = PrefabProto::TelemetryEvents.new(
203
+ instance_hash: client.instance_hash,
204
+ events: [
205
+ PrefabProto::TelemetryEvent.new(
206
+ example_contexts: PrefabProto::ExampleContexts.new(
207
+ examples: [
208
+ PrefabProto::ExampleContext.new(
209
+ timestamp: (now.utc.to_i + (60 * 60) + 1) * 1000,
210
+ contextSet: PrefabProto::ContextSet.new(
211
+ contexts: [
212
+ PrefabProto::Context.new(
213
+ type: 'user',
214
+ values: {
215
+ 'key' => PrefabProto::ConfigValue.new(string: 'bozo'),
216
+ 'name' => PrefabProto::ConfigValue.new(string: 'Bozo'),
217
+ 'age' => PrefabProto::ConfigValue.new(int: 99)
218
+ }
219
+ )
220
+ ]
221
+ )
222
+ )
223
+ ]
224
+ )
225
+ )
226
+ ]
227
+ )
228
+
229
+ assert_equal [[
230
+ '/api/v1/telemetry',
231
+ expected_post
232
+ ]], requests
233
+
234
+ # The last sync should have cleared the cache of everything except the latest context
235
+ assert_equal 1, aggregator.cache.data.size
236
+ assert_equal ['user:bozo'], aggregator.cache.data.keys
237
+ end
238
+ end
data/test/test_helper.rb CHANGED
@@ -7,137 +7,11 @@ Minitest::Reporters.use!
7
7
 
8
8
  require 'prefab-cloud-ruby'
9
9
 
10
- class MockBaseClient
11
- STAGING_ENV_ID = 1
12
- PRODUCTION_ENV_ID = 2
13
- TEST_ENV_ID = 3
14
- attr_reader :namespace
15
- attr_reader :logger
16
- attr_reader :config_client
17
- attr_reader :options
18
-
19
- def initialize(options = Prefab::Options.new)
20
- @options = options
21
- @namespace = namespace
22
- @logger = Prefab::LoggerClient.new($stdout)
23
- @config_client = MockConfigClient.new
24
- end
25
-
26
- def project_id
27
- 1
28
- end
29
-
30
- def log
31
- @logger
32
- end
33
-
34
- def log_internal(level, message); end
35
-
36
- def context_shape_aggregator; end
37
-
38
- def evaluated_keys_aggregator; end
39
-
40
- def evaluated_configs_aggregator; end
41
-
42
- def config_value(key)
43
- @config_values[key]
44
- end
45
- end
46
-
47
- class MockConfigClient
48
- def initialize(config_values = {})
49
- @config_values = config_values
50
- end
51
-
52
- def get(key, default = nil)
53
- @config_values.fetch(key, default)
54
- end
55
-
56
- def get_config(key)
57
- PrefabProto::Config.new(value: @config_values[key], key: key)
58
- end
59
-
60
- def mock_this_config(key, config_value)
61
- @config_values[key] = config_value
62
- end
63
- end
64
-
65
- class MockConfigLoader
66
- def calc_config; end
67
- end
68
-
69
- private
70
-
71
- def default_ff_rule(variant_idx)
72
- [
73
- Prefab::Rule.new(
74
- criteria: Prefab::Criteria.new(operator: Prefab::Criteria::CriteriaOperator::ALWAYS_TRUE),
75
- variant_weights: [
76
- Prefab::VariantWeight.new(weight: 1000,
77
- variant_idx: variant_idx)
78
- ]
79
- )
80
- ]
81
- end
82
-
83
- def with_env(key, value, &block)
84
- old_value = ENV[key]
85
-
86
- ENV[key] = value
87
- block.call
88
- ensure
89
- ENV[key] = old_value
90
- end
91
-
92
- def new_client(overrides = {})
93
- options = Prefab::Options.new(**{
94
- prefab_config_override_dir: 'none',
95
- prefab_config_classpath_dir: 'test',
96
- prefab_envs: ['unit_tests'],
97
- prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
98
- }.merge(overrides))
99
-
100
- Prefab::Client.new(options)
101
- end
102
-
103
- def string_list(values)
104
- PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: values))
105
- end
106
-
107
- def inject_config(client, config)
108
- resolver = client.config_client.instance_variable_get('@config_resolver')
109
- store = resolver.instance_variable_get('@local_store')
110
-
111
- store[config.key] = { config: config }
10
+ Dir.glob(File.join(File.dirname(__FILE__), 'support', '**', '*.rb')).each do |file|
11
+ require file
112
12
  end
113
13
 
114
- def inject_project_env_id(client, project_env_id)
115
- resolver = client.config_client.instance_variable_get('@config_resolver')
116
- resolver.project_env_id = project_env_id
117
- end
118
-
119
- def wait_for_post_requests(client)
120
- max_wait = 2
121
- sleep_time = 0.01
122
-
123
- requests = []
124
-
125
- client.define_singleton_method(:post) do |*params|
126
- requests.push(params)
127
-
128
- OpenStruct.new(status: 200)
129
- end
130
-
131
- yield
132
-
133
- # let the flush thread run
134
- wait_time = 0
135
- while requests.empty?
136
- wait_time += sleep_time
137
- sleep sleep_time
138
-
139
- raise "Waited #{max_wait} seconds for the flush thread to run, but it never did" if wait_time > max_wait
140
- end
141
-
142
- requests
14
+ MiniTest::Test.class_eval do
15
+ include CommonHelpers
16
+ extend CommonHelpers
143
17
  end
@@ -34,7 +34,7 @@ class TestLocalConfigParser < Minitest::Test
34
34
  assert_equal 1, config.rows[0].values.size
35
35
 
36
36
  value_row = config.rows[0].values[0]
37
- assert_equal 'all-features', Prefab::ConfigValueUnwrapper.unwrap(value_row.value, key, {})
37
+ assert_equal 'all-features', Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, key, {}).unwrap
38
38
  end
39
39
 
40
40
  def test_flag_in_user_key
@@ -53,7 +53,7 @@ class TestLocalConfigParser < Minitest::Test
53
53
  assert_equal 1, config.rows[0].values[0].criteria.size
54
54
 
55
55
  value_row = config.rows[0].values[0]
56
- assert_equal true, Prefab::ConfigValueUnwrapper.unwrap(value_row.value, key, {})
56
+ assert_equal true, Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, key, {}).unwrap
57
57
 
58
58
  assert_equal 'user.key', value_row.criteria[0].property_name
59
59
  assert_equal :PROP_IS_ONE_OF, value_row.criteria[0].operator
data/test/test_logger.rb CHANGED
@@ -65,20 +65,20 @@ class TestLogger < Minitest::Test
65
65
  assert_equal ::Logger::INFO,
66
66
  @logger.level_of('app.models.user'), 'PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL is info'
67
67
 
68
- @logger.set_config_client(MockConfigClient.new({}))
68
+ @logger.config_client = MockConfigClient.new({})
69
69
  assert_equal ::Logger::WARN,
70
70
  @logger.level_of('app.models.user'), 'default is warn'
71
71
 
72
- @logger.set_config_client(MockConfigClient.new('log-level.app' => :INFO))
72
+ @logger.config_client = MockConfigClient.new('log-level.app' => :INFO)
73
73
  assert_equal ::Logger::INFO,
74
74
  @logger.level_of('app.models.user')
75
75
 
76
- @logger.set_config_client(MockConfigClient.new('log-level.app' => :DEBUG))
76
+ @logger.config_client = MockConfigClient.new('log-level.app' => :DEBUG)
77
77
  assert_equal ::Logger::DEBUG,
78
78
  @logger.level_of('app.models.user')
79
79
 
80
- @logger.set_config_client(MockConfigClient.new('log-level.app' => :DEBUG,
81
- 'log-level.app.models' => :ERROR))
80
+ @logger.config_client = MockConfigClient.new('log-level.app' => :DEBUG,
81
+ 'log-level.app.models' => :ERROR)
82
82
  assert_equal ::Logger::ERROR,
83
83
  @logger.level_of('app.models.user'), 'test leveling'
84
84
  end
data/test/test_options.rb CHANGED
@@ -41,4 +41,12 @@ class TestOptions < Minitest::Test
41
41
  collect_logs: false)
42
42
  assert_equal 0, options.collect_max_paths
43
43
  end
44
+
45
+ def test_collect_max_evaluation_summaries
46
+ assert_equal 0, Prefab::Options.new.collect_max_evaluation_summaries
47
+ assert_equal 100_000, Prefab::Options.new(collect_evaluation_summaries: true).collect_max_evaluation_summaries
48
+ assert_equal 3,
49
+ Prefab::Options.new(collect_evaluation_summaries: true,
50
+ collect_max_evaluation_summaries: 3).collect_max_evaluation_summaries
51
+ end
44
52
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'timecop'
5
+
6
+ class RateLimitCacheTest < Minitest::Test
7
+ def test_set_and_fresh
8
+ cache = Prefab::RateLimitCache.new(5)
9
+ cache.set('key')
10
+ assert cache.fresh?('key')
11
+ end
12
+
13
+ def test_fresh_with_no_set
14
+ cache = Prefab::RateLimitCache.new(5)
15
+ refute cache.fresh?('key')
16
+ end
17
+
18
+ def test_get_after_expiration
19
+ cache = Prefab::RateLimitCache.new(5)
20
+
21
+ Timecop.freeze(Time.now - 6) do
22
+ cache.set('key')
23
+ assert cache.fresh?('key')
24
+ end
25
+
26
+ refute cache.fresh?('key')
27
+
28
+ # but the data is still there
29
+ assert cache.data.get('key')
30
+ end
31
+
32
+ def test_prune
33
+ cache = Prefab::RateLimitCache.new(5)
34
+
35
+ Timecop.freeze(Time.now - 6) do
36
+ cache.set('key')
37
+ assert cache.fresh?('key')
38
+ end
39
+
40
+ cache.prune
41
+
42
+ refute cache.fresh?('key')
43
+ end
44
+ end
@@ -8,30 +8,36 @@ class TestWeightedValueResolver < Minitest::Test
8
8
  def test_resolving_single_value
9
9
  values = weighted_values([['abc', 1]])
10
10
  resolver = Prefab::WeightedValueResolver.new(values, KEY, nil)
11
- assert_equal 'abc', resolver.resolve.value.string
11
+ assert_equal 'abc', resolver.resolve[0].value.string
12
+ assert_equal 0, resolver.resolve[1]
12
13
  end
13
14
 
14
15
  def test_resolving_multiple_values_evenly_distributed
15
16
  values = weighted_values([['abc', 1], ['def', 1]])
16
17
 
17
18
  resolver = Prefab::WeightedValueResolver.new(values, KEY, 'user:001')
18
- assert_equal 'abc', resolver.resolve.value.string
19
+ assert_equal 'abc', resolver.resolve[0].value.string
20
+ assert_equal 0, resolver.resolve[1]
19
21
 
20
22
  resolver = Prefab::WeightedValueResolver.new(values, KEY, 'user:456')
21
- assert_equal 'def', resolver.resolve.value.string
23
+ assert_equal 'def', resolver.resolve[0].value.string
24
+ assert_equal 1, resolver.resolve[1]
22
25
  end
23
26
 
24
27
  def test_resolving_multiple_values_unevenly_distributed
25
28
  values = weighted_values([['abc', 1], ['def', 98], ['ghi', 1]])
26
29
 
27
30
  resolver = Prefab::WeightedValueResolver.new(values, KEY, 'user:456')
28
- assert_equal 'def', resolver.resolve.value.string
31
+ assert_equal 'def', resolver.resolve[0].value.string
32
+ assert_equal 1, resolver.resolve[1]
29
33
 
30
34
  resolver = Prefab::WeightedValueResolver.new(values, KEY, 'user:103')
31
- assert_equal 'ghi', resolver.resolve.value.string
35
+ assert_equal 'ghi', resolver.resolve[0].value.string
36
+ assert_equal 2, resolver.resolve[1]
32
37
 
33
38
  resolver = Prefab::WeightedValueResolver.new(values, KEY, 'user:119')
34
- assert_equal 'abc', resolver.resolve.value.string
39
+ assert_equal 'abc', resolver.resolve[0].value.string
40
+ assert_equal 0, resolver.resolve[1]
35
41
  end
36
42
 
37
43
  def test_resolving_multiple_values_with_simulation
@@ -39,7 +45,7 @@ class TestWeightedValueResolver < Minitest::Test
39
45
  results = {}
40
46
 
41
47
  10_000.times do |i|
42
- result = Prefab::WeightedValueResolver.new(values, KEY, "user:#{i}").resolve.value.string
48
+ result = Prefab::WeightedValueResolver.new(values, KEY, "user:#{i}").resolve[0].value.string
43
49
  results[result] ||= 0
44
50
  results[result] += 1
45
51
  end