curb 1.2.2 → 1.3.2

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.
@@ -0,0 +1,145 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class TestCurbCurlErrorMappings < Test::Unit::TestCase
4
+ def assert_easy_error_mapping(code, expected_class)
5
+ actual_class, message = Curl::Easy.error(code)
6
+
7
+ assert_equal expected_class, actual_class
8
+ assert_kind_of String, message
9
+ assert_not_empty message
10
+ end
11
+
12
+ def test_easy_error_known_mappings
13
+ assert_easy_error_mapping(0, Curl::Err::CurlOK)
14
+ assert_easy_error_mapping(1, Curl::Err::UnsupportedProtocolError)
15
+ assert_easy_error_mapping(2, Curl::Err::FailedInitError)
16
+ assert_easy_error_mapping(3, Curl::Err::MalformedURLError)
17
+ assert_easy_error_mapping(5, Curl::Err::ProxyResolutionError)
18
+ assert_easy_error_mapping(6, Curl::Err::HostResolutionError)
19
+ assert_easy_error_mapping(7, Curl::Err::ConnectionFailedError)
20
+ assert_easy_error_mapping(23, Curl::Err::WriteError)
21
+ assert_easy_error_mapping(26, Curl::Err::ReadError)
22
+ assert_easy_error_mapping(28, Curl::Err::TimeoutError)
23
+ assert_easy_error_mapping(42, Curl::Err::AbortedByCallbackError)
24
+ assert_easy_error_mapping(47, Curl::Err::TooManyRedirectsError)
25
+ assert_easy_error_mapping(52, Curl::Err::GotNothingError)
26
+ assert_easy_error_mapping(55, Curl::Err::SendError)
27
+ assert_easy_error_mapping(56, Curl::Err::RecvError)
28
+ assert_easy_error_mapping(57, Curl::Err::ShareInUseError)
29
+ assert_easy_error_mapping(58, Curl::Err::SSLCertificateError)
30
+ assert_easy_error_mapping(59, Curl::Err::SSLCypherError)
31
+ assert_easy_error_mapping(61, Curl::Err::BadContentEncodingError)
32
+ assert_easy_error_mapping(63, Curl::Err::FileSizeExceededError)
33
+ assert_easy_error_mapping(64, Curl::Err::FTPSSLFailed)
34
+
35
+ ssl_peer_or_ca_error =
36
+ if Gem::Version.new(Curl::CURL_VERSION) >= Gem::Version.new('7.62.0')
37
+ Curl::Err::SSLPeerCertificateError
38
+ else
39
+ Curl::Err::SSLCACertificateError
40
+ end
41
+
42
+ assert_easy_error_mapping(60, ssl_peer_or_ca_error)
43
+ end
44
+
45
+ def test_easy_error_returns_error_info_for_known_numeric_range
46
+ 0.upto(92) do |code|
47
+ error_class, message = Curl::Easy.error(code)
48
+
49
+ assert_kind_of Class, error_class
50
+ assert error_class <= Curl::Err::CurlError
51
+ assert_kind_of String, message
52
+ assert_not_empty message
53
+ end
54
+ end
55
+
56
+ def test_easy_error_uses_generic_mapping_for_unknown_codes
57
+ error_class, message = Curl::Easy.error(9_999)
58
+
59
+ assert_equal Curl::Err::CurlError, error_class
60
+ assert_equal 'Unknown error result from libcurl', message
61
+ end
62
+ end
63
+
64
+ class TestCurbCurlNativeCoverage < Test::Unit::TestCase
65
+ include TestServerMethods
66
+
67
+ def setup
68
+ server_setup
69
+ end
70
+
71
+ def test_clone_preserves_native_lists_after_original_handle_closes
72
+ easy = Curl::Easy.new("http://curb.invalid:#{TestServlet.port}#{TestServlet.path}")
73
+ easy.headers['X-Test'] = '1'
74
+ easy.proxy_headers['X-Proxy'] = '2'
75
+ easy.ftp_commands = ['PWD']
76
+ easy.resolve = ["curb.invalid:#{TestServlet.port}:127.0.0.1"]
77
+
78
+ clone = easy.clone
79
+ easy.close
80
+
81
+ clone.http_get
82
+
83
+ assert_equal 'GET', clone.body_str
84
+ assert_equal '1', clone.headers['X-Test']
85
+ assert_equal '2', clone.proxy_headers['X-Proxy']
86
+ assert_equal ['PWD'], clone.ftp_commands
87
+ assert_equal ["curb.invalid:#{TestServlet.port}:127.0.0.1"], clone.resolve
88
+ ensure
89
+ clone.close if defined?(clone) && clone
90
+ easy.close if defined?(easy) && easy
91
+ end
92
+
93
+ def test_clone_rebinds_upload_callbacks_to_clone_state
94
+ easy = Curl::Easy.new(TestServlet.url)
95
+ easy.put_data = 'clone-data'
96
+
97
+ clone = easy.clone
98
+ easy.put_data = 'other-data'
99
+
100
+ clone.perform
101
+
102
+ assert_equal "PUT\nclone-data", clone.body_str
103
+ ensure
104
+ clone.close if defined?(clone) && clone
105
+ easy.close if defined?(easy) && easy
106
+ end
107
+
108
+ def test_native_accessors_round_trip
109
+ easy = Curl::Easy.new(TestServlet.url)
110
+
111
+ assert_equal '127.0.0.1', easy.interface = '127.0.0.1'
112
+ assert_equal 'user:pass', easy.userpwd = 'user:pass'
113
+ assert_equal 'proxy:pass', easy.proxypwd = 'proxy:pass'
114
+ assert_equal 'tests/cert.pem', easy.cert_key = 'tests/cert.pem'
115
+ assert_equal 'gzip', easy.encoding = 'gzip'
116
+
117
+ if easy.respond_to?(:max_send_speed_large=)
118
+ assert_equal 123, easy.max_send_speed_large = 123
119
+ assert_equal 123, easy.max_send_speed_large
120
+ end
121
+
122
+ if easy.respond_to?(:max_recv_speed_large=)
123
+ assert_equal 456, easy.max_recv_speed_large = 456
124
+ assert_equal 456, easy.max_recv_speed_large
125
+ end
126
+
127
+ assert_equal '127.0.0.1', easy.interface
128
+ assert_equal 'user:pass', easy.userpwd
129
+ assert_equal 'proxy:pass', easy.proxypwd
130
+ assert_equal 'tests/cert.pem', easy.cert_key
131
+ assert_equal 'gzip', easy.encoding
132
+ ensure
133
+ easy.close if defined?(easy) && easy
134
+ end
135
+
136
+ def test_upload_round_trips_stream_and_offset
137
+ upload = Curl::Upload.new
138
+ stream = StringIO.new('payload')
139
+
140
+ assert_same stream, upload.stream = stream
141
+ assert_same stream, upload.stream
142
+ assert_equal 7, upload.offset = 7
143
+ assert_equal 7, upload.offset
144
+ end
145
+ end
@@ -136,9 +136,185 @@ class TestCurbCurlPostfield < Test::Unit::TestCase
136
136
  assert_equal "foo=FOOBAR", pf.to_s
