webmock 3.5.1 → 3.18.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/CI.yml +38 -0
  3. data/CHANGELOG.md +343 -2
  4. data/Gemfile +1 -1
  5. data/README.md +116 -32
  6. data/Rakefile +12 -4
  7. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +221 -0
  8. data/lib/webmock/http_lib_adapters/curb_adapter.rb +12 -3
  9. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
  10. data/lib/webmock/http_lib_adapters/excon_adapter.rb +3 -0
  11. data/lib/webmock/http_lib_adapters/http_rb/client.rb +2 -1
  12. data/lib/webmock/http_lib_adapters/http_rb/response.rb +27 -3
  13. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +5 -3
  14. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +6 -2
  15. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +23 -6
  16. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
  17. data/lib/webmock/http_lib_adapters/net_http.rb +35 -103
  18. data/lib/webmock/http_lib_adapters/patron_adapter.rb +1 -1
  19. data/lib/webmock/request_body_diff.rb +1 -1
  20. data/lib/webmock/request_pattern.rb +106 -56
  21. data/lib/webmock/request_signature.rb +2 -2
  22. data/lib/webmock/request_stub.rb +15 -0
  23. data/lib/webmock/response.rb +19 -13
  24. data/lib/webmock/rspec.rb +2 -1
  25. data/lib/webmock/stub_registry.rb +26 -11
  26. data/lib/webmock/test_unit.rb +1 -3
  27. data/lib/webmock/util/query_mapper.rb +4 -2
  28. data/lib/webmock/util/uri.rb +8 -8
  29. data/lib/webmock/version.rb +1 -1
  30. data/lib/webmock/webmock.rb +20 -3
  31. data/lib/webmock.rb +1 -0
  32. data/minitest/webmock_spec.rb +1 -1
  33. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  34. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  35. data/spec/acceptance/curb/curb_spec.rb +34 -5
  36. data/spec/acceptance/em_http_request/em_http_request_spec.rb +57 -1
  37. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +2 -2
  38. data/spec/acceptance/excon/excon_spec.rb +2 -2
  39. data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
  40. data/spec/acceptance/http_rb/http_rb_spec.rb +11 -0
  41. data/spec/acceptance/manticore/manticore_spec.rb +51 -0
  42. data/spec/acceptance/net_http/net_http_shared.rb +46 -9
  43. data/spec/acceptance/net_http/net_http_spec.rb +87 -23
  44. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  45. data/spec/acceptance/patron/patron_spec.rb +19 -21
  46. data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
  47. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
  48. data/spec/acceptance/shared/callbacks.rb +3 -2
  49. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
  50. data/spec/acceptance/shared/request_expectations.rb +7 -0
  51. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  52. data/spec/acceptance/shared/stubbing_requests.rb +40 -0
  53. data/spec/support/webmock_server.rb +1 -0
  54. data/spec/unit/request_pattern_spec.rb +201 -49
  55. data/spec/unit/request_signature_spec.rb +21 -1
  56. data/spec/unit/request_stub_spec.rb +35 -0
  57. data/spec/unit/response_spec.rb +51 -19
  58. data/spec/unit/util/query_mapper_spec.rb +7 -0
  59. data/spec/unit/util/uri_spec.rb +74 -2
  60. data/spec/unit/webmock_spec.rb +108 -5
  61. data/test/test_webmock.rb +6 -0
  62. data/webmock.gemspec +15 -7
  63. metadata +78 -35
  64. data/.travis.yml +0 -21
@@ -18,7 +18,7 @@ shared_context "complex cross-concern behaviors" do |*adapter_info|
18
18
  expect(played_back_response).to eq(real_response)
19
19
  end
20
20
 
21
- let(:no_content_url) { 'http://httpstat.us/204' }
21
+ let(:no_content_url) { 'https://httpstat.us/204' }
22
22
  [nil, ''].each do |stub_val|
23
23
  it "returns the same value (nil or "") for a request stubbed as #{stub_val.inspect} that a real empty response has", net_connect: true do
24
24
  unless http_library == :curb
