quonfig 0.0.10 → 0.0.12

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +94 -0
  4. data/lib/quonfig/caching_http_connection.rb +3 -3
  5. data/lib/quonfig/client.rb +22 -27
  6. data/lib/quonfig/config_loader.rb +5 -1
  7. data/lib/quonfig/config_store.rb +10 -6
  8. data/lib/quonfig/context.rb +5 -4
  9. data/lib/quonfig/datadir.rb +4 -10
  10. data/lib/quonfig/dev_context.rb +4 -2
  11. data/lib/quonfig/duration.rb +2 -2
  12. data/lib/quonfig/encryption.rb +12 -16
  13. data/lib/quonfig/errors/invalid_environment_error.rb +1 -3
  14. data/lib/quonfig/errors/invalid_sdk_key_error.rb +6 -7
  15. data/lib/quonfig/errors/missing_env_var_error.rb +0 -3
  16. data/lib/quonfig/errors/missing_environment_error.rb +1 -1
  17. data/lib/quonfig/errors/uninitialized_error.rb +1 -1
  18. data/lib/quonfig/evaluation.rb +11 -8
  19. data/lib/quonfig/evaluator.rb +34 -37
  20. data/lib/quonfig/fixed_size_hash.rb +1 -0
  21. data/lib/quonfig/http_connection.rb +2 -4
  22. data/lib/quonfig/internal_logger.rb +63 -27
  23. data/lib/quonfig/murmer3.rb +2 -2
  24. data/lib/quonfig/options.rb +62 -75
  25. data/lib/quonfig/periodic_sync.rb +1 -1
  26. data/lib/quonfig/quonfig.rb +3 -3
  27. data/lib/quonfig/reason.rb +2 -1
  28. data/lib/quonfig/resolver.rb +8 -9
  29. data/lib/quonfig/semantic_logger_filter.rb +4 -3
  30. data/lib/quonfig/semver.rb +6 -8
  31. data/lib/quonfig/sse_config_client.rb +14 -15
  32. data/lib/quonfig/stdlib_formatter.rb +3 -3
  33. data/lib/quonfig/telemetry/context_shape_aggregator.rb +2 -3
  34. data/lib/quonfig/telemetry/example_contexts_aggregator.rb +1 -1
  35. data/lib/quonfig/telemetry/telemetry_reporter.rb +1 -0
  36. data/lib/quonfig/time_helpers.rb +2 -0
  37. data/lib/quonfig/version.rb +5 -0
  38. data/lib/quonfig.rb +2 -1
  39. data/quonfig.gemspec +29 -165
  40. metadata +24 -193
  41. data/.claude/rules/constitution.md +0 -81
  42. data/.claude/rules/git-safety.md +0 -11
  43. data/.claude/rules/issue-tracking.md +0 -13
  44. data/.claude/rules/testing-workflow.md +0 -28
  45. data/.envrc.sample +0 -3
  46. data/.github/CODEOWNERS +0 -2
  47. data/.github/pull_request_template.md +0 -8
  48. data/.github/workflows/release.yml +0 -49
  49. data/.github/workflows/ruby.yml +0 -60
  50. data/.github/workflows/test.yaml +0 -40
  51. data/.rubocop.yml +0 -13
  52. data/.tool-versions +0 -1
  53. data/CLAUDE.md +0 -29
  54. data/CODEOWNERS +0 -1
  55. data/Gemfile +0 -26
  56. data/Gemfile.lock +0 -177
  57. data/Rakefile +0 -64
  58. data/VERSION +0 -1
  59. data/dev/allocation_stats +0 -60
  60. data/dev/benchmark +0 -40
  61. data/dev/console +0 -12
  62. data/dev/script_setup.rb +0 -18
  63. data/test/fixtures/datafile.json +0 -87
  64. data/test/integration/test_context_precedence.rb +0 -112
  65. data/test/integration/test_datadir_environment.rb +0 -54
  66. data/test/integration/test_dev_overrides.rb +0 -40
  67. data/test/integration/test_enabled.rb +0 -478
  68. data/test/integration/test_enabled_with_contexts.rb +0 -64
  69. data/test/integration/test_get.rb +0 -136
  70. data/test/integration/test_get_feature_flag.rb +0 -28
  71. data/test/integration/test_get_or_raise.rb +0 -60
  72. data/test/integration/test_get_weighted_values.rb +0 -34
  73. data/test/integration/test_helpers.rb +0 -667
  74. data/test/integration/test_helpers_test.rb +0 -73
  75. data/test/integration/test_post.rb +0 -44
  76. data/test/integration/test_telemetry.rb +0 -170
  77. data/test/support/common_helpers.rb +0 -106
  78. data/test/support/mock_base_client.rb +0 -27
  79. data/test/support/mock_config_loader.rb +0 -1
  80. data/test/test_bound_client.rb +0 -109
  81. data/test/test_caching_http_connection.rb +0 -218
  82. data/test/test_client.rb +0 -255
  83. data/test/test_client_network_mode.rb +0 -136
  84. data/test/test_client_telemetry.rb +0 -175
  85. data/test/test_config_loader.rb +0 -70
  86. data/test/test_context.rb +0 -139
  87. data/test/test_context_shape.rb +0 -37
  88. data/test/test_context_shape_aggregator.rb +0 -126
  89. data/test/test_datadir.rb +0 -203
  90. data/test/test_details_getters.rb +0 -242
  91. data/test/test_dev_context.rb +0 -163
  92. data/test/test_duration.rb +0 -37
  93. data/test/test_encryption.rb +0 -16
  94. data/test/test_evaluation_summaries_aggregator.rb +0 -180
  95. data/test/test_evaluator.rb +0 -285
  96. data/test/test_example_contexts_aggregator.rb +0 -119
  97. data/test/test_exponential_backoff.rb +0 -44
  98. data/test/test_fixed_size_hash.rb +0 -119
  99. data/test/test_helper.rb +0 -17
  100. data/test/test_http_connection.rb +0 -81
  101. data/test/test_internal_logger.rb +0 -34
  102. data/test/test_options.rb +0 -198
  103. data/test/test_rate_limit_cache.rb +0 -44
  104. data/test/test_reason.rb +0 -79
  105. data/test/test_rename.rb +0 -65
  106. data/test/test_resolver.rb +0 -291
  107. data/test/test_semantic_logger_filter.rb +0 -144
  108. data/test/test_semver.rb +0 -108
  109. data/test/test_should_log.rb +0 -186
  110. data/test/test_sse_config_client.rb +0 -297
  111. data/test/test_stdlib_formatter.rb +0 -195
  112. data/test/test_telemetry_reporter.rb +0 -209
  113. data/test/test_typed_getters.rb +0 -131
  114. data/test/test_types.rb +0 -141
  115. data/test/test_weighted_value_resolver.rb +0 -84
