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.
@@ -59,86 +59,86 @@ class TestEvaluationSummaryAggregator < Minitest::Test
59
59
  end
60
60
 
61
61
  def test_sync
62
- awhile_ago = Time.now - 60
63
- now = Time.now
62
+ Timecop.freeze('2023-08-09 15:18:12 -0400') do
63
+ awhile_ago = Time.now - 60
64
+ now = Time.now
64
65
 
65
- client = MockBaseClient.new
66
+ client = MockBaseClient.new
66
67
 
67
- aggregator = nil
68
+ aggregator = nil
68
69
 
69
- Timecop.freeze(awhile_ago) do
70
- # start the aggregator in the past
71
- aggregator = Prefab::EvaluationSummaryAggregator.new(client: client, max_keys: 10,
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
- end
74
-
75
- add_example_data(aggregator)
74
+ end
76
75
 
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
- ]
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
- requests = wait_for_post_requests(client) do
133
- Timecop.freeze(now) do
134
- aggregator.sync
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
- assert_equal [[
139
- '/api/v1/telemetry',
140
- expected_post
141
- ]], requests
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
@@ -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.8.pre.1
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: 2024-12-03 00:00:00.000000000 Z
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: 1.3.1
348
+ version: '0'
342
349
  requirements: []
343
- rubygems_version: 3.2.32
350
+ rubygems_version: 3.4.19
344
351
  signing_key:
345
352
  specification_version: 4
346
353
  summary: Prefab Ruby Infrastructure