@@ -172,6 +172,13 @@ shared_context "request expectations" do |*adapter_info|
172
172
  expect(a_request(:get, "www.example.com").with(query: hash_excluding(a: ['b', 'c']))).to have_been_made
173
173
  }.not_to raise_error
174
174
  end
175
+
176
+ it 'should satisfy expectation if the request was executed with an empty array in the query params' do
177
+ expect {
178
+ http_request(:get, "http://www.example.com/?a[]")
179
+ expect(a_request(:get, "www.example.com").with(query: hash_including(a: []))).to have_been_made
180
+ }.not_to raise_error
181
+ end
175
182
  end
176
183
 
177
184
  context "when using flat array notation" do
@@ -64,7 +64,10 @@ shared_context "declared responses" do |*adapter_info|
64
64
  it "should return response with declared headers" do
65
65
  stub_request(:get, "www.example.com").to_return(headers: SAMPLE_HEADERS)
66
66
  response = http_request(:get, "http://www.example.com/")
67
- expect(response.headers["Content-Length"]).to eq("8888")
67
+ expect(response.headers["Accept"]).to eq("application/json")
68
+ unless adapter_info.include?(:no_content_length_header)
69
+ expect(response.headers["Content-Length"]).to eq("8888")
70
+ end
68
71
  end
69
72
 
70
73
  it "should return response with declared headers even if there are multiple headers with the same key" do
@@ -171,13 +174,22 @@ shared_context "declared responses" do |*adapter_info|
171
174
  end
172
175
 
173
176
  it "should return recorded headers" do
174
- expect(@response.headers).to eq({
175
- "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
176
- "Content-Type"=>"text/html; charset=UTF-8",
177
- "Content-Length"=>"419",
178
- "Connection"=>"Keep-Alive",
179
- "Accept"=>"image/jpeg, image/png"
180
- })
177
+ if adapter_info.include?(:no_content_length_header)
178
+ expect(@response.headers).to eq({
179
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
180
+ "Content-Type"=>"text/html; charset=UTF-8",
181
+ "Connection"=>"Keep-Alive",
182
+ "Accept"=>"image/jpeg, image/png"
183
+ })
184
+ else
185
+ expect(@response.headers).to eq({
186
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
187
+ "Content-Type"=>"text/html; charset=UTF-8",
188
+ "Content-Length"=>"419",
189
+ "Connection"=>"Keep-Alive",
190
+ "Accept"=>"image/jpeg, image/png"
191
+ })
192
+ end
181
193
  end
182
194
 
183
195
  it "should return recorded body" do
@@ -205,13 +217,22 @@ shared_context "declared responses" do |*adapter_info|
205
217
  end
206
218
 
207
219
  it "should return recorded headers" do
208
- expect(@response.headers).to eq({
209
- "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
210
- "Content-Type"=>"text/html; charset=UTF-8",
211
- "Content-Length"=>"419",
212
- "Connection"=>"Keep-Alive",
213
- "Accept"=>"image/jpeg, image/png"
214
- })
220
+ if adapter_info.include?(:no_content_length_header)
221
+ expect(@response.headers).to eq({
222
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
223
+ "Content-Type"=>"text/html; charset=UTF-8",
224
+ "Connection"=>"Keep-Alive",
225
+ "Accept"=>"image/jpeg, image/png"
226
+ })
227
+ else
228
+ expect(@response.headers).to eq({
229
+ "Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
230
+ "Content-Type"=>"text/html; charset=UTF-8",
231
+ "Content-Length"=>"419",
232
+ "Connection"=>"Keep-Alive",
233
+ "Accept"=>"image/jpeg, image/png"
234
+ })
235
+ end
215
236
  end
216
237
 
217
238
  it "should return recorded body" do
@@ -68,6 +68,11 @@ shared_examples_for "stubbing requests" do |*adapter_info|
68
68
  stub_request(:get, 'www.example.com').with(query: hash_excluding(a: ['b', 'c'])).to_return(body: 'abc')
69
69
  expect(http_request(:get, 'http://www.example.com/?a[]=c&a[]=d&b=1').body).to eq('abc')
70
70
  end