@@ -1,119 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minitest/autorun'
4
- require 'test_helper'
5
-
6
- module Quonfig
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_helper.rb DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minitest/autorun'
4
- require 'minitest/focus'
5
- require 'minitest/reporters'
6
- Minitest::Reporters.use! unless ENV['RM_INFO']
7
-
8
- require 'quonfig'
9
-
10
- Dir.glob(File.join(File.dirname(__FILE__), 'support', '**', '*.rb')).each do |file|
11
- require file
12
- end
13
-
14
- Minitest::Test.class_eval do
15
- include CommonHelpers
16
- extend CommonHelpers
17
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
- require 'base64'
5
- require 'json'
6
-
7
- module Quonfig
8
- class HttpConnectionTest < Minitest::Test
9
- URI = 'https://primary.quonfig.com'
10
- SDK_KEY = 'abc.def.123'
11
-
12
- def test_uses_basic_auth_with_username_1
13
- conn = HttpConnection.new(URI, SDK_KEY).connection
14
- expected = 'Basic ' + Base64.strict_encode64("1:#{SDK_KEY}")
15
- assert_equal expected, conn.headers['Authorization']
16
- end
17
-
18
- def test_json_content_type_and_accept_headers
19
- conn = HttpConnection.new(URI, SDK_KEY).connection
20
- assert_equal 'application/json', conn.headers['Content-Type']
21
- assert_equal 'application/json', conn.headers['Accept']
22
- end
23
-
24
- def test_x_quonfig_sdk_version_header
25
- conn = HttpConnection.new(URI, SDK_KEY).connection
26
- assert_equal "ruby-#{Quonfig::VERSION}", conn.headers['X-Quonfig-SDK-Version']
27
- end
28
-
29
- def test_no_protobuf_content_type
30
- conn = HttpConnection.new(URI, SDK_KEY).connection
31
- refute_equal 'application/x-protobuf', conn.headers['Content-Type']
32
- refute_equal 'application/x-protobuf', conn.headers['Accept']
33
- end
34
-
35
- def test_post_serializes_body_as_json
36
- stubs = Faraday::Adapter::Test::Stubs.new
37
- captured_body = nil
38
- captured_content_type = nil
39
- stubs.post('/telemetry') do |env|
40
- captured_body = env.body
41
- captured_content_type = env.request_headers['Content-Type']
42
- [200, {}, '']
43
- end
44
-
45
- http = HttpConnection.new(URI, SDK_KEY)
46
- http.define_singleton_method(:connection) do |headers = {}|
47
- Faraday.new(URI) do |conn|
48
- conn.headers.merge!(HttpConnection::JSON_HEADERS.merge(headers))
49
- conn.adapter :test, stubs
50
- end
51
- end
52
-
53
- body = { hello: 'world', n: 3 }
54
- http.post('/telemetry', body)
55
-
56
- assert_equal 'application/json', captured_content_type
57
- assert_equal body.to_json, captured_body
58
- stubs.verify_stubbed_calls
59
- end
60
- end
61
-
62
- class OptionsDefaultApiUrlsTest < Minitest::Test
63
- def test_default_api_urls_use_quonfig_domain
64
- assert_includes Options::DEFAULT_API_URLS, 'https://primary.quonfig.com'
65
- refute(Options::DEFAULT_API_URLS.any? { |s| s.include?('reforge.com') })
66
- end
67
-
68
- def test_telemetry_destination_honors_quonfig_domain_env
69
- with_env('QUONFIG_DOMAIN', 'quonfig-staging.com') do
70
- assert_equal 'https://telemetry.quonfig-staging.com',
71
- Options.new.telemetry_destination
72
- end
73
- end
74
-
75
- def test_telemetry_destination_default
76
- with_env('QUONFIG_DOMAIN', nil) do
77
- assert_equal 'https://telemetry.quonfig.com', Options.new.telemetry_destination
78
- end
79
- end
80
- end
81
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class TestInternalLogger < Minitest::Test
6
-
7
- def teardown
8
- # using_quonfig_log_filter! mutates the shared @@instances list — restore
9
- # the default :warn level so it doesn't bleed into other tests' $logs.
10
- Quonfig::InternalLogger.class_variable_get(:@@instances).each do |logger|
11
- logger.level = :warn
12
- end
13
- super
14
- end
15
-
16
- def test_levels
17
- logger_a = Quonfig::InternalLogger.new(A)
18
- logger_b = Quonfig::InternalLogger.new(B)
19
-
20
- assert_equal :warn, logger_a.level
21
- assert_equal :warn, logger_b.level
22
-
23
- Quonfig::InternalLogger.using_quonfig_log_filter!
24
- assert_equal :trace, logger_a.level
25
- assert_equal :trace, logger_b.level
26
- end
27
-
28
- end
29
-
30
- class A
31
- end
32
-
33
- class B
34
- end
data/test/test_options.rb DELETED
@@ -1,198 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class TestOptions < Minitest::Test
6
- API_KEY = 'abcdefg'
7
-
8
- def test_default_api_urls_point_to_quonfig
9
- # DEFAULT_API_URLS is the hardcoded fallback when neither QUONFIG_DOMAIN
10
- # nor an explicit api_urls: kwarg is set. Domain-derivation happens at
11
- # construction time, not at constant load time — see derive_api_urls.
12
- assert_equal [
13
- 'https://primary.quonfig.com',
14
- 'https://secondary.quonfig.com',
15
- ], Quonfig::Options::DEFAULT_API_URLS
16
- end
17
-
18
- def test_overriding_api_urls
19
- assert_equal Quonfig::Options::DEFAULT_API_URLS, Quonfig::Options.new.api_urls
20
-
21
- # a plain string ends up wrapped in an array
22
- api_url = 'https://example.com'
23
- assert_equal [api_url], Quonfig::Options.new(api_urls: api_url).api_urls
24
-
25
- api_urls = ['https://example.com', 'https://example2.com']
26
- assert_equal api_urls, Quonfig::Options.new(api_urls: api_urls).api_urls
27
- end
28
-
29
- def test_derive_stream_url_prepends_stream_to_hostname
30
- assert_equal 'https://stream.primary.quonfig.com',
31
- Quonfig::Options.derive_stream_url('https://primary.quonfig.com')
32
- end
33
-
34
- def test_derive_stream_url_preserves_port
35
- assert_equal 'http://stream.localhost:6550',
36
- Quonfig::Options.derive_stream_url('http://localhost:6550')
37
- end
38
-
39
- def test_derive_stream_url_preserves_scheme_and_path
40
- assert_equal 'http://stream.api.example.com/base',
41
- Quonfig::Options.derive_stream_url('http://api.example.com/base')
42
- end
43
-
44
- def test_derive_stream_url_with_eu_subdomain
45
- assert_equal 'https://stream.primary.eu.quonfig.com',
46
- Quonfig::Options.derive_stream_url('https://primary.eu.quonfig.com')
47
- end
48
-
49
- def test_works_with_named_arguments
50
- assert_equal API_KEY, Quonfig::Options.new(sdk_key: API_KEY).sdk_key
51
- end
52
-
53
- def test_works_with_hash
54
- assert_equal API_KEY, Quonfig::Options.new({ sdk_key: API_KEY }).sdk_key
55
- end
56
-
57
- def test_sdk_key_reads_from_quonfig_backend_sdk_key
58
- with_env('QUONFIG_BACKEND_SDK_KEY', 'env-key') do
59
- assert_equal 'env-key', Quonfig::Options.new.sdk_key
60
- end
61
- end
62
-
63
- def test_environment_reads_from_quonfig_environment
64
- with_env('QUONFIG_ENVIRONMENT', 'staging') do
65
- assert_equal 'staging', Quonfig::Options.new.environment
66
- end
67
- end
68
-
69
- def test_environment_explicit_overrides_env_var
70
- with_env('QUONFIG_ENVIRONMENT', 'staging') do
71
- assert_equal 'production', Quonfig::Options.new(environment: 'production').environment
72
- end
73
- end
74
-
75
- def test_enable_sse_defaults_true
76
- assert_equal true, Quonfig::Options.new.enable_sse
77
- assert_equal false, Quonfig::Options.new(enable_sse: false).enable_sse
78
- end
79
-
80
- def test_enable_polling_defaults_true
81
- assert_equal true, Quonfig::Options.new.enable_polling
82
- assert_equal false, Quonfig::Options.new(enable_polling: false).enable_polling
83
- end
84
-
85
- def test_datadir_reads_from_quonfig_dir_env
86
- with_env('QUONFIG_DIR', '/tmp/some/workspace') do
87
- assert_equal '/tmp/some/workspace', Quonfig::Options.new.datadir
88
- end
89
- end
90
-
91
- def test_datadir_explicit_overrides_env_var
92
- with_env('QUONFIG_DIR', '/tmp/env/workspace') do
93
- assert_equal '/tmp/explicit', Quonfig::Options.new(datadir: '/tmp/explicit').datadir
94
- end
95
- end
96
-
97
- def test_datadir_predicate
98
- assert_equal false, Quonfig::Options.new.datadir?
99
- assert_equal true, Quonfig::Options.new(datadir: '/tmp/ws').datadir?
100
- end
101
-
102
- def test_local_only_uses_datadir_presence
103
- refute Quonfig::Options.new.local_only?
104
- assert Quonfig::Options.new(datadir: '/tmp/ws').local_only?
105
- end
106
-
107
- def test_collect_max_paths
108
- assert_equal 1000, Quonfig::Options.new.collect_max_paths
109
- assert_equal 100, Quonfig::Options.new(collect_max_paths: 100).collect_max_paths
110
- end
111
-
112
- def test_collect_max_evaluation_summaries
113
- assert_equal 100_000, Quonfig::Options.new.collect_max_evaluation_summaries
114
- assert_equal 0, Quonfig::Options.new(collect_evaluation_summaries: false).collect_max_evaluation_summaries
115
- assert_equal 3,
116
- Quonfig::Options.new(collect_max_evaluation_summaries: 3).collect_max_evaluation_summaries
117
- end
118
-
119
- def test_context_upload_mode_periodic
120
- options = Quonfig::Options.new(context_upload_mode: :periodic_example, context_max_size: 100)
121
- assert_equal 100, options.collect_max_example_contexts
122
-
123
- options = Quonfig::Options.new(context_upload_mode: :none)
124
- assert_equal 0, options.collect_max_example_contexts
125
- end
126
-
127
- def test_context_upload_mode_shapes_only
128
- options = Quonfig::Options.new(context_upload_mode: :shapes_only, context_max_size: 100)
129
- assert_equal 100, options.collect_max_shapes
130
-
131
- options = Quonfig::Options.new(context_upload_mode: :none)
132
- assert_equal 0, options.collect_max_shapes
133
- end
134
-
135
- def test_context_upload_mode_none
136
- options = Quonfig::Options.new(context_upload_mode: :none)
137
- assert_equal 0, options.collect_max_example_contexts
138
-
139
- options = Quonfig::Options.new(context_upload_mode: :none)
140
- assert_equal 0, options.collect_max_shapes
141
- end
142
-
143
- # ---- QUONFIG_DOMAIN tests (qfg-w6gg) ----
144
- # A single env var `QUONFIG_DOMAIN` governs api, sse, and telemetry URL
145
- # derivation. Resolution order (highest wins): explicit kwargs >
146
- # QUONFIG_DOMAIN > hardcoded default 'quonfig.com'.
147
-
148
- def test_default_domain_is_quonfig_com
149
- with_env('QUONFIG_DOMAIN', nil) do
150
- options = Quonfig::Options.new
151
- assert_equal [
152
- 'https://primary.quonfig.com',
153
- 'https://secondary.quonfig.com',
154
- ], options.api_urls
155
- assert_equal 'https://telemetry.quonfig.com', options.telemetry_destination
156
- end
157
- end
158
-
159
- def test_quonfig_domain_env_var_derives_all_urls
160
- with_env('QUONFIG_DOMAIN', 'quonfig-staging.com') do
161
- options = Quonfig::Options.new
162
- assert_equal [
163
- 'https://primary.quonfig-staging.com',
164
- 'https://secondary.quonfig-staging.com',
165
- ], options.api_urls
166
- assert_equal [
167
- 'https://stream.primary.quonfig-staging.com',
168
- 'https://stream.secondary.quonfig-staging.com',
169
- ], options.sse_api_urls
170
- assert_equal 'https://telemetry.quonfig-staging.com', options.telemetry_destination
171
- end
172
- end
173
-
174
- def test_explicit_api_urls_override_quonfig_domain
175
- with_env('QUONFIG_DOMAIN', 'quonfig-staging.com') do
176
- options = Quonfig::Options.new(api_urls: ['http://localhost:8080'])
177
- assert_equal ['http://localhost:8080'], options.api_urls
178
- end
179
- end
180
-
181
- def test_explicit_telemetry_url_overrides_quonfig_domain
182
- with_env('QUONFIG_DOMAIN', 'quonfig-staging.com') do
183
- options = Quonfig::Options.new(telemetry_url: 'http://localhost:6555')
184
- assert_equal 'http://localhost:6555', options.telemetry_destination
185
- end
186
- end
187
-
188
- def test_quonfig_telemetry_url_env_var_no_longer_read
189
- # QUONFIG_TELEMETRY_URL has been removed. Setting it must not affect
190
- # anything; the default (quonfig.com) wins.
191
- with_env('QUONFIG_DOMAIN', nil) do
192
- with_env('QUONFIG_TELEMETRY_URL', 'https://should-be-ignored.example.com') do
193
- assert_equal 'https://telemetry.quonfig.com',
194
- Quonfig::Options.new.telemetry_destination
195
- end
196
- end
197
- end
198
- end
@@ -1,44 +0,0 @@
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 = Quonfig::RateLimitCache.new(5)
9
- cache.set('key')
10
- assert cache.fresh?('key')
11
- end
12
-
13
- def test_fresh_with_no_set
14
- cache = Quonfig::RateLimitCache.new(5)
15
- refute cache.fresh?('key')
16
- end
17
-
18
- def test_get_after_expiration
19
- cache = Quonfig::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 = Quonfig::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
data/test/test_reason.rb DELETED
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minitest/autorun'
4
- require 'quonfig/reason'
5
-
6
- # Tests Quonfig::Reason in isolation — uses plain Structs so the test does not
7
- # depend on PrefabProto (which is not loaded by the current bootstrap).
8
- class TestReason < Minitest::Test
9
- FakeCriterion = Struct.new(:operator)
10
- FakeConditionalValue = Struct.new(:criteria)
11
- FakeRow = Struct.new(:values, :project_env_id)
12
- FakeConfig = Struct.new(:rows)
13
-
14
- ALWAYS_TRUE = FakeCriterion.new(:ALWAYS_TRUE)
15
- PROP_MATCH = FakeCriterion.new(:PROP_IS_ONE_OF)
16
-
17
- DEFAULT_CV = FakeConditionalValue.new([])
18
- ALWAYS_TRUE_CV = FakeConditionalValue.new([ALWAYS_TRUE])
19
- RULE_CV = FakeConditionalValue.new([PROP_MATCH])
20
-
21
- def default_only_config
22
- FakeConfig.new([FakeRow.new([DEFAULT_CV], 0)])
23
- end
24
-
25
- def targeted_config
26
- FakeConfig.new([
27
- FakeRow.new([DEFAULT_CV], 0),
28
- FakeRow.new([RULE_CV, ALWAYS_TRUE_CV], 1)
29
- ])
30
- end
31
-
32
- def test_default_for_default_only_config
33
- reason = Quonfig::Reason.compute(
34
- config: default_only_config,
35
- conditional_value: DEFAULT_CV
36
- )
37
- assert_equal :DEFAULT, reason
38
- end
39
-
40
- def test_rule_match_when_targeting_rule_matched
41
- reason = Quonfig::Reason.compute(
42
- config: targeted_config,
43
- conditional_value: RULE_CV
44
- )
45
- assert_equal :RULE_MATCH, reason
46
- end
47
-
48
- def test_rule_match_when_falling_back_to_always_true_in_targeted_config
49
- reason = Quonfig::Reason.compute(
50
- config: targeted_config,
51
- conditional_value: ALWAYS_TRUE_CV
52
- )
53
- assert_equal :RULE_MATCH, reason
54
- end
55
-
56
- def test_split_when_weighted_value_index_positive
57
- reason = Quonfig::Reason.compute(
58
- config: default_only_config,
59
- conditional_value: DEFAULT_CV,
60
- weighted_value_index: 2
61
- )
62
- assert_equal :SPLIT, reason
63
- end
64
-
65
- def test_weighted_value_index_zero_is_not_split
66
- reason = Quonfig::Reason.compute(
67
- config: default_only_config,
68
- conditional_value: DEFAULT_CV,
69
- weighted_value_index: 0
70
- )
71
- assert_equal :DEFAULT, reason
72
- end
73
-
74
- def test_default_when_only_always_true_criteria_and_no_targeting_rules
75
- config = FakeConfig.new([FakeRow.new([ALWAYS_TRUE_CV], 0)])
76
- reason = Quonfig::Reason.compute(config: config, conditional_value: ALWAYS_TRUE_CV)
77
- assert_equal :DEFAULT, reason
78
- end
79
- end
data/test/test_rename.rb DELETED
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- class TestRename < Minitest::Test
6
- def test_quonfig_module_is_defined
7
- assert defined?(Quonfig), 'Quonfig module must be defined after rename'
8
- end
9
-
10
- def test_reforge_module_is_gone
11
- # NOTE: 'Reforge' intentionally not renamed — this test guards against the old constant leaking back in.
12
- refute Object.const_defined?(:Reforge), 'Reforge constant must be removed after rename'
13
- end
14
-
15
- def test_quonfig_options_exists
16
- assert defined?(Quonfig::Options), 'Quonfig::Options must be defined'
17
- end
18
-
19
- def test_quonfig_client_exists
20
- assert defined?(Quonfig::Client), 'Quonfig::Client must be defined'
21
- end
22
-
23
- def test_quonfig_sdk_key_env_var
24
- # NOTE: the REFORGE_/PREFAB_ keys below are intentionally spelled out in string literals
25
- # so the bulk rename tool does not touch them — we are asserting that the NEW env var name
26
- # is the one the SDK reads.
27
- old_key_a = 'REFORGE_' + 'BACKEND_SDK_KEY'
28
- old_key_b = 'PREFAB_' + 'API_KEY'
29
- original = ENV.to_h.slice('QUONFIG_BACKEND_SDK_KEY', old_key_a, old_key_b)
30
- ENV.delete(old_key_a)
31
- ENV.delete(old_key_b)
32
- ENV['QUONFIG_BACKEND_SDK_KEY'] = 'quonfig-test-key-123'
33
- options = Quonfig::Options.new
34
- assert_equal 'quonfig-test-key-123', options.sdk_key
35
- ensure
36
- ENV.delete('QUONFIG_BACKEND_SDK_KEY')
37
- original&.each { |k, v| ENV[k] = v }
38
- end
39
-
40
- def test_gemspec_file_is_quonfig
41
- root = File.expand_path('..', __dir__)
42
- assert File.exist?(File.join(root, 'quonfig.gemspec')),
43
- 'quonfig.gemspec must exist at repo root'
44
- refute File.exist?(File.join(root, 'sdk-reforge.gemspec')),
45
- 'sdk-reforge.gemspec must be removed'
46
- end
47
-
48
- def test_gemspec_name_is_quonfig
49
- root = File.expand_path('..', __dir__)
50
- spec = Gem::Specification.load(File.join(root, 'quonfig.gemspec'))
51
- assert_equal 'quonfig', spec.name
52
- end
53
-
54
- def test_lib_entrypoint_renamed
55
- root = File.expand_path('..', __dir__)
56
- assert File.exist?(File.join(root, 'lib', 'quonfig.rb')),
57
- 'lib/quonfig.rb must exist'
58
- assert Dir.exist?(File.join(root, 'lib', 'quonfig')),
59
- 'lib/quonfig/ directory must exist'
60
- refute File.exist?(File.join(root, 'lib', 'sdk-reforge.rb')),
61
- 'lib/sdk-reforge.rb must be removed'
62
- refute Dir.exist?(File.join(root, 'lib', 'reforge')),
63
- 'lib/reforge/ directory must be removed'
64
- end
65
- end