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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +241 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +110 -13
  5. data/UPGRADING.md +491 -0
  6. data/http.gemspec +32 -29
  7. data/lib/http/base64.rb +11 -1
  8. data/lib/http/chainable/helpers.rb +62 -0
  9. data/lib/http/chainable/verbs.rb +136 -0
  10. data/lib/http/chainable.rb +232 -136
  11. data/lib/http/client.rb +158 -127
  12. data/lib/http/connection/internals.rb +141 -0
  13. data/lib/http/connection.rb +126 -97
  14. data/lib/http/content_type.rb +61 -6
  15. data/lib/http/errors.rb +25 -1
  16. data/lib/http/feature.rb +65 -5
  17. data/lib/http/features/auto_deflate.rb +124 -17
  18. data/lib/http/features/auto_inflate.rb +38 -15
  19. data/lib/http/features/caching/entry.rb +178 -0
  20. data/lib/http/features/caching/in_memory_store.rb +63 -0
  21. data/lib/http/features/caching.rb +216 -0
  22. data/lib/http/features/digest_auth.rb +234 -0
  23. data/lib/http/features/instrumentation.rb +97 -17
  24. data/lib/http/features/logging.rb +183 -5
  25. data/lib/http/features/normalize_uri.rb +17 -0
  26. data/lib/http/features/raise_error.rb +18 -3
  27. data/lib/http/form_data/composite_io.rb +106 -0
  28. data/lib/http/form_data/file.rb +95 -0
  29. data/lib/http/form_data/multipart/param.rb +62 -0
  30. data/lib/http/form_data/multipart.rb +106 -0
  31. data/lib/http/form_data/part.rb +52 -0
  32. data/lib/http/form_data/readable.rb +58 -0
  33. data/lib/http/form_data/urlencoded.rb +175 -0
  34. data/lib/http/form_data/version.rb +8 -0
  35. data/lib/http/form_data.rb +102 -0
  36. data/lib/http/headers/known.rb +3 -0
  37. data/lib/http/headers/normalizer.rb +17 -36
  38. data/lib/http/headers.rb +172 -65
  39. data/lib/http/mime_type/adapter.rb +24 -9
  40. data/lib/http/mime_type/json.rb +19 -4
  41. data/lib/http/mime_type.rb +21 -3
  42. data/lib/http/options/definitions.rb +189 -0
  43. data/lib/http/options.rb +172 -125
  44. data/lib/http/redirector.rb +80 -75
  45. data/lib/http/request/body.rb +87 -6
  46. data/lib/http/request/builder.rb +184 -0
  47. data/lib/http/request/proxy.rb +83 -0
  48. data/lib/http/request/writer.rb +76 -16
  49. data/lib/http/request.rb +214 -98
  50. data/lib/http/response/body.rb +103 -18
  51. data/lib/http/response/inflater.rb +35 -7
  52. data/lib/http/response/parser.rb +98 -4
  53. data/lib/http/response/status/reasons.rb +2 -4
  54. data/lib/http/response/status.rb +141 -31
  55. data/lib/http/response.rb +219 -61
  56. data/lib/http/retriable/delay_calculator.rb +38 -11
  57. data/lib/http/retriable/errors.rb +21 -0
  58. data/lib/http/retriable/performer.rb +82 -38
  59. data/lib/http/session.rb +280 -0
  60. data/lib/http/timeout/global.rb +147 -34
  61. data/lib/http/timeout/null.rb +155 -9
  62. data/lib/http/timeout/per_operation.rb +139 -18
  63. data/lib/http/uri/normalizer.rb +82 -0
  64. data/lib/http/uri/parsing.rb +182 -0
  65. data/lib/http/uri.rb +289 -124
  66. data/lib/http/version.rb +2 -1
  67. data/lib/http.rb +11 -2
  68. data/sig/deps.rbs +122 -0
  69. data/sig/http.rbs +1619 -0
  70. data/test/http/base64_test.rb +28 -0
  71. data/test/http/client_test.rb +739 -0
  72. data/test/http/connection_test.rb +1533 -0
  73. data/test/http/content_type_test.rb +190 -0
  74. data/test/http/errors_test.rb +28 -0
  75. data/test/http/feature_test.rb +49 -0
  76. data/test/http/features/auto_deflate_test.rb +317 -0
  77. data/test/http/features/auto_inflate_test.rb +213 -0
  78. data/test/http/features/caching_test.rb +942 -0
  79. data/test/http/features/digest_auth_test.rb +996 -0
  80. data/test/http/features/instrumentation_test.rb +246 -0
  81. data/test/http/features/logging_test.rb +654 -0
  82. data/test/http/features/normalize_uri_test.rb +41 -0
  83. data/test/http/features/raise_error_test.rb +77 -0
  84. data/test/http/form_data/composite_io_test.rb +215 -0
  85. data/test/http/form_data/file_test.rb +255 -0
  86. data/test/http/form_data/fixtures/the-http-gem.info +1 -0
  87. data/test/http/form_data/multipart_test.rb +303 -0
  88. data/test/http/form_data/part_test.rb +90 -0
  89. data/test/http/form_data/urlencoded_test.rb +164 -0
  90. data/test/http/form_data_test.rb +232 -0
  91. data/test/http/headers/normalizer_test.rb +93 -0
  92. data/test/http/headers_test.rb +888 -0
  93. data/test/http/mime_type/json_test.rb +39 -0
  94. data/test/http/mime_type_test.rb +150 -0
  95. data/test/http/options/base_uri_test.rb +148 -0
  96. data/test/http/options/body_test.rb +21 -0
  97. data/test/http/options/features_test.rb +38 -0
  98. data/test/http/options/form_test.rb +21 -0
  99. data/test/http/options/headers_test.rb +32 -0
  100. data/test/http/options/json_test.rb +21 -0
  101. data/test/http/options/merge_test.rb +78 -0
  102. data/test/http/options/new_test.rb +37 -0
  103. data/test/http/options/proxy_test.rb +32 -0
  104. data/test/http/options_test.rb +575 -0
  105. data/test/http/redirector_test.rb +639 -0
  106. data/test/http/request/body_test.rb +318 -0
  107. data/test/http/request/builder_test.rb +623 -0
  108. data/test/http/request/writer_test.rb +391 -0
  109. data/test/http/request_test.rb +1733 -0
  110. data/test/http/response/body_test.rb +292 -0
  111. data/test/http/response/parser_test.rb +105 -0
  112. data/test/http/response/status_test.rb +322 -0
  113. data/test/http/response_test.rb +502 -0
  114. data/test/http/retriable/delay_calculator_test.rb +194 -0
  115. data/test/http/retriable/errors_test.rb +71 -0
  116. data/test/http/retriable/performer_test.rb +551 -0
  117. data/test/http/session_test.rb +424 -0
  118. data/test/http/timeout/global_test.rb +239 -0
  119. data/test/http/timeout/null_test.rb +218 -0
  120. data/test/http/timeout/per_operation_test.rb +220 -0
  121. data/test/http/uri/normalizer_test.rb +89 -0
  122. data/test/http/uri_test.rb +1140 -0
  123. data/test/http/version_test.rb +15 -0
  124. data/test/http_test.rb +818 -0
  125. data/test/regression_tests.rb +27 -0
  126. data/test/support/dummy_server/encoding_routes.rb +47 -0
  127. data/test/support/dummy_server/routes.rb +201 -0
  128. data/test/support/dummy_server/servlet.rb +81 -0
  129. data/test/support/dummy_server.rb +200 -0
  130. data/{spec → test}/support/fakeio.rb +2 -2
  131. data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
  132. data/test/support/http_handling_shared/timeout_tests.rb +134 -0
  133. data/test/support/http_handling_shared.rb +11 -0
  134. data/test/support/proxy_server.rb +207 -0
  135. data/test/support/servers/runner.rb +67 -0
  136. data/{spec → test}/support/simplecov.rb +11 -2
  137. data/test/support/ssl_helper.rb +108 -0
  138. data/test/test_helper.rb +38 -0
  139. metadata +108 -168
  140. data/.github/workflows/ci.yml +0 -67
  141. data/.gitignore +0 -15
  142. data/.rspec +0 -1
  143. data/.rubocop/layout.yml +0 -8
  144. data/.rubocop/metrics.yml +0 -4
  145. data/.rubocop/rspec.yml +0 -9
  146. data/.rubocop/style.yml +0 -32
  147. data/.rubocop.yml +0 -11
  148. data/.rubocop_todo.yml +0 -219
  149. data/.yardopts +0 -2
  150. data/CHANGES_OLD.md +0 -1002
  151. data/Gemfile +0 -51
  152. data/Guardfile +0 -18
  153. data/Rakefile +0 -64
  154. data/lib/http/headers/mixin.rb +0 -34
  155. data/lib/http/retriable/client.rb +0 -37
  156. data/logo.png +0 -0
  157. data/spec/lib/http/client_spec.rb +0 -556
  158. data/spec/lib/http/connection_spec.rb +0 -88
  159. data/spec/lib/http/content_type_spec.rb +0 -47
  160. data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
  161. data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
  162. data/spec/lib/http/features/instrumentation_spec.rb +0 -81
  163. data/spec/lib/http/features/logging_spec.rb +0 -65
  164. data/spec/lib/http/features/raise_error_spec.rb +0 -62
  165. data/spec/lib/http/headers/mixin_spec.rb +0 -36
  166. data/spec/lib/http/headers/normalizer_spec.rb +0 -52
  167. data/spec/lib/http/headers_spec.rb +0 -527
  168. data/spec/lib/http/options/body_spec.rb +0 -15
  169. data/spec/lib/http/options/features_spec.rb +0 -33
  170. data/spec/lib/http/options/form_spec.rb +0 -15
  171. data/spec/lib/http/options/headers_spec.rb +0 -24
  172. data/spec/lib/http/options/json_spec.rb +0 -15
  173. data/spec/lib/http/options/merge_spec.rb +0 -68
  174. data/spec/lib/http/options/new_spec.rb +0 -30
  175. data/spec/lib/http/options/proxy_spec.rb +0 -20
  176. data/spec/lib/http/options_spec.rb +0 -13
  177. data/spec/lib/http/redirector_spec.rb +0 -530
  178. data/spec/lib/http/request/body_spec.rb +0 -211
  179. data/spec/lib/http/request/writer_spec.rb +0 -121
  180. data/spec/lib/http/request_spec.rb +0 -234
  181. data/spec/lib/http/response/body_spec.rb +0 -85
  182. data/spec/lib/http/response/parser_spec.rb +0 -74
  183. data/spec/lib/http/response/status_spec.rb +0 -253
  184. data/spec/lib/http/response_spec.rb +0 -262
  185. data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
  186. data/spec/lib/http/retriable/performer_spec.rb +0 -302
  187. data/spec/lib/http/uri/normalizer_spec.rb +0 -95
  188. data/spec/lib/http/uri_spec.rb +0 -71
  189. data/spec/lib/http_spec.rb +0 -535
  190. data/spec/regression_specs.rb +0 -24
  191. data/spec/spec_helper.rb +0 -89
  192. data/spec/support/black_hole.rb +0 -13
  193. data/spec/support/dummy_server/servlet.rb +0 -203
  194. data/spec/support/dummy_server.rb +0 -44
  195. data/spec/support/fuubar.rb +0 -21
  196. data/spec/support/http_handling_shared.rb +0 -190
  197. data/spec/support/proxy_server.rb +0 -39
  198. data/spec/support/servers/config.rb +0 -11
  199. data/spec/support/servers/runner.rb +0 -19
  200. data/spec/support/ssl_helper.rb +0 -104
  201. /data/{spec → test}/support/capture_warning.rb +0 -0
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPMimeTypeJSONTest < Minitest::Test
6
+ cover "HTTP::MimeType*"
7
+
8
+ def adapter
9
+ @adapter ||= HTTP::MimeType::JSON.send(:new)
10
+ end
11
+
12
+ def test_encode_uses_to_json_when_available
13
+ assert_equal '{"foo":"bar"}', adapter.encode(foo: "bar")
14
+ end
15
+
16
+ def test_encode_prefers_to_json_over_json_dump
17
+ obj = Object.new
18
+ def obj.to_json(*args)
19
+ args.empty? ? '"direct"' : '"via_dump"'
20
+ end
21
+
22
+ assert_equal '"direct"', adapter.encode(obj)
23
+ end
24
+
25
+ def test_encode_falls_back_to_json_dump_for_objects_without_to_json
26
+ obj = Object.new
27
+ obj.define_singleton_method(:respond_to?) do |method, *args|
28
+ return false if method == :to_json
29
+
30
+ super(method, *args)
31
+ end
32
+
33
+ assert_kind_of String, adapter.encode(obj)
34
+ end
35
+
36
+ def test_decode_parses_json_strings
37
+ assert_equal({ "foo" => "bar" }, adapter.decode('{"foo":"bar"}'))
38
+ end
39
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "securerandom"
5
+
6
+ class HTTPMimeTypeTest < Minitest::Test
7
+ cover "HTTP::MimeType*"
8
+
9
+ def sample_type
10
+ @sample_type ||= "application/mutant-check-#{SecureRandom.hex(4)}"
11
+ end
12
+
13
+ def sample_adapter
14
+ @sample_adapter ||= Module.new do
15
+ def self.encode(obj); end
16
+
17
+ def self.decode(str); end
18
+ end
19
+ end
20
+
21
+ def test_register_adapter_stores_the_adapter_retrievable_via_brackets
22
+ HTTP::MimeType.register_adapter(sample_type, sample_adapter)
23
+
24
+ assert_same sample_adapter, HTTP::MimeType[sample_type]
25
+ end
26
+
27
+ def test_register_adapter_calls_to_s_on_the_type_argument
28
+ type_str = "application/test-to-s-#{SecureRandom.hex(4)}"
29
+ type_obj = fake(to_s: type_str)
30
+
31
+ HTTP::MimeType.register_adapter(type_obj, sample_adapter)
32
+
33
+ assert_same sample_adapter, HTTP::MimeType[type_str]
34
+ end
35
+
36
+ def test_brackets_retrieves_a_registered_adapter
37
+ HTTP::MimeType.register_adapter(sample_type, sample_adapter)
38
+
39
+ assert_same sample_adapter, HTTP::MimeType[sample_type]
40
+ end
41
+
42
+ def test_brackets_raises_unsupported_mime_type_error_for_unknown_type
43
+ assert_raises(HTTP::UnsupportedMimeTypeError) do
44
+ HTTP::MimeType["application/nonexistent-#{SecureRandom.hex(4)}"]
45
+ end
46
+ end
47
+
48
+ def test_brackets_includes_the_type_in_the_error_message
49
+ unknown = "application/unknown-#{SecureRandom.hex(4)}"
50
+
51
+ err = assert_raises(HTTP::UnsupportedMimeTypeError) do
52
+ HTTP::MimeType[unknown]
53
+ end
54
+ assert_includes err.message, unknown
55
+ end
56
+
57
+ def test_brackets_resolves_aliases_when_looking_up_adapters
58
+ shortcut = :"test_shortcut_#{SecureRandom.hex(4)}"
59
+ HTTP::MimeType.register_adapter(sample_type, sample_adapter)
60
+ HTTP::MimeType.register_alias(sample_type, shortcut)
61
+
62
+ assert_same sample_adapter, HTTP::MimeType[shortcut]
63
+ end
64
+
65
+ def test_register_alias_stores_alias_resolvable_via_normalize
66
+ shortcut = :"test_alias_#{SecureRandom.hex(4)}"
67
+ HTTP::MimeType.register_alias(sample_type, shortcut)
68
+
69
+ assert_equal sample_type, HTTP::MimeType.normalize(shortcut)
70
+ end
71
+
72
+ def test_register_alias_calls_to_sym_on_the_shortcut_argument
73
+ shortcut_str = "test_sym_shortcut_#{SecureRandom.hex(4)}"
74
+ HTTP::MimeType.register_alias(sample_type, shortcut_str)
75
+
76
+ assert_equal sample_type, HTTP::MimeType.normalize(shortcut_str.to_sym)
77
+ end
78
+
79
+ def test_register_alias_calls_to_s_on_the_type_argument
80
+ type_str = "application/test-alias-to-s-#{SecureRandom.hex(4)}"
81
+ type_obj = fake(to_s: type_str)
82
+
83
+ shortcut = :"test_alias_to_s_#{SecureRandom.hex(4)}"
84
+ HTTP::MimeType.register_alias(type_obj, shortcut)
85
+
86
+ assert_equal type_str, HTTP::MimeType.normalize(shortcut)
87
+ end
88
+
89
+ def test_normalize_returns_the_type_string_if_no_alias_found
90
+ unaliased = "application/no-alias-#{SecureRandom.hex(4)}"
91
+
92
+ assert_equal unaliased, HTTP::MimeType.normalize(unaliased)
93
+ end
94
+
95
+ def test_normalize_returns_aliased_type_if_alias_exists
96
+ shortcut = :"test_norm_#{SecureRandom.hex(4)}"
97
+ HTTP::MimeType.register_alias(sample_type, shortcut)
98
+
99
+ assert_equal sample_type, HTTP::MimeType.normalize(shortcut)
100
+ end
101
+
102
+ def test_normalize_calls_to_s_on_the_argument_when_no_alias_found
103
+ type_str = "application/normalize-to-s-#{SecureRandom.hex(4)}"
104
+ type_obj = fake(to_s: type_str)
105
+
106
+ assert_equal type_str, HTTP::MimeType.normalize(type_obj)
107
+ end
108
+
109
+ def test_adapters_initializes_as_a_hash_when_accessed_for_the_first_time
110
+ original = HTTP::MimeType.instance_variable_get(:@adapters)
111
+ HTTP::MimeType.instance_variable_set(:@adapters, nil)
112
+ begin
113
+ result = HTTP::MimeType.send(:adapters)
114
+
115
+ assert_instance_of Hash, result
116
+ ensure
117
+ HTTP::MimeType.instance_variable_set(:@adapters, original)
118
+ end
119
+ end
120
+
121
+ def test_aliases_initializes_as_a_hash_when_accessed_for_the_first_time
122
+ original = HTTP::MimeType.instance_variable_get(:@aliases)
123
+ HTTP::MimeType.instance_variable_set(:@aliases, nil)
124
+ begin
125
+ result = HTTP::MimeType.send(:aliases)
126
+
127
+ assert_instance_of Hash, result
128
+ ensure
129
+ HTTP::MimeType.instance_variable_set(:@aliases, original)
130
+ end
131
+ end
132
+
133
+ def test_adapter_raises_error_on_encode_with_class_name_in_message
134
+ err = assert_raises(HTTP::Error) { HTTP::MimeType::Adapter.instance.encode("data") }
135
+ assert_equal "HTTP::MimeType::Adapter does not supports #encode", err.message
136
+ end
137
+
138
+ def test_adapter_raises_error_on_decode_with_class_name_in_message
139
+ err = assert_raises(HTTP::Error) { HTTP::MimeType::Adapter.instance.decode("data") }
140
+ assert_equal "HTTP::MimeType::Adapter does not supports #decode", err.message
141
+ end
142
+
143
+ def test_builtin_has_json_adapter_registered_for_application_json
144
+ assert_equal HTTP::MimeType::JSON, HTTP::MimeType["application/json"]
145
+ end
146
+
147
+ def test_builtin_has_json_alias_for_application_json
148
+ assert_equal "application/json", HTTP::MimeType.normalize(:json)
149
+ end
150
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsBaseURITest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ # #with_base_uri
9
+
10
+ def test_with_base_uri_sets_base_uri_from_a_string
11
+ opts = HTTP::Options.new
12
+ result = opts.with_base_uri("https://example.com/api")
13
+
14
+ assert_equal "https://example.com/api", result.base_uri.to_s
15
+ end
16
+
17
+ def test_with_base_uri_sets_base_uri_from_an_http_uri
18
+ opts = HTTP::Options.new
19
+ uri = HTTP::URI.parse("https://example.com/api")
20
+ result = opts.with_base_uri(uri)
21
+
22
+ assert_equal "https://example.com/api", result.base_uri.to_s
23
+ end
24
+
25
+ def test_with_base_uri_raises_for_uri_without_scheme
26
+ opts = HTTP::Options.new
27
+ err = assert_raises(HTTP::Error) { opts.with_base_uri("/users") }
28
+
29
+ assert_includes err.message, "Invalid base URI"
30
+ assert_includes err.message, "/users"
31
+ end
32
+
33
+ def test_with_base_uri_raises_for_protocol_relative_uri
34
+ opts = HTTP::Options.new
35
+ err = assert_raises(HTTP::Error) { opts.with_base_uri("//example.com/users") }
36
+
37
+ assert_includes err.message, "Invalid base URI"
38
+ assert_includes err.message, "//example.com/users"
39
+ end
40
+
41
+ def test_with_base_uri_does_not_modify_the_original_options
42
+ opts = HTTP::Options.new
43
+ opts.with_base_uri("https://example.com")
44
+
45
+ refute_predicate opts, :base_uri?
46
+ end
47
+
48
+ # #base_uri?
49
+
50
+ def test_base_uri_predicate_returns_false_by_default
51
+ opts = HTTP::Options.new
52
+
53
+ refute_predicate opts, :base_uri?
54
+ end
55
+
56
+ def test_base_uri_predicate_returns_true_when_base_uri_is_set
57
+ opts = HTTP::Options.new
58
+ result = opts.with_base_uri("https://example.com")
59
+
60
+ assert_predicate result, :base_uri?
61
+ end
62
+
63
+ # chaining base URIs
64
+
65
+ def test_chaining_joins_a_relative_path_onto_existing_base_uri
66
+ opts = HTTP::Options.new
67
+ result = opts.with_base_uri("https://example.com").with_base_uri("api/v1")
68
+
69
+ assert_equal "https://example.com/api/v1", result.base_uri.to_s
70
+ end
71
+
72
+ def test_chaining_joins_an_absolute_path_onto_existing_base_uri
73
+ opts = HTTP::Options.new
74
+ result = opts.with_base_uri("https://example.com/api").with_base_uri("/v2")
75
+
76
+ assert_equal "https://example.com/v2", result.base_uri.to_s
77
+ end
78
+
79
+ def test_chaining_replaces_with_a_full_uri
80
+ opts = HTTP::Options.new
81
+ result = opts.with_base_uri("https://example.com/api").with_base_uri("https://other.com/v2")
82
+
83
+ assert_equal "https://other.com/v2", result.base_uri.to_s
84
+ end
85
+
86
+ def test_chaining_joins_onto_base_uri_with_trailing_slash
87
+ opts = HTTP::Options.new
88
+ result = opts.with_base_uri("https://example.com/api/").with_base_uri("v2")
89
+
90
+ assert_equal "https://example.com/api/v2", result.base_uri.to_s
91
+ end
92
+
93
+ def test_chaining_handles_parent_path_traversal
94
+ opts = HTTP::Options.new
95
+ result = opts.with_base_uri("https://example.com/api/v1").with_base_uri("../v2")
96
+
97
+ assert_equal "https://example.com/api/v2", result.base_uri.to_s
98
+ end
99
+
100
+ def test_chaining_does_not_mutate_the_intermediate_base_uri
101
+ opts = HTTP::Options.new
102
+ intermediate = opts.with_base_uri("https://example.com/api")
103
+ intermediate.with_base_uri("v2")
104
+
105
+ assert_equal "https://example.com/api", intermediate.base_uri.to_s
106
+ end
107
+
108
+ # persistent and base_uri interaction
109
+
110
+ def test_persistent_allows_compatible_persistent_and_base_uri
111
+ opts = HTTP::Options.new
112
+ result = opts.with_base_uri("https://example.com/api").with_persistent("https://example.com")
113
+
114
+ assert_equal "https://example.com/api", result.base_uri.to_s
115
+ assert_equal "https://example.com", result.persistent
116
+ end
117
+
118
+ def test_persistent_raises_when_persistent_origin_conflicts_with_base_uri
119
+ opts = HTTP::Options.new
120
+ with_base = opts.with_base_uri("https://example.com/api")
121
+
122
+ err = assert_raises(HTTP::Error) { with_base.with_persistent("https://other.com") }
123
+ assert_includes err.message, "https://other.com"
124
+ assert_includes err.message, "base URI origin (https://example.com)"
125
+ end
126
+
127
+ def test_persistent_raises_when_base_uri_origin_conflicts_with_persistent
128
+ opts = HTTP::Options.new
129
+ with_persistent = opts.with_persistent("https://example.com")
130
+
131
+ err = assert_raises(HTTP::Error) { with_persistent.with_base_uri("https://other.com/api") }
132
+ assert_includes err.message, "https://example.com"
133
+ assert_includes err.message, "base URI origin (https://other.com)"
134
+ end
135
+
136
+ def test_persistent_allows_setting_both_via_constructor_when_origins_match
137
+ result = HTTP::Options.new(base_uri: "https://example.com/api", persistent: "https://example.com")
138
+
139
+ assert_equal "https://example.com/api", result.base_uri.to_s
140
+ assert_equal "https://example.com", result.persistent
141
+ end
142
+
143
+ def test_persistent_raises_via_constructor_when_origins_conflict
144
+ assert_raises(HTTP::Error) do
145
+ HTTP::Options.new(base_uri: "https://example.com/api", persistent: "https://other.com")
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsBodyTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_defaults_to_nil
9
+ opts = HTTP::Options.new
10
+
11
+ assert_nil opts.body
12
+ end
13
+
14
+ def test_may_be_specified_with_with_body
15
+ opts = HTTP::Options.new
16
+ opts2 = opts.with_body("foo")
17
+
18
+ assert_nil opts.body
19
+ assert_equal "foo", opts2.body
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsFeaturesTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_defaults_to_be_empty
9
+ opts = HTTP::Options.new
10
+
11
+ assert_empty opts.features
12
+ end
13
+
14
+ def test_accepts_plain_symbols_in_array
15
+ opts = HTTP::Options.new
16
+ opts2 = opts.with_features([:auto_inflate])
17
+
18
+ assert_empty opts.features
19
+ assert_equal [:auto_inflate], opts2.features.keys
20
+ assert_instance_of HTTP::Features::AutoInflate, opts2.features[:auto_inflate]
21
+ end
22
+
23
+ def test_accepts_feature_name_with_its_options_in_array
24
+ opts = HTTP::Options.new
25
+ opts2 = opts.with_features([{ auto_deflate: { method: :deflate } }])
26
+
27
+ assert_empty opts.features
28
+ assert_equal [:auto_deflate], opts2.features.keys
29
+ assert_instance_of HTTP::Features::AutoDeflate, opts2.features[:auto_deflate]
30
+ assert_equal "deflate", opts2.features[:auto_deflate].method
31
+ end
32
+
33
+ def test_raises_error_for_not_supported_features
34
+ opts = HTTP::Options.new
35
+ error = assert_raises(HTTP::Error) { opts.with_features([:wrong_feature]) }
36
+ assert_equal "Unsupported feature: wrong_feature", error.message
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsFormTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_defaults_to_nil
9
+ opts = HTTP::Options.new
10
+
11
+ assert_nil opts.form
12
+ end
13
+
14
+ def test_may_be_specified_with_with_form_data
15
+ opts = HTTP::Options.new
16
+ opts2 = opts.with_form(foo: 42)
17
+
18
+ assert_nil opts.form
19
+ assert_equal({ foo: 42 }, opts2.form)
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsHeadersTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_defaults_to_be_empty
9
+ opts = HTTP::Options.new
10
+
11
+ assert_empty opts.headers
12
+ end
13
+
14
+ def test_may_be_specified_with_with_headers
15
+ opts = HTTP::Options.new
16
+ opts2 = opts.with_headers(accept: "json")
17
+
18
+ assert_empty opts.headers
19
+ assert_equal [%w[Accept json]], opts2.headers.to_a
20
+ end
21
+
22
+ def test_accepts_any_object_that_respond_to_to_hash
23
+ opts = HTTP::Options.new
24
+ x = if RUBY_VERSION >= "3.2.0"
25
+ Data.define(:to_hash).new(to_hash: { "accept" => "json" })
26
+ else
27
+ Struct.new(:to_hash).new({ "accept" => "json" })
28
+ end
29
+
30
+ assert_equal "json", opts.with_headers(x).headers["accept"]
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsJSONTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_defaults_to_nil
9
+ opts = HTTP::Options.new
10
+
11
+ assert_nil opts.json
12
+ end
13
+
14
+ def test_may_be_specified_with_with_json_data
15
+ opts = HTTP::Options.new
16
+ opts2 = opts.with_json(foo: 42)
17
+
18
+ assert_nil opts.json
19
+ assert_equal({ foo: 42 }, opts2.json)
20
+ end
21
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsMergeTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_supports_a_hash
9
+ opts = HTTP::Options.new
10
+ old_response = opts.response
11
+
12
+ assert_equal :body, opts.merge(response: :body).response
13
+ assert_equal old_response, opts.response
14
+ end
15
+
16
+ def test_supports_another_options
17
+ opts = HTTP::Options.new
18
+ merged = opts.merge(HTTP::Options.new(response: :body))
19
+
20
+ assert_equal :body, merged.response
21
+ end
22
+
23
+ def test_merges_as_expected_in_complex_cases
24
+ # FIXME: yuck :(
25
+
26
+ foo = HTTP::Options.new(
27
+ response: :body,
28
+ params: { baz: "bar" },
29
+ form: { foo: "foo" },
30
+ body: "body-foo",
31
+ json: { foo: "foo" },
32
+ headers: { accept: "json", foo: "foo" },
33
+ proxy: {},
34
+ features: {}
35
+ )
36
+
37
+ bar = HTTP::Options.new(
38
+ response: :parsed_body,
39
+ persistent: "https://www.googe.com",
40
+ params: { plop: "plip" },
41
+ form: { bar: "bar" },
42
+ body: "body-bar",
43
+ json: { bar: "bar" },
44
+ keep_alive_timeout: 10,
45
+ headers: { accept: "xml", bar: "bar" },
46
+ timeout_options: { foo: :bar },
47
+ ssl: { foo: "bar" },
48
+ proxy: { proxy_address: "127.0.0.1", proxy_port: 8080 }
49
+ )
50
+
51
+ assert_equal(
52
+ {
53
+ response: :parsed_body,
54
+ timeout_class: HTTP::Options.default_timeout_class,
55
+ timeout_options: { foo: :bar },
56
+ params: { plop: "plip" },
57
+ form: { bar: "bar" },
58
+ body: "body-bar",
59
+ json: { bar: "bar" },
60
+ persistent: "https://www.googe.com",
61
+ keep_alive_timeout: 10,
62
+ ssl: { foo: "bar" },
63
+ headers: { "Foo" => "foo", "Accept" => "xml", "Bar" => "bar" },
64
+ proxy: { proxy_address: "127.0.0.1", proxy_port: 8080 },
65
+ follow: nil,
66
+ retriable: nil,
67
+ base_uri: nil,
68
+ socket_class: HTTP::Options.default_socket_class,
69
+ nodelay: false,
70
+ ssl_socket_class: HTTP::Options.default_ssl_socket_class,
71
+ ssl_context: nil,
72
+ encoding: nil,
73
+ features: {}
74
+ },
75
+ foo.merge(bar).to_hash
76
+ )
77
+ end
78
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsNewTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_supports_a_options_instance
9
+ opts = HTTP::Options.new
10
+
11
+ assert_equal opts, HTTP::Options.new(opts)
12
+ end
13
+
14
+ def test_with_a_hash_coerces_response_correctly
15
+ opts = HTTP::Options.new(response: :object)
16
+
17
+ assert_equal :object, opts.response
18
+ end
19
+
20
+ def test_with_a_hash_coerces_headers_correctly
21
+ opts = HTTP::Options.new(headers: { accept: "json" })
22
+
23
+ assert_equal [%w[Accept json]], opts.headers.to_a
24
+ end
25
+
26
+ def test_with_a_hash_coerces_proxy_correctly
27
+ opts = HTTP::Options.new(proxy: { proxy_address: "127.0.0.1", proxy_port: 8080 })
28
+
29
+ assert_equal({ proxy_address: "127.0.0.1", proxy_port: 8080 }, opts.proxy)
30
+ end
31
+
32
+ def test_with_a_hash_coerces_form_correctly
33
+ opts = HTTP::Options.new(form: { foo: 42 })
34
+
35
+ assert_equal({ foo: 42 }, opts.form)
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPOptionsProxyTest < Minitest::Test
6
+ cover "HTTP::Options*"
7
+
8
+ def test_defaults_to_empty_hash
9
+ opts = HTTP::Options.new
10
+
11
+ assert_equal({}, opts.proxy)
12
+ end
13
+
14
+ def test_may_be_specified_with_with_proxy
15
+ opts = HTTP::Options.new
16
+ opts2 = opts.with_proxy(proxy_address: "127.0.0.1", proxy_port: 8080)
17
+
18
+ assert_equal({}, opts.proxy)
19
+ assert_equal({ proxy_address: "127.0.0.1", proxy_port: 8080 }, opts2.proxy)
20
+ end
21
+
22
+ def test_accepts_proxy_address_port_username_and_password
23
+ opts = HTTP::Options.new
24
+ opts2 = opts.with_proxy(proxy_address: "127.0.0.1", proxy_port: 8080, proxy_username: "username",
25
+ proxy_password: "password")
26
+
27
+ assert_equal(
28
+ { proxy_address: "127.0.0.1", proxy_port: 8080, proxy_username: "username",
29
+ proxy_password: "password" }, opts2.proxy
30
+ )
31
+ end
32
+ end