71
+
72
+ it "should return stubbed response when stub expects an empty array" do
73
+ stub_request(:get, 'www.example.com').with(query: { a: [] }).to_return(body: 'abc')
74
+ expect(http_request(:get, 'http://www.example.com/?a[]').body).to eq('abc')
75
+ end
71
76
  end
72
77
 
73
78
  describe "based on method" do
@@ -588,6 +593,23 @@ shared_examples_for "stubbing requests" do |*adapter_info|
588
593
  end
589
594
  end
590
595
  end
596
+
597
+ context "when global stub should be invoked last" do
598
+ before do
599
+ WebMock.globally_stub_request(:after_local_stubs) do
600
+ { body: "global stub body" }
601
+ end
602
+ end
603
+
604
+ it "uses global stub when non-global stub is not defined" do
605
+ expect(http_request(:get, "http://www.example.com/").body).to eq("global stub body")
606
+ end
607
+
608
+ it "uses non-global stub first" do
609
+ stub_request(:get, "www.example.com").to_return(body: 'non-global stub body')
610
+ expect(http_request(:get, "http://www.example.com/").body).to eq("non-global stub body")
611
+ end
612
+ end
591
613
  end
592
614
 
593
615
  describe "when stubbing request with a block evaluated on request" do
@@ -635,4 +657,22 @@ shared_examples_for "stubbing requests" do |*adapter_info|
635
657
  }.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://www.example.com/))
636
658
  end
637
659
  end
660
+
661
+ describe "in Rspec around(:each) hook" do
662
+ # order goes
663
+ # around(:each)
664
+ # before(:each)
665
+ # after(:each)
666
+ # anything after example.run in around(:each)
667
+ around(:each) do |example|
668
+ example.run
669
+ expect {
670
+ http_request(:get, "http://www.example.com/")
671
+ }.to_not raise_error # WebMock::NetConnectNotAllowedError
672
+ end
673
+
674
+ it "should still allow me to make a mocked request" do
675
+ stub_request(:get, "www.example.com")
676
+ end
677
+ end
638
678
  end
@@ -36,6 +36,7 @@ class WebMockServer
36
36
  end
37
37
  end
38
38
  server.start do |socket|
39
+ socket.read(1)
39
40
  socket.puts <<-EOT.gsub(/^\s+\|/, '')
40
41
  |HTTP/1.1 200 OK\r
41
42
  |Date: Fri, 31 Dec 1999 23:59:59 GMT\r
@@ -58,7 +58,7 @@ describe WebMock::RequestPattern do
58
58
  end
59
59
 
60
60
  it "should raise an error if neither options or block is provided" do
61
- expect { @request_pattern.with() }.to raise_error('#with method invoked with no arguments. Either options hash or block must be specified.')
61
+ expect { @request_pattern.with() }.to raise_error('#with method invoked with no arguments. Either options hash or block must be specified. Created a block with do..end? Try creating it with curly braces {} instead.')
62
62
  end
63
63
  end
64
64
 
@@ -111,6 +111,21 @@ describe WebMock::RequestPattern do
111
111
  to match(WebMock::RequestSignature.new(:get, "www.example.com"))
112
112
  end
113
113
 
114
+ it "should match if uri matches requesst uri as URI object" do
115
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"))).
116
+ to match(WebMock::RequestSignature.new(:get, "www.example.com"))
117
+ end
118
+
119
+ it "should match if uri proc pattern returning true" do
120
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true })).
121
+ to match(WebMock::RequestSignature.new(:get, "www.example.com"))
122
+ end
123
+
124
+ it "should not match if uri proc pattern returns false" do
125
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { false })).
126
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com"))
127
+ end
128
+
114
129
  it "should match if uri Addressable::Template pattern matches unescaped form of request uri" do
115
130
  expect(WebMock::RequestPattern.new(:get, Addressable::Template.new("www.example.com/{any_path}"))).
116
131
  to match(WebMock::RequestSignature.new(:get, "www.example.com/my%20path"))
@@ -121,12 +136,41 @@ describe WebMock::RequestPattern do
121
136
  to match(WebMock::RequestSignature.new(:get, "www.example.com"))
122
137
  end
123
138
 