137
137
  end
138
138
 
139
+ def test_content_proc_survives_gc
140
+ pf = postfield_with_only_native_proc_reference
141
+
142
+ 10.times do
143
+ GC.start(full_mark: true, immediate_sweep: true)
144
+ GC.compact if GC.respond_to?(:compact)
145
+ end
146
+
147
+ assert_equal "foo=FOOBAR", pf.to_s
148
+ end
149
+
139
150
  def test_to_s_04
140
151
  pf = Curl::PostField.file('foo.file', 'bar.file')
141
152
  assert_nothing_raised { pf.to_s }
142
153
  #assert_raise(Curl::Err::InvalidPostFieldError) { pf.to_s }
143
154
  end
155
+
156
+ def postfield_with_only_native_proc_reference
157
+ Curl::PostField.content('foo') { |field| field.name.upcase + "BAR" }
158
+ end
159
+ end
160
+
161
+ class TestCurbCurlPostfieldNativeCoverage < Test::Unit::TestCase
162
+ def test_attribute_writers_round_trip
163
+ pf = Curl::PostField.content('foo', 'bar')
164
+
165
+ assert_equal 'renamed', pf.name = 'renamed'
166
+ assert_equal 'payload', pf.content = 'payload'
167
+ assert_equal 'text/plain', pf.content_type = 'text/plain'
168
+ assert_equal 'local.txt', pf.local_file = 'local.txt'
169
+ assert_equal 'remote.txt', pf.remote_file = 'remote.txt'
170
+
171
+ assert_equal 'renamed', pf.name
172
+ assert_equal 'payload', pf.content
173
+ assert_equal 'text/plain', pf.content_type
174
+ assert_equal 'local.txt', pf.local_file
175
+ assert_equal 'remote.txt', pf.remote_file
176
+ end
177
+
178
+ def test_to_s_accepts_non_string_name_via_to_s
179
+ name_like = Object.new
180
+ def name_like.to_s
181
+ 'fancy name'
182
+ end
183
+
184
+ pf = Curl::PostField.content(name_like, 'value')
185
+ assert_equal 'fancy%20name=value', pf.to_s
186
+ end
187
+
188
+ def test_to_s_uses_remote_file_when_local_file_is_missing
189
+ pf = Curl::PostField.file('upload', 'local.txt', 'remote.txt')
190
+ pf.local_file = nil
191
+
192
+ assert_equal 'upload=remote.txt', pf.to_s
193
+ end
194
+
195
+ def test_to_s_rejects_name_without_to_s
196
+ name_like = Class.new do
197
+ undef to_s
198
+ end.new
199
+
200
+ pf = Curl::PostField.content(name_like, 'value')
201
+
202
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { pf.to_s }
203
+ assert_match(/Cannot convert unnamed field to string/, error.message)
204
+ end
205
+
206
+ def test_to_s_rejects_content_without_to_s
207
+ content_like = Class.new do
208
+ undef to_s
209
+ end.new
210
+
211
+ pf = Curl::PostField.content('name', content_like)
212
+
213
+ error = assert_raise(RuntimeError) { pf.to_s }
214
+ assert_match(/does not respond_to to_s/, error.message)
215
+ end
216
+
217
+ def test_multipart_rejects_unnamed_field
218
+ curl = Curl::Easy.new(TestServlet.url)
219
+ curl.multipart_form_post = true
220
+ pf = Curl::PostField.content('name', 'value')
221
+ pf.name = nil
222
+
223
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { curl.http_post(pf) }
224
+ assert_match(/Cannot post unnamed field/, error.message)
225
+ end
226
+
227
+ def test_multipart_rejects_content_field_without_data
228
+ curl = Curl::Easy.new(TestServlet.url)
229
+ curl.multipart_form_post = true
230
+ pf = Curl::PostField.content('name', 'value')
231
+ pf.content = nil
232
+
233
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { curl.http_post(pf) }
234
+ assert_match(/Cannot post content field with no data/, error.message)
235
+ end
236
+
237
+ def test_multipart_rejects_file_field_without_filename
238
+ curl = Curl::Easy.new(TestServlet.url)
239
+ curl.multipart_form_post = true
240
+ pf = Curl::PostField.file('upload', 'remote.txt') { 'payload' }
241
+ pf.local_file = 'dummy.txt'
242
+ pf.remote_file = nil
243
+
244
+ error = assert_raise(Curl::Err::InvalidPostFieldError) { curl.http_post(pf) }
245
+ assert_match(/Cannot post file upload field with no filename/, error.message)
246
+ end
247
+ end
248
+
249
+ class TestCurbCurlPostfieldMultipartCoverage < Test::Unit::TestCase
250
+ include TestServerMethods
251
+
252
+ def setup
253
+ server_setup
254
+ end
255
+
256
+ def test_multipart_content_variants_include_dynamic_and_typed_fields
257
+ curl = Curl::Easy.new(TestServlet.url)
258
+ curl.multipart_form_post = true
259
+
260
+ proc_without_type = Curl::PostField.content('proc_without_type') { 'alpha' }
261
+ proc_with_type = Curl::PostField.content('proc_with_type', 'text/plain') { 'beta' }
262
+ direct_with_type = Curl::PostField.content('direct_with_type', 'gamma', 'text/plain')
263
+
264
+ curl.http_post([proc_without_type, proc_with_type, direct_with_type])
265
+ body = curl.body_str
266
+
267
+ assert_match(/name="proc_without_type"/, body)
268
+ assert_match(/alpha/, body)
269
+ assert_match(/name="proc_with_type"/, body)
270
+ assert_match(/beta/, body)
271
+ assert_match(/name="direct_with_type"/, body)
272
+ assert_match(/gamma/, body)
273
+ assert_match(/Content-Type: text\/plain/, body)
274
+ end
275
+
276
+ def test_multipart_file_variants_include_buffered_and_local_uploads
277
+ readme = File.expand_path(File.join(File.dirname(__FILE__), '..', 'README.md'))
278
+
279
+ proc_without_type = Curl::PostField.file('proc_without_type', 'proc_without_type.txt') { 'alpha' }
280
+ proc_with_type = Curl::PostField.file('proc_with_type', 'proc_with_type.txt') { 'beta' }
281
+ proc_with_type.content_type = 'text/plain'
282
+
283
+ direct_without_type = Curl::PostField.file('direct_without_type', 'ignored.txt', 'direct_without_type.txt')
284
+ direct_without_type.content = 'gamma'
285
+ direct_without_type.local_file = nil
286
+
287
+ direct_with_type = Curl::PostField.file('direct_with_type', 'ignored.txt', 'direct_with_type.txt')
288
+ direct_with_type.content = 'delta'
289
+ direct_with_type.local_file = nil
290
+ direct_with_type.content_type = 'text/plain'
291
+
292
+ local_with_type = Curl::PostField.file('local_with_type', readme)
293
+ local_with_type.content_type = 'text/plain'
294
+
295
+ curl = Curl::Easy.new(TestServlet.url)
296
+ curl.multipart_form_post = true
297
+ curl.http_post([proc_without_type, proc_with_type, direct_without_type, direct_with_type, local_with_type])
298
+ body = curl.body_str
299
+
300
+ assert_match(/name="proc_without_type"/, body)
301
+ assert_match(/filename="proc_without_type.txt"/, body)
302
+ assert_match(/alpha/, body)
303
+
304
+ assert_match(/name="proc_with_type"/, body)
305
+ assert_match(/filename="proc_with_type.txt"/, body)
306
+ assert_match(/beta/, body)
307
+
308
+ assert_match(/name="direct_without_type"/, body)
309
+ assert_match(/filename="direct_without_type.txt"/, body)
310
+ assert_match(/gamma/, body)
311
+
312
+ assert_match(/name="direct_with_type"/, body)
313
+ assert_match(/filename="direct_with_type.txt"/, body)
314
+ assert_match(/delta/, body)
315
+
316
+ assert_match(/name="local_with_type"/, body)
317
+ assert_match(/Curb - Libcurl bindings for Ruby/, body)
318
+ assert_match(/Content-Type: text\/plain/, body)
319
+ end
144
320
  end