http 5.3.1 → 6.0.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/CHANGELOG.md +241 -41
- data/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/UPGRADING.md +491 -0
- data/http.gemspec +32 -29
- data/lib/http/base64.rb +11 -1
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +232 -136
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +126 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +25 -1
- data/lib/http/feature.rb +65 -5
- data/lib/http/features/auto_deflate.rb +124 -17
- data/lib/http/features/auto_inflate.rb +38 -15
- data/lib/http/features/caching/entry.rb +178 -0
- data/lib/http/features/caching/in_memory_store.rb +63 -0
- data/lib/http/features/caching.rb +216 -0
- data/lib/http/features/digest_auth.rb +234 -0
- data/lib/http/features/instrumentation.rb +97 -17
- data/lib/http/features/logging.rb +183 -5
- data/lib/http/features/normalize_uri.rb +17 -0
- data/lib/http/features/raise_error.rb +18 -3
- data/lib/http/form_data/composite_io.rb +106 -0
- data/lib/http/form_data/file.rb +95 -0
- data/lib/http/form_data/multipart/param.rb +62 -0
- data/lib/http/form_data/multipart.rb +106 -0
- data/lib/http/form_data/part.rb +52 -0
- data/lib/http/form_data/readable.rb +58 -0
- data/lib/http/form_data/urlencoded.rb +175 -0
- data/lib/http/form_data/version.rb +8 -0
- data/lib/http/form_data.rb +102 -0
- data/lib/http/headers/known.rb +3 -0
- data/lib/http/headers/normalizer.rb +17 -36
- data/lib/http/headers.rb +172 -65
- data/lib/http/mime_type/adapter.rb +24 -9
- data/lib/http/mime_type/json.rb +19 -4
- data/lib/http/mime_type.rb +21 -3
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +172 -125
- data/lib/http/redirector.rb +80 -75
- data/lib/http/request/body.rb +87 -6
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +76 -16
- data/lib/http/request.rb +214 -98
- data/lib/http/response/body.rb +103 -18
- data/lib/http/response/inflater.rb +35 -7
- data/lib/http/response/parser.rb +98 -4
- data/lib/http/response/status/reasons.rb +2 -4
- data/lib/http/response/status.rb +141 -31
- data/lib/http/response.rb +219 -61
- data/lib/http/retriable/delay_calculator.rb +38 -11
- data/lib/http/retriable/errors.rb +21 -0
- data/lib/http/retriable/performer.rb +82 -38
- data/lib/http/session.rb +280 -0
- data/lib/http/timeout/global.rb +147 -34
- data/lib/http/timeout/null.rb +155 -9
- data/lib/http/timeout/per_operation.rb +139 -18
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +289 -124
- data/lib/http/version.rb +2 -1
- data/lib/http.rb +11 -2
- data/sig/deps.rbs +122 -0
- data/sig/http.rbs +1619 -0
- data/test/http/base64_test.rb +28 -0
- data/test/http/client_test.rb +739 -0
- data/test/http/connection_test.rb +1533 -0
- data/test/http/content_type_test.rb +190 -0
- data/test/http/errors_test.rb +28 -0
- data/test/http/feature_test.rb +49 -0
- data/test/http/features/auto_deflate_test.rb +317 -0
- data/test/http/features/auto_inflate_test.rb +213 -0
- data/test/http/features/caching_test.rb +942 -0
- data/test/http/features/digest_auth_test.rb +996 -0
- data/test/http/features/instrumentation_test.rb +246 -0
- data/test/http/features/logging_test.rb +654 -0
- data/test/http/features/normalize_uri_test.rb +41 -0
- data/test/http/features/raise_error_test.rb +77 -0
- data/test/http/form_data/composite_io_test.rb +215 -0
- data/test/http/form_data/file_test.rb +255 -0
- data/test/http/form_data/fixtures/the-http-gem.info +1 -0
- data/test/http/form_data/multipart_test.rb +303 -0
- data/test/http/form_data/part_test.rb +90 -0
- data/test/http/form_data/urlencoded_test.rb +164 -0
- data/test/http/form_data_test.rb +232 -0
- data/test/http/headers/normalizer_test.rb +93 -0
- data/test/http/headers_test.rb +888 -0
- data/test/http/mime_type/json_test.rb +39 -0
- data/test/http/mime_type_test.rb +150 -0
- data/test/http/options/base_uri_test.rb +148 -0
- data/test/http/options/body_test.rb +21 -0
- data/test/http/options/features_test.rb +38 -0
- data/test/http/options/form_test.rb +21 -0
- data/test/http/options/headers_test.rb +32 -0
- data/test/http/options/json_test.rb +21 -0
- data/test/http/options/merge_test.rb +78 -0
- data/test/http/options/new_test.rb +37 -0
- data/test/http/options/proxy_test.rb +32 -0
- data/test/http/options_test.rb +575 -0
- data/test/http/redirector_test.rb +639 -0
- data/test/http/request/body_test.rb +318 -0
- data/test/http/request/builder_test.rb +623 -0
- data/test/http/request/writer_test.rb +391 -0
- data/test/http/request_test.rb +1733 -0
- data/test/http/response/body_test.rb +292 -0
- data/test/http/response/parser_test.rb +105 -0
- data/test/http/response/status_test.rb +322 -0
- data/test/http/response_test.rb +502 -0
- data/test/http/retriable/delay_calculator_test.rb +194 -0
- data/test/http/retriable/errors_test.rb +71 -0
- data/test/http/retriable/performer_test.rb +551 -0
- data/test/http/session_test.rb +424 -0
- data/test/http/timeout/global_test.rb +239 -0
- data/test/http/timeout/null_test.rb +218 -0
- data/test/http/timeout/per_operation_test.rb +220 -0
- data/test/http/uri/normalizer_test.rb +89 -0
- data/test/http/uri_test.rb +1140 -0
- data/test/http/version_test.rb +15 -0
- data/test/http_test.rb +818 -0
- data/test/regression_tests.rb +27 -0
- data/test/support/dummy_server/encoding_routes.rb +47 -0
- data/test/support/dummy_server/routes.rb +201 -0
- data/test/support/dummy_server/servlet.rb +81 -0
- data/test/support/dummy_server.rb +200 -0
- data/{spec → test}/support/fakeio.rb +2 -2
- data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
- data/test/support/http_handling_shared/timeout_tests.rb +134 -0
- data/test/support/http_handling_shared.rb +11 -0
- data/test/support/proxy_server.rb +207 -0
- data/test/support/servers/runner.rb +67 -0
- data/{spec → test}/support/simplecov.rb +11 -2
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +108 -168
- data/.github/workflows/ci.yml +0 -67
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.rubocop/layout.yml +0 -8
- data/.rubocop/metrics.yml +0 -4
- data/.rubocop/rspec.yml +0 -9
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -219
- data/.yardopts +0 -2
- data/CHANGES_OLD.md +0 -1002
- data/Gemfile +0 -51
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/lib/http/headers/mixin.rb +0 -34
- data/lib/http/retriable/client.rb +0 -37
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +0 -556
- data/spec/lib/http/connection_spec.rb +0 -88
- data/spec/lib/http/content_type_spec.rb +0 -47
- data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
- data/spec/lib/http/features/instrumentation_spec.rb +0 -81
- data/spec/lib/http/features/logging_spec.rb +0 -65
- data/spec/lib/http/features/raise_error_spec.rb +0 -62
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers/normalizer_spec.rb +0 -52
- data/spec/lib/http/headers_spec.rb +0 -527
- data/spec/lib/http/options/body_spec.rb +0 -15
- data/spec/lib/http/options/features_spec.rb +0 -33
- data/spec/lib/http/options/form_spec.rb +0 -15
- data/spec/lib/http/options/headers_spec.rb +0 -24
- data/spec/lib/http/options/json_spec.rb +0 -15
- data/spec/lib/http/options/merge_spec.rb +0 -68
- data/spec/lib/http/options/new_spec.rb +0 -30
- data/spec/lib/http/options/proxy_spec.rb +0 -20
- data/spec/lib/http/options_spec.rb +0 -13
- data/spec/lib/http/redirector_spec.rb +0 -530
- data/spec/lib/http/request/body_spec.rb +0 -211
- data/spec/lib/http/request/writer_spec.rb +0 -121
- data/spec/lib/http/request_spec.rb +0 -234
- data/spec/lib/http/response/body_spec.rb +0 -85
- data/spec/lib/http/response/parser_spec.rb +0 -74
- data/spec/lib/http/response/status_spec.rb +0 -253
- data/spec/lib/http/response_spec.rb +0 -262
- data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
- data/spec/lib/http/retriable/performer_spec.rb +0 -302
- data/spec/lib/http/uri/normalizer_spec.rb +0 -95
- data/spec/lib/http/uri_spec.rb +0 -71
- data/spec/lib/http_spec.rb +0 -535
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -89
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/dummy_server/servlet.rb +0 -203
- data/spec/support/dummy_server.rb +0 -44
- data/spec/support/fuubar.rb +0 -21
- data/spec/support/http_handling_shared.rb +0 -190
- data/spec/support/proxy_server.rb +0 -39
- data/spec/support/servers/config.rb +0 -11
- data/spec/support/servers/runner.rb +0 -19
- data/spec/support/ssl_helper.rb +0 -104
- /data/{spec → test}/support/capture_warning.rb +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class FormDataTest < Minitest::Test
|
|
6
|
+
cover "HTTP::FormData*"
|
|
7
|
+
|
|
8
|
+
def fixture_path
|
|
9
|
+
@fixture_path ||= Pathname.new(__dir__).join("form_data/fixtures/the-http-gem.info").realpath
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# --- create ---
|
|
13
|
+
|
|
14
|
+
def test_create_returns_urlencoded_when_no_files
|
|
15
|
+
assert_instance_of HTTP::FormData::Urlencoded, HTTP::FormData.create({ foo: :bar })
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_create_returns_multipart_when_file_param
|
|
19
|
+
file = HTTP::FormData::File.new(fixture_path.to_s)
|
|
20
|
+
|
|
21
|
+
assert_instance_of HTTP::FormData::Multipart, HTTP::FormData.create({ foo: :bar, baz: file })
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_create_returns_multipart_when_file_in_array_param
|
|
25
|
+
file = HTTP::FormData::File.new(fixture_path.to_s)
|
|
26
|
+
|
|
27
|
+
assert_instance_of HTTP::FormData::Multipart, HTTP::FormData.create({ foo: :bar, baz: [file] })
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_create_returns_empty_urlencoded_for_nil_data
|
|
31
|
+
result = HTTP::FormData.create(nil)
|
|
32
|
+
|
|
33
|
+
assert_instance_of HTTP::FormData::Urlencoded, result
|
|
34
|
+
assert_equal "", result.to_s
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_create_includes_file_content_in_multipart
|
|
38
|
+
file = HTTP::FormData::File.new(StringIO.new("file content"))
|
|
39
|
+
result = HTTP::FormData.create({ name: file })
|
|
40
|
+
|
|
41
|
+
assert_instance_of HTTP::FormData::Multipart, result
|
|
42
|
+
assert_includes result.to_s, "file content"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_create_encodes_urlencoded_content
|
|
46
|
+
result = HTTP::FormData.create({ foo: "bar" })
|
|
47
|
+
|
|
48
|
+
assert_instance_of HTTP::FormData::Urlencoded, result
|
|
49
|
+
assert_equal "foo=bar", result.to_s
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_create_passes_encoder_to_urlencoded
|
|
53
|
+
custom_encoder = proc { |data| data.map { |k, v| "#{k}:#{v}" }.join(",") }
|
|
54
|
+
result = HTTP::FormData.create({ foo: "bar" }, encoder: custom_encoder)
|
|
55
|
+
|
|
56
|
+
assert_instance_of HTTP::FormData::Urlencoded, result
|
|
57
|
+
assert_equal "foo:bar", result.to_s
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_create_accepts_to_h_objects
|
|
61
|
+
obj = Object.new
|
|
62
|
+
def obj.to_h = { foo: "bar" }
|
|
63
|
+
|
|
64
|
+
result = HTTP::FormData.create(obj)
|
|
65
|
+
|
|
66
|
+
assert_instance_of HTTP::FormData::Urlencoded, result
|
|
67
|
+
assert_equal "foo=bar", result.to_s
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# --- multipart? detection ---
|
|
71
|
+
|
|
72
|
+
def test_string_values_are_not_multipart
|
|
73
|
+
assert_instance_of HTTP::FormData::Urlencoded, HTTP::FormData.create({ foo: "bar", baz: "qux" })
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_array_of_strings_is_not_multipart
|
|
77
|
+
assert_instance_of HTTP::FormData::Urlencoded, HTTP::FormData.create({ foo: %w[bar baz] })
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_empty_array_is_not_multipart
|
|
81
|
+
assert_instance_of HTTP::FormData::Urlencoded, HTTP::FormData.create({ foo: [] })
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_part_value_is_multipart
|
|
85
|
+
part = HTTP::FormData::Part.new("hello", content_type: "text/plain")
|
|
86
|
+
|
|
87
|
+
assert_instance_of HTTP::FormData::Multipart, HTTP::FormData.create({ foo: part })
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_array_containing_part_is_multipart
|
|
91
|
+
part = HTTP::FormData::Part.new("hello", content_type: "text/plain")
|
|
92
|
+
|
|
93
|
+
assert_instance_of HTTP::FormData::Multipart, HTTP::FormData.create({ foo: [part] })
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_to_ary_containing_part_is_multipart
|
|
97
|
+
part = HTTP::FormData::Part.new("hello")
|
|
98
|
+
obj = Object.new
|
|
99
|
+
obj.define_singleton_method(:to_ary) { [part] }
|
|
100
|
+
|
|
101
|
+
assert_instance_of HTTP::FormData::Multipart, HTTP::FormData.create({ foo: obj })
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def test_create_multipart_preserves_all_params
|
|
105
|
+
file = HTTP::FormData::File.new(StringIO.new("content"))
|
|
106
|
+
body = HTTP::FormData.create({ user: "ixti", file: file }).to_s
|
|
107
|
+
|
|
108
|
+
assert_includes body, "ixti"
|
|
109
|
+
assert_includes body, "content"
|
|
110
|
+
assert_includes body, "user"
|
|
111
|
+
assert_includes body, "file"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# --- ensure_data ---
|
|
115
|
+
|
|
116
|
+
def test_ensure_data_with_hash
|
|
117
|
+
assert_equal({ foo: :bar }, HTTP::FormData.ensure_data({ foo: :bar }))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_ensure_data_with_array
|
|
121
|
+
data = [%i[foo bar], %i[foo baz]]
|
|
122
|
+
|
|
123
|
+
assert_equal data, HTTP::FormData.ensure_data(data)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_ensure_data_with_enumerator
|
|
127
|
+
assert_instance_of Enumerator, HTTP::FormData.ensure_data(Enumerator.new { |y| y << %i[foo bar] })
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def test_ensure_data_with_to_h
|
|
131
|
+
obj = Object.new
|
|
132
|
+
def obj.to_h = { foo: :bar }
|
|
133
|
+
|
|
134
|
+
assert_equal({ foo: :bar }, HTTP::FormData.ensure_data(obj))
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_ensure_data_with_nil
|
|
138
|
+
result = HTTP::FormData.ensure_data(nil)
|
|
139
|
+
|
|
140
|
+
assert_instance_of Array, result
|
|
141
|
+
assert_empty result
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_ensure_data_with_invalid_input
|
|
145
|
+
error = assert_raises(HTTP::FormData::Error) { HTTP::FormData.ensure_data(42) }
|
|
146
|
+
|
|
147
|
+
assert_includes error.message, "42"
|
|
148
|
+
assert_includes error.message, "is neither Enumerable nor responds to :to_h"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_ensure_data_with_false_raises
|
|
152
|
+
assert_raises(HTTP::FormData::Error) { HTTP::FormData.ensure_data(false) }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def test_ensure_data_returns_same_array_object
|
|
156
|
+
input = [%i[foo bar]]
|
|
157
|
+
|
|
158
|
+
assert_same input, HTTP::FormData.ensure_data(input)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_ensure_data_returns_same_hash_object
|
|
162
|
+
input = { foo: :bar }
|
|
163
|
+
|
|
164
|
+
assert_same input, HTTP::FormData.ensure_data(input)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def test_ensure_data_error_message_uses_inspect
|
|
168
|
+
obj = Object.new
|
|
169
|
+
def obj.inspect = "CUSTOM_INSPECT"
|
|
170
|
+
def obj.to_s = "CUSTOM_TO_S"
|
|
171
|
+
|
|
172
|
+
error = assert_raises(HTTP::FormData::Error) { HTTP::FormData.ensure_data(obj) }
|
|
173
|
+
|
|
174
|
+
assert_includes error.message, "CUSTOM_INSPECT"
|
|
175
|
+
refute_includes error.message, "CUSTOM_TO_S"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# --- ensure_hash ---
|
|
179
|
+
|
|
180
|
+
def test_ensure_hash_with_hash
|
|
181
|
+
assert_equal({ foo: :bar }, HTTP::FormData.ensure_hash({ foo: :bar }))
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_ensure_hash_with_to_h
|
|
185
|
+
obj = Object.new
|
|
186
|
+
def obj.to_h = { foo: :bar }
|
|
187
|
+
|
|
188
|
+
assert_equal({ foo: :bar }, HTTP::FormData.ensure_hash(obj))
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_ensure_hash_with_nil
|
|
192
|
+
result = HTTP::FormData.ensure_hash(nil)
|
|
193
|
+
|
|
194
|
+
assert_instance_of Hash, result
|
|
195
|
+
assert_empty result
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def test_ensure_hash_with_invalid_input
|
|
199
|
+
error = assert_raises(HTTP::FormData::Error) { HTTP::FormData.ensure_hash(42) }
|
|
200
|
+
|
|
201
|
+
assert_includes error.message, "42"
|
|
202
|
+
assert_includes error.message, "is neither Hash nor responds to :to_h"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def test_ensure_hash_with_hash_subclass
|
|
206
|
+
subclass = Class.new(Hash)
|
|
207
|
+
obj = subclass.new
|
|
208
|
+
obj[:foo] = :bar
|
|
209
|
+
|
|
210
|
+
assert_same obj, HTTP::FormData.ensure_hash(obj)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def test_ensure_hash_returns_same_hash_object
|
|
214
|
+
input = { foo: :bar }
|
|
215
|
+
|
|
216
|
+
assert_same input, HTTP::FormData.ensure_hash(input)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def test_ensure_hash_with_false_raises
|
|
220
|
+
assert_raises(HTTP::FormData::Error) { HTTP::FormData.ensure_hash(false) }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def test_ensure_hash_error_message_uses_inspect
|
|
224
|
+
obj = Object.new
|
|
225
|
+
def obj.inspect = "HASH_INSPECT"
|
|
226
|
+
def obj.to_s = "HASH_TO_S"
|
|
227
|
+
|
|
228
|
+
error = assert_raises(HTTP::FormData::Error) { HTTP::FormData.ensure_hash(obj) }
|
|
229
|
+
|
|
230
|
+
assert_includes error.message, "HASH_INSPECT"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class HTTPHeadersNormalizerTest < Minitest::Test
|
|
6
|
+
cover "HTTP::Headers::Normalizer*"
|
|
7
|
+
|
|
8
|
+
def normalizer
|
|
9
|
+
@normalizer ||= HTTP::Headers::Normalizer.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def setup
|
|
13
|
+
super
|
|
14
|
+
Thread.current[HTTP::Headers::Normalizer::CACHE_KEY] = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_call_normalizes_the_header
|
|
18
|
+
assert_equal "Content-Type", normalizer.call("content_type")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_call_returns_a_non_frozen_string
|
|
22
|
+
refute_predicate normalizer.call("content_type"), :frozen?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_call_returns_mutable_strings
|
|
26
|
+
normalized_headers = Array.new(3) { normalizer.call("content_type") }
|
|
27
|
+
|
|
28
|
+
# All values should be equal
|
|
29
|
+
assert_equal 1, normalized_headers.uniq.size
|
|
30
|
+
# Each should be a distinct object
|
|
31
|
+
assert_equal normalized_headers.size, normalized_headers.map(&:object_id).uniq.size
|
|
32
|
+
# None should be frozen
|
|
33
|
+
assert normalized_headers.none?(&:frozen?)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if RUBY_ENGINE == "ruby"
|
|
37
|
+
def test_call_allocates_minimal_memory_for_normalization_of_the_same_header
|
|
38
|
+
normalizer.call("accept") # Ensure normalizer is pre-allocated
|
|
39
|
+
|
|
40
|
+
# On first call it is expected to allocate during normalization
|
|
41
|
+
assert_allocations(Array: 1, MatchData: 1, String: 6) do
|
|
42
|
+
normalizer.call("content_type")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# On subsequent call it is expected to only allocate copy of a cached string
|
|
46
|
+
assert_allocations(Array: 0, MatchData: 0, String: 1) do
|
|
47
|
+
normalizer.call("content_type")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_call_calls_to_s_on_the_name_argument
|
|
53
|
+
name = fake(to_s: "content_type")
|
|
54
|
+
|
|
55
|
+
assert_equal "Content-Type", normalizer.call(name)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_call_caches_the_normalized_value_and_reuses_it
|
|
59
|
+
first = normalizer.call("content_type")
|
|
60
|
+
second = normalizer.call("content_type")
|
|
61
|
+
|
|
62
|
+
assert_equal first, second
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_call_passes_through_names_already_in_canonical_form
|
|
66
|
+
assert_equal "Content-Type", normalizer.call("Content-Type")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_call_normalizes_underscore_separated_names
|
|
70
|
+
assert_equal "Content-Type", normalizer.call("content_type")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_call_normalizes_dash_separated_names
|
|
74
|
+
assert_equal "Content-Type", normalizer.call("content-type")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_call_raises_header_error_for_invalid_header_names
|
|
78
|
+
err = assert_raises(HTTP::HeaderError) { normalizer.call("invalid header") }
|
|
79
|
+
assert_includes err.message, "invalid header"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_call_includes_inspect_formatted_name_in_invalid_header_error
|
|
83
|
+
err = assert_raises(HTTP::HeaderError) { normalizer.call("invalid header") }
|
|
84
|
+
assert_includes err.message, '"invalid header"'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_call_freezes_cached_value_internally_but_returns_a_dup
|
|
88
|
+
result = normalizer.call("accept")
|
|
89
|
+
|
|
90
|
+
refute_predicate result, :frozen?
|
|
91
|
+
assert_equal "Accept", result
|
|
92
|
+
end
|
|
93
|
+
end
|