139
+ it "should match if uri Addressable::Template pattern matches request uri without TLD" do
140
+ expect(WebMock::RequestPattern.new(:get, Addressable::Template.new("localhost"))).
141
+ to match(WebMock::RequestSignature.new(:get, "localhost"))
142
+ end
143
+
124
144
  it "should match if Addressable::Template pattern that has ip address host matches request uri" do
125
145
  signature = WebMock::RequestSignature.new(:get, "127.0.0.1:3000/1234")
126
146
  uri = Addressable::Template.new("127.0.0.1:3000/{id}")
127
147
  expect(WebMock::RequestPattern.new(:get, uri)).to match(signature)
128
148
  end
129
149
 
150
+ it "should match if Addressable::Template pattern that has ip address host without port matches request uri" do
151
+ signature = WebMock::RequestSignature.new(:get, "127.0.0.1/1234")
152
+ uri = Addressable::Template.new("127.0.0.1/{id}")
153
+ expect(WebMock::RequestPattern.new(:get, uri)).to match(signature)
154
+ end
155
+
156
+ it "should match if Addressable::Template pattern host matches request uri" do
157
+ signature = WebMock::RequestSignature.new(:get, "www.example.com")
158
+ uri = Addressable::Template.new("{subdomain}.example.com")
159
+ expect(WebMock::RequestPattern.new(:get, uri)).to match(signature)
160
+ end
161
+
162
+ it "should not match if Addressable::Template pattern host does not match request uri" do
163
+ signature = WebMock::RequestSignature.new(:get, "www.bad-example.com")
164
+ uri = Addressable::Template.new("{subdomain}.example.com")
165
+ expect(WebMock::RequestPattern.new(:get, uri)).not_to match(signature)
166
+ end
167
+
168
+ it "should match if uri Addressable::Template pattern matches request uri without a schema and a path " do
169
+ signature = WebMock::RequestSignature.new(:get, "127.0.0.1:3000")
170
+ uri = Addressable::Template.new("127.0.0.1:3000")
171
+ expect(WebMock::RequestPattern.new(:get, uri)).to match(signature)
172
+ end
173
+
130
174
  it "should match for uris with same parameters as pattern" do
131
175
  expect(WebMock::RequestPattern.new(:get, "www.example.com?a=1&b=2")).
132
176
  to match(WebMock::RequestSignature.new(:get, "www.example.com?a=1&b=2"))
@@ -194,7 +238,7 @@ describe WebMock::RequestPattern do
194
238
  to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
195
239
  end
196
240
 
197
- it "should match request query params if params don't match" do
241
+ it "should not match request query params if params don't match" do
198
242
  expect(WebMock::RequestPattern.new(:get, /.*example.*/, query: {"x" => ["b", "c"]})).
199
243
  not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
200
244
  end
@@ -224,13 +268,85 @@ describe WebMock::RequestPattern do
224
268
  end
225
269
  end
226
270
 
271
+ describe "when uri is described as URI" do
272
+ it "should match request query params" do
273
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"), query: {"a" => ["b", "c"]})).
274
+ to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
275
+ end
276
+
277
+ it "should not match request query params if params don't match" do
278
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"), query: {"x" => ["b", "c"]})).
279
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
280
+ end
281
+
282
+ it "should match when query params are declared as HashIncluding matcher matching params" do
283
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"),
284
+ query: WebMock::Matchers::HashIncludingMatcher.new({"a" => ["b", "c"]}))).
285
+ to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
286
+ end
287
+
288
+ it "should not match when query params are declared as HashIncluding matcher not matching params" do
289
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"),
290
+ query: WebMock::Matchers::HashIncludingMatcher.new({"x" => ["b", "c"]}))).
291
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
292
+ end
293
+
294
+ it "should match when query params are declared as RSpec HashIncluding matcher matching params" do
295
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"),
296
+ query: RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher.new({"a" => ["b", "c"]}))).
297
+ to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
298
+ end
299
+
300
+ it "should not match when query params are declared as RSpec HashIncluding matcher not matching params" do
301
+ expect(WebMock::RequestPattern.new(:get, URI.parse("www.example.com"),
302
+ query: RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher.new({"a" => ["b", "d"]}))).
303
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
304
+ end
305
+ end
306
+
307
+ describe "when uri is described as a proc" do
308
+ it "should match request query params" do
309
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true }, query: {"a" => ["b", "c"]})).
310
+ to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
311
+ end
312
+
313
+ it "should not match request query params if params don't match" do
314
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true }, query: {"x" => ["b", "c"]})).
315
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
316
+ end
317
+
318
+ it "should match when query params are declared as HashIncluding matcher matching params" do
319
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true },
320
+ query: WebMock::Matchers::HashIncludingMatcher.new({"a" => ["b", "c"]}))).
321
+ to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
322
+ end
323
+
324
+ it "should not match when query params are declared as HashIncluding matcher not matching params" do
325
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true },
326
+ query: WebMock::Matchers::HashIncludingMatcher.new({"x" => ["b", "c"]}))).
327
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
328
+ end
329
+
330
+ it "should match when query params are declared as RSpec HashIncluding matcher matching params" do
331
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true },
332
+ query: RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher.new({"a" => ["b", "c"]}))).
333
+ to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
334
+ end
335
+
336
+ it "should not match when query params are declared as RSpec HashIncluding matcher not matching params" do
337
+ expect(WebMock::RequestPattern.new(:get, ->(uri) { true },
338
+ query: RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher.new({"a" => ["b", "d"]}))).
339
+ not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c&b=1"))
340
+ end
341
+ end
342
+
227
343
  describe "when uri is described as Addressable::Template" do
228
344
  it "should raise error if query params are specified" do
229
345
  expect(WebMock::RequestPattern.new(:get, Addressable::Template.new("www.example.com"), query: {"a" => ["b", "c"]})).
230
346
  to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
231
347
  end
232
348
 
233
- it "should match request query params if params don't match" do
349
+ it "should not match request query params if params don't match" do
234
350
  expect(WebMock::RequestPattern.new(:get, Addressable::Template.new("www.example.com"), query: {"x" => ["b", "c"]})).
235
351
  not_to match(WebMock::RequestSignature.new(:get, "www.example.com?a[]=b&a[]=c"))
236
352
  end
@@ -418,69 +534,105 @@ describe WebMock::RequestPattern do
418
534
  end
419
535
 
420
536
  describe "for request with json body and content type is set to json" do
421
- it "should match when hash matches body" do
422
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
423
- to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: 'application/json'},
424
- body: "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}"))
425
- end
537
+ shared_examples "a json body" do
538
+ it "should match when hash matches body" do
539
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
540
+ to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
541
+ body: "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}"))
542
+ end
426
543
 
427
- it "should match if hash matches body in different form" do
428
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
429
- to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: 'application/json'},
430
- body: "{\"a\":\"1\",\"b\":\"five\",\"c\":{\"d\":[\"e\",\"f\"]}}"))
431
- end
544
+ it "should match if hash matches body in different form" do
545
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
546
+ to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
547
+ body: "{\"a\":\"1\",\"b\":\"five\",\"c\":{\"d\":[\"e\",\"f\"]}}"))
548
+ end
549
+
550
+ it "should match if the request body has a top level array" do
551
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: [{a: 1}])).
552
+ to match(WebMock::RequestSignature.new(:post, "www.example.com",
553
+ headers: {content_type: content_type}, body: "[{\"a\":1}]"))
554
+ end
432
555
 
433
- it "should not match when body is not json" do
434
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
435
- not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
436
- headers: {content_type: 'application/json'}, body: "foo bar"))
437
- end
556
+ it "should not match if the request body has a different top level array" do
557
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: ["a", "b"])).
558
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
559
+ headers: {content_type: content_type}, body: "[\"a\", \"c\"]"))
560
+ end
561
+
562
+ it "should not match when body is not json" do
563
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
564
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
565
+ headers: {content_type: content_type}, body: "foo bar"))
566
+ end
567
+
568
+ it "should not match if request body is different" do
569
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1, b: 2})).
570
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
571
+ headers: {content_type: content_type}, body: "{\"a\":1,\"c\":null}"))
572
+ end
438
573
 
439
- it "should not match if request body is different" do
440
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1, b: 2})).
441
- not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
442
- headers: {content_type: 'application/json'}, body: "{\"a\":1,\"c\":null}"))
574
+ it "should not match if request body is has less params than pattern" do
575
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1, b: 2})).
576
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
577
+ headers: {content_type: content_type}, body: "{\"a\":1}"))
578
+ end
579
+
580
+ it "should not match if request body is has more params than pattern" do
581
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1})).
582
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
583
+ headers: {content_type: content_type}, body: "{\"a\":1,\"c\":1}"))
584
+ end
443
585
  end
444
586
 
445
- it "should not match if request body is has less params than pattern" do
446
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1, b: 2})).
447
- not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
448
- headers: {content_type: 'application/json'}, body: "{\"a\":1}"))
587
+ context "standard application/json" do
588
+ let(:content_type) { 'application/json' }
589
+ it_behaves_like "a json body"
449
590
  end
450
591
 
451
- it "should not match if request body is has more params than pattern" do
452
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: {a: 1})).
453
- not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
454
- headers: {content_type: 'application/json'}, body: "{\"a\":1,\"c\":1}"))
592
+ context "custom json content type" do
593
+ let(:content_type) { 'application/vnd.api+json' }
594
+ it_behaves_like "a json body"
455
595
  end
456
596
  end
457
597
 
458
598
  describe "for request with xml body and content type is set to xml" do
459
599
  let(:body_hash) { {"opt" => {:a => '1', :b => 'five', 'c' => {'d' => ['e', 'f']}}} }
460
600
 
461
- it "should match when hash matches body" do
462
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
463
- to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: 'application/xml'},
464
- body: "<opt a=\"1\" b=\"five\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
465
- end
601
+ shared_examples "a xml body" do
602
+ it "should match when hash matches body" do
603
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
604
+ to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
605
+ body: "<opt a=\"1\" b=\"five\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
606
+ end
466
607
 
467
- it "should match if hash matches body in different form" do
468
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
469
- to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: 'application/xml'},
470
- body: "<opt b=\"five\" a=\"1\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
471
- end
608
+ it "should match if hash matches body in different form" do
609
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
610
+ to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: content_type},
611
+ body: "<opt b=\"five\" a=\"1\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
612
+ end
472
613
 
473
- it "should not match when body is not xml" do
474
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
475
- not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
476
- headers: {content_type: 'application/xml'}, body: "foo bar"))
477
- end
614
+ it "should not match when body is not xml" do
615
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
616
+ not_to match(WebMock::RequestSignature.new(:post, "www.example.com",
617
+ headers: {content_type: content_type}, body: "foo bar"))
618
+ end
478
619
 
479
- it "matches when the content type include a charset" do
480
- expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
481
- to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: 'application/xml;charset=UTF-8'},
482
- body: "<opt a=\"1\" b=\"five\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
620
+ it "matches when the content type include a charset" do
621
+ expect(WebMock::RequestPattern.new(:post, 'www.example.com', body: body_hash)).
622
+ to match(WebMock::RequestSignature.new(:post, "www.example.com", headers: {content_type: "#{content_type};charset=UTF-8"},
623
+ body: "<opt a=\"1\" b=\"five\">\n <c>\n <d>e</d>\n <d>f</d>\n </c>\n</opt>\n"))
624
+
625
+ end
626
+ end
627
+
628
+ context "standard application/xml" do
629
+ let(:content_type) { 'application/xml' }
630
+ it_behaves_like "a xml body"
631
+ end
483
632
 
633
+ context "custom xml content type" do
634
+ let(:content_type) { 'application/atom+xml' }
635
+ it_behaves_like "a xml body"
484
636
  end
485
637
  end
486
638
  end
@@ -18,7 +18,7 @@ describe WebMock::RequestSignature do
18
18
  end
19
19
 
20
20
  it "assigns normalized headers" do
21
- expect(WebMock::Util::Headers).to receive(:normalize_headers).with('A' => 'a').and_return('B' => 'b')
21
+ allow(WebMock::Util::Headers).to receive(:normalize_headers).with({'A' => 'a'}.freeze).and_return('B' => 'b')
22
22
  expect(
23
23
  WebMock::RequestSignature.new(:get, "www.example.com", headers: {'A' => 'a'}).headers
24
24
  ).to eq({'B' => 'b'})
@@ -125,11 +125,21 @@ describe WebMock::RequestSignature do
125
125
  expect(subject.url_encoded?).to be true
126
126
  end
127
127
 
128
+ it "returns true if the headers are urlencoded with a specified charset" do
129
+ subject.headers = { "Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8" }
130
+ expect(subject.url_encoded?).to be true
131
+ end
132
+
128
133
  it "returns false if the headers are NOT urlencoded" do
129
134
  subject.headers = { "Content-Type" => "application/made-up-format" }
130
135
  expect(subject.url_encoded?).to be false
131
136
  end
132
137
 
138
+ it "returns false when no content type header is present" do
139
+ subject.headers = { "Some-Header" => "some-value" }
140
+ expect(subject.url_encoded?).to be false
141
+ end
142
+
133
143
  it "returns false when no headers are set" do
134
144
  subject.headers = nil
135
145
  expect(subject.url_encoded?).to be false
@@ -142,11 +152,21 @@ describe WebMock::RequestSignature do
142
152
  expect(subject.json_headers?).to be true
143
153
  end
144
154
 
155
+ it "returns true if the headers are json with a specified charset" do
156
+ subject.headers = { "Content-Type" => "application/json; charset=UTF-8" }
157
+ expect(subject.json_headers?).to be true
158
+ end
159
+
145
160
  it "returns false if the headers are NOT json" do
146
161
  subject.headers = { "Content-Type" => "application/made-up-format" }
147
162
  expect(subject.json_headers?).to be false
148
163
  end
149
164
 
165
+ it "returns false when no content type header is present" do
166
+ subject.headers = { "Some-Header" => "some-value" }
167
+ expect(subject.json_headers?).to be false
168
+ end
169
+
150
170
  it "returns false when no headers are set" do
151
171
  subject.headers = nil
152
172
  expect(subject.json_headers?).to be false
@@ -50,6 +50,41 @@ describe WebMock::RequestStub do
50
50
 
51
51
  end
52
52
 
53
+ describe "to_return_json" do
54
+
55
+ it "should raise if a block is given" do
56
+ expect {
57
+ @request_stub.to_return_json(body: "abc", status: 500) { puts "don't call me" }
58
+ }.to raise_error(ArgumentError, '#to_return_json does not support passing a block')
59
+ end
60
+
61
+ it "should assign responses normally" do
62
+ @request_stub.to_return_json([{body: "abc"}, {body: "def"}])
63
+ expect([@request_stub.response.body, @request_stub.response.body]).to eq(["abc", "def"])
64
+ end
65
+
66
+ it "should json-ify a Hash body" do
67
+ @request_stub.to_return_json(body: {abc: "def"}, status: 500)
68
+ expect(@request_stub.response.body).to eq({abc: "def"}.to_json)
69
+ expect(@request_stub.response.status).to eq([500, ""])
70
+ end
71
+
72
+ it "should apply the content_type header" do
73
+ @request_stub.to_return_json(body: {abc: "def"}, status: 500)
74
+ expect(@request_stub.response.headers).to eq({"Content-Type"=>"application/json"})
75
+ end
76
+
77
+ it "should preserve existing headers" do
78
+ @request_stub.to_return_json(headers: {"A" => "a"}, body: "")
79
+ expect(@request_stub.response.headers).to eq({"A"=>"a", "Content-Type"=>"application/json"})
80
+ end
81
+
82
+ it "should allow callsites to override content_type header" do
83
+ @request_stub.to_return_json(headers: {content_type: 'application/super-special-json'})
84
+ expect(@request_stub.response.headers).to eq({"Content-Type"=>"application/super-special-json"})
85
+ end
86
+ end
87
+
53
88
  describe "then" do
54
89
  it "should return stub without any modifications, acting as syntactic sugar" do
55
90
  expect(@request_stub.then).to eq(@request_stub)