prefab-cloud-ruby 1.8.8.pre.1 → 1.8.9
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/.github/pull_request_template.md +8 -0
- data/.github/workflows/ruby.yml +1 -1
- data/.tool-versions +1 -1
- data/CHANGELOG.md +94 -81
- data/VERSION +1 -1
- data/compile_protos.sh +4 -2
- data/lib/prefab/caching_http_connection.rb +95 -0
- data/lib/prefab/config_client.rb +7 -8
- data/lib/prefab/context.rb +1 -8
- data/lib/prefab/criteria_evaluator.rb +182 -9
- data/lib/prefab/fixed_size_hash.rb +14 -0
- data/lib/prefab/http_connection.rb +10 -6
- data/lib/prefab/semver.rb +132 -0
- data/lib/prefab-cloud-ruby.rb +3 -0
- data/lib/prefab_pb.rb +4 -23
- data/prefab-cloud-ruby.gemspec +27 -39
- data/test/support/common_helpers.rb +17 -13
- data/test/test_caching_http_connection.rb +218 -0
- data/test/test_context.rb +0 -15
- data/test/test_context_shape.rb +1 -1
- data/test/test_criteria_evaluator.rb +333 -0
- data/test/test_evaluation_summary_aggregator.rb +69 -69
- data/test/test_fixed_size_hash.rb +119 -0
- data/test/test_semver.rb +108 -0
- metadata +12 -5
@@ -59,86 +59,86 @@ class TestEvaluationSummaryAggregator < Minitest::Test
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def test_sync
|
62
|
-
|
63
|
-
|
62
|
+
Timecop.freeze('2023-08-09 15:18:12 -0400') do
|
63
|
+
awhile_ago = Time.now - 60
|
64
|
+
now = Time.now
|
64
65
|
|
65
|
-
|
66
|
+
client = MockBaseClient.new
|
66
67
|
|
67
|
-
|
68
|
+
aggregator = nil
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
Timecop.freeze(awhile_ago) do
|
71
|
+
# start the aggregator in the past
|
72
|
+
aggregator = Prefab::EvaluationSummaryAggregator.new(client: client, max_keys: 10,
|
72
73
|
sync_interval: EFFECTIVELY_NEVER)
|
73
|
-
|
74
|
-
|
75
|
-
add_example_data(aggregator)
|
74
|
+
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
76
|
+
add_example_data(aggregator)
|
77
|
+
|
78
|
+
expected_post = PrefabProto::TelemetryEvents.new(
|
79
|
+
instance_hash: client.instance_hash,
|
80
|
+
events: [
|
81
|
+
PrefabProto::TelemetryEvent.new(
|
82
|
+
summaries: PrefabProto::ConfigEvaluationSummaries.new(
|
83
|
+
start: awhile_ago.to_i * 1000,
|
84
|
+
end: now.to_i * 1000,
|
85
|
+
summaries: [
|
86
|
+
PrefabProto::ConfigEvaluationSummary.new(
|
87
|
+
key: 'config-1',
|
88
|
+
type: :CONFIG,
|
89
|
+
counters: [
|
90
|
+
PrefabProto::ConfigEvaluationCounter.new(
|
91
|
+
config_id: 1,
|
92
|
+
selected_index: 2,
|
93
|
+
config_row_index: 3,
|
94
|
+
conditional_value_index: 4,
|
95
|
+
weighted_value_index: 5,
|
96
|
+
selected_value: EXAMPLE_VALUE_1,
|
97
|
+
count: 3
|
98
|
+
),
|
99
|
+
PrefabProto::ConfigEvaluationCounter.new(
|
100
|
+
config_id: 1,
|
101
|
+
selected_index: 3,
|
102
|
+
config_row_index: 7,
|
103
|
+
conditional_value_index: 8,
|
104
|
+
weighted_value_index: 10,
|
105
|
+
selected_value: EXAMPLE_VALUE_2,
|
106
|
+
count: 1
|
107
|
+
)
|
108
|
+
]
|
109
|
+
),
|
110
|
+
PrefabProto::ConfigEvaluationSummary.new(
|
111
|
+
key: 'config-2',
|
112
|
+
type: :FEATURE_FLAG,
|
113
|
+
counters: [
|
114
|
+
PrefabProto::ConfigEvaluationCounter.new(
|
115
|
+
config_id: 2,
|
116
|
+
selected_index: 3,
|
117
|
+
config_row_index: 5,
|
118
|
+
conditional_value_index: 7,
|
119
|
+
weighted_value_index: 6,
|
120
|
+
selected_value: EXAMPLE_VALUE_1,
|
121
|
+
count: 9
|
122
|
+
)
|
123
|
+
]
|
124
|
+
)
|
125
|
+
]
|
126
|
+
)
|
125
127
|
)
|
126
128
|
]
|
127
129
|
)
|
128
|
-
)
|
129
|
-
]
|
130
|
-
)
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
|
131
|
+
requests = wait_for_post_requests(client) do
|
132
|
+
Timecop.freeze(now) do
|
133
|
+
aggregator.sync
|
134
|
+
end
|
135
135
|
end
|
136
|
-
end
|
137
136
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
assert_equal [[
|
138
|
+
'/api/v1/telemetry',
|
139
|
+
expected_post
|
140
|
+
]], requests
|
141
|
+
end
|
142
142
|
end
|
143
143
|
|
144
144
|
private
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'test_helper'
|
5
|
+
|
6
|
+
module Prefab
|
7
|
+
class FixedSizeHashTest < Minitest::Test
|
8
|
+
def setup
|
9
|
+
@max_size = 3
|
10
|
+
@hash = FixedSizeHash.new(@max_size)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_acts_like_a_regular_hash_when_under_max_size
|
14
|
+
@hash[:a] = 1
|
15
|
+
@hash[:b] = 2
|
16
|
+
|
17
|
+
assert_equal 1, @hash[:a]
|
18
|
+
assert_equal 2, @hash[:b]
|
19
|
+
assert_equal 2, @hash.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_enforces_max_size_by_evicting_first_added_item
|
23
|
+
@hash[:a] = 1
|
24
|
+
@hash[:b] = 2
|
25
|
+
@hash[:c] = 3
|
26
|
+
assert_equal @max_size, @hash.size
|
27
|
+
|
28
|
+
@hash[:d] = 4
|
29
|
+
assert_equal @max_size, @hash.size
|
30
|
+
assert_nil @hash[:a] # First item should be evicted
|
31
|
+
assert_equal 4, @hash[:d]
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_updating_existing_key_does_not_trigger_eviction
|
35
|
+
@hash[:a] = 1
|
36
|
+
@hash[:b] = 2
|
37
|
+
@hash[:c] = 3
|
38
|
+
|
39
|
+
@hash[:b] = 'new value' # Update existing key
|
40
|
+
|
41
|
+
assert_equal @max_size, @hash.size
|
42
|
+
assert_equal 1, @hash[:a] # First item should still be present
|
43
|
+
assert_equal 'new value', @hash[:b]
|
44
|
+
assert_equal 3, @hash[:c]
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_handles_nil_values
|
48
|
+
@hash[:a] = nil
|
49
|
+
@hash[:b] = 2
|
50
|
+
@hash[:c] = 3
|
51
|
+
@hash[:d] = 4
|
52
|
+
|
53
|
+
assert_nil @hash[:a] # First item should be evicted
|
54
|
+
assert_equal 4, @hash[:d]
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_preserves_hash_methods
|
58
|
+
@hash[:a] = 1
|
59
|
+
@hash[:b] = 2
|
60
|
+
|
61
|
+
assert_equal [:a, :b], @hash.keys
|
62
|
+
assert_equal [1, 2], @hash.values
|
63
|
+
assert @hash.key?(:a)
|
64
|
+
refute @hash.key?(:z)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_handles_string_keys
|
68
|
+
@hash['a'] = 1
|
69
|
+
@hash['b'] = 2
|
70
|
+
@hash['c'] = 3
|
71
|
+
@hash['d'] = 4
|
72
|
+
|
73
|
+
assert_nil @hash['a'] # First item should be evicted
|
74
|
+
assert_equal 4, @hash['d']
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_handles_object_keys
|
78
|
+
key1 = Object.new
|
79
|
+
key2 = Object.new
|
80
|
+
key3 = Object.new
|
81
|
+
key4 = Object.new
|
82
|
+
|
83
|
+
@hash[key1] = 1
|
84
|
+
@hash[key2] = 2
|
85
|
+
@hash[key3] = 3
|
86
|
+
@hash[key4] = 4
|
87
|
+
|
88
|
+
assert_nil @hash[key1] # First item should be evicted
|
89
|
+
assert_equal 4, @hash[key4]
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_can_be_initialized_empty
|
93
|
+
assert_equal 0, @hash.size
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_enumerable_methods
|
97
|
+
@hash[:a] = 1
|
98
|
+
@hash[:b] = 2
|
99
|
+
|
100
|
+
mapped = @hash.map { |k, v| [k, v * 2] }.to_h
|
101
|
+
assert_equal({ a: 2, b: 4 }, mapped)
|
102
|
+
|
103
|
+
filtered = @hash.select { |_, v| v > 1 }
|
104
|
+
assert_equal({ b: 2 }, filtered.to_h)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_clear_maintains_max_size_constraint
|
108
|
+
@hash[:a] = 1
|
109
|
+
@hash[:b] = 2
|
110
|
+
@hash.clear
|
111
|
+
|
112
|
+
assert_equal 0, @hash.size
|
113
|
+
|
114
|
+
# Should still enforce max size after clear
|
115
|
+
(@max_size + 1).times { |i| @hash[i] = i }
|
116
|
+
assert_equal @max_size, @hash.size
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/test/test_semver.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
class TestSemanticVersion < Minitest::Test
|
3
|
+
def test_parse_valid_version
|
4
|
+
version = SemanticVersion.parse('1.2.3')
|
5
|
+
assert_equal 1, version.major
|
6
|
+
assert_equal 2, version.minor
|
7
|
+
assert_equal 3, version.patch
|
8
|
+
assert_nil version.prerelease
|
9
|
+
assert_nil version.build_metadata
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_parse_version_with_prerelease
|
13
|
+
version = SemanticVersion.parse('1.2.3-alpha.1')
|
14
|
+
assert_equal 1, version.major
|
15
|
+
assert_equal 2, version.minor
|
16
|
+
assert_equal 3, version.patch
|
17
|
+
assert_equal 'alpha.1', version.prerelease
|
18
|
+
assert_nil version.build_metadata
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_parse_version_with_build_metadata
|
22
|
+
version = SemanticVersion.parse('1.2.3+build.123')
|
23
|
+
assert_equal 1, version.major
|
24
|
+
assert_equal 2, version.minor
|
25
|
+
assert_equal 3, version.patch
|
26
|
+
assert_nil version.prerelease
|
27
|
+
assert_equal 'build.123', version.build_metadata
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_parse_full_version
|
31
|
+
version = SemanticVersion.parse('1.2.3-alpha.1+build.123')
|
32
|
+
assert_equal 1, version.major
|
33
|
+
assert_equal 2, version.minor
|
34
|
+
assert_equal 3, version.patch
|
35
|
+
assert_equal 'alpha.1', version.prerelease
|
36
|
+
assert_equal 'build.123', version.build_metadata
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_parse_invalid_version
|
40
|
+
assert_raises(ArgumentError) { SemanticVersion.parse('invalid') }
|
41
|
+
assert_raises(ArgumentError) { SemanticVersion.parse('1.2') }
|
42
|
+
assert_raises(ArgumentError) { SemanticVersion.parse('1.2.3.4') }
|
43
|
+
assert_raises(ArgumentError) { SemanticVersion.parse('') }
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_parse_quietly
|
47
|
+
assert_nil SemanticVersion.parse_quietly('invalid')
|
48
|
+
refute_nil SemanticVersion.parse_quietly('1.2.3')
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_to_string
|
52
|
+
assert_equal '1.2.3', SemanticVersion.parse('1.2.3').to_s
|
53
|
+
assert_equal '1.2.3-alpha.1', SemanticVersion.parse('1.2.3-alpha.1').to_s
|
54
|
+
assert_equal '1.2.3+build.123', SemanticVersion.parse('1.2.3+build.123').to_s
|
55
|
+
assert_equal '1.2.3-alpha.1+build.123', SemanticVersion.parse('1.2.3-alpha.1+build.123').to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_equality
|
59
|
+
v1 = SemanticVersion.parse('1.2.3')
|
60
|
+
v2 = SemanticVersion.parse('1.2.3')
|
61
|
+
v3 = SemanticVersion.parse('1.2.4')
|
62
|
+
v4 = SemanticVersion.parse('1.2.3-alpha')
|
63
|
+
v5 = SemanticVersion.parse('1.2.3+build.123')
|
64
|
+
|
65
|
+
assert_equal v1, v2
|
66
|
+
refute_equal v1, v3
|
67
|
+
refute_equal v1, v4
|
68
|
+
assert_equal v1, v5 # build metadata is ignored in equality
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_comparison
|
72
|
+
versions = [
|
73
|
+
'1.0.0-alpha',
|
74
|
+
'1.0.0-alpha.1',
|
75
|
+
'1.0.0-beta.2',
|
76
|
+
'1.0.0-beta.11',
|
77
|
+
'1.0.0-rc.1',
|
78
|
+
'1.0.0',
|
79
|
+
'2.0.0',
|
80
|
+
'2.1.0',
|
81
|
+
'2.1.1'
|
82
|
+
].map { |v| SemanticVersion.parse(v) }
|
83
|
+
|
84
|
+
# Test that each version is less than the next version
|
85
|
+
(versions.length - 1).times do |i|
|
86
|
+
assert versions[i] < versions[i + 1], "Expected #{versions[i]} < #{versions[i + 1]}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_prerelease_comparison
|
91
|
+
# Test specific prerelease comparison cases
|
92
|
+
cases = [
|
93
|
+
['1.0.0-alpha', '1.0.0-alpha.1', -1],
|
94
|
+
['1.0.0-alpha.1', '1.0.0-alpha.beta', -1],
|
95
|
+
['1.0.0-alpha.beta', '1.0.0-beta', -1],
|
96
|
+
['1.0.0-beta', '1.0.0-beta.2', -1],
|
97
|
+
['1.0.0-beta.2', '1.0.0-beta.11', -1],
|
98
|
+
['1.0.0-beta.11', '1.0.0-rc.1', -1],
|
99
|
+
['1.0.0-rc.1', '1.0.0', -1]
|
100
|
+
]
|
101
|
+
|
102
|
+
cases.each do |v1_str, v2_str, expected|
|
103
|
+
v1 = SemanticVersion.parse(v1_str)
|
104
|
+
v2 = SemanticVersion.parse(v2_str)
|
105
|
+
assert_equal expected, (v1 <=> v2), "Expected #{v1} <=> #{v2} to be #{expected}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prefab-cloud-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Dwyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -222,6 +222,7 @@ extra_rdoc_files:
|
|
222
222
|
- README.md
|
223
223
|
files:
|
224
224
|
- ".envrc.sample"
|
225
|
+
- ".github/pull_request_template.md"
|
225
226
|
- ".github/workflows/ruby.yml"
|
226
227
|
- ".gitmodules"
|
227
228
|
- ".rubocop.yml"
|
@@ -240,6 +241,7 @@ files:
|
|
240
241
|
- dev/console
|
241
242
|
- dev/script_setup.rb
|
242
243
|
- lib/prefab-cloud-ruby.rb
|
244
|
+
- lib/prefab/caching_http_connection.rb
|
243
245
|
- lib/prefab/client.rb
|
244
246
|
- lib/prefab/config_client.rb
|
245
247
|
- lib/prefab/config_client_presenter.rb
|
@@ -265,6 +267,7 @@ files:
|
|
265
267
|
- lib/prefab/example_contexts_aggregator.rb
|
266
268
|
- lib/prefab/exponential_backoff.rb
|
267
269
|
- lib/prefab/feature_flag_client.rb
|
270
|
+
- lib/prefab/fixed_size_hash.rb
|
268
271
|
- lib/prefab/http_connection.rb
|
269
272
|
- lib/prefab/internal_logger.rb
|
270
273
|
- lib/prefab/javascript_stub.rb
|
@@ -277,6 +280,7 @@ files:
|
|
277
280
|
- lib/prefab/prefab.rb
|
278
281
|
- lib/prefab/rate_limit_cache.rb
|
279
282
|
- lib/prefab/resolved_config_presenter.rb
|
283
|
+
- lib/prefab/semver.rb
|
280
284
|
- lib/prefab/sse_config_client.rb
|
281
285
|
- lib/prefab/time_helpers.rb
|
282
286
|
- lib/prefab/weighted_value_resolver.rb
|
@@ -292,6 +296,7 @@ files:
|
|
292
296
|
- test/support/mock_base_client.rb
|
293
297
|
- test/support/mock_config_client.rb
|
294
298
|
- test/support/mock_config_loader.rb
|
299
|
+
- test/test_caching_http_connection.rb
|
295
300
|
- test/test_client.rb
|
296
301
|
- test/test_config_client.rb
|
297
302
|
- test/test_config_loader.rb
|
@@ -308,6 +313,7 @@ files:
|
|
308
313
|
- test/test_example_contexts_aggregator.rb
|
309
314
|
- test/test_exponential_backoff.rb
|
310
315
|
- test/test_feature_flag_client.rb
|
316
|
+
- test/test_fixed_size_hash.rb
|
311
317
|
- test/test_helper.rb
|
312
318
|
- test/test_integration.rb
|
313
319
|
- test/test_internal_logger.rb
|
@@ -319,6 +325,7 @@ files:
|
|
319
325
|
- test/test_options.rb
|
320
326
|
- test/test_prefab.rb
|
321
327
|
- test/test_rate_limit_cache.rb
|
328
|
+
- test/test_semver.rb
|
322
329
|
- test/test_sse_config_client.rb
|
323
330
|
- test/test_weighted_value_resolver.rb
|
324
331
|
homepage: http://github.com/prefab-cloud/prefab-cloud-ruby
|
@@ -336,11 +343,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
336
343
|
version: '0'
|
337
344
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
338
345
|
requirements:
|
339
|
-
- - "
|
346
|
+
- - ">="
|
340
347
|
- !ruby/object:Gem::Version
|
341
|
-
version:
|
348
|
+
version: '0'
|
342
349
|
requirements: []
|
343
|
-
rubygems_version: 3.
|
350
|
+
rubygems_version: 3.4.19
|
344
351
|
signing_key:
|
345
352
|
specification_version: 4
|
346
353
|
summary: Prefab Ruby Infrastructure
|