webmock 1.21.0 → 1.22.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.travis.yml +8 -0
  2. data/CHANGELOG.md +51 -1
  3. data/Gemfile +0 -1
  4. data/README.md +17 -6
  5. data/lib/webmock.rb +4 -1
  6. data/lib/webmock/config.rb +2 -0
  7. data/lib/webmock/errors.rb +3 -21
  8. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +5 -0
  9. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +123 -0
  10. data/lib/webmock/http_lib_adapters/net_http.rb +16 -2
  11. data/lib/webmock/request_body_diff.rb +63 -0
  12. data/lib/webmock/request_execution_verifier.rb +24 -21
  13. data/lib/webmock/request_pattern.rb +2 -0
  14. data/lib/webmock/request_signature.rb +5 -1
  15. data/lib/webmock/request_signature_snippet.rb +61 -0
  16. data/lib/webmock/rspec/matchers.rb +0 -1
  17. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +4 -0
  18. data/lib/webmock/rspec/matchers/webmock_matcher.rb +4 -0
  19. data/lib/webmock/stub_request_snippet.rb +4 -0
  20. data/lib/webmock/util/json.rb +25 -6
  21. data/lib/webmock/util/query_mapper.rb +6 -4
  22. data/lib/webmock/version.rb +1 -1
  23. data/lib/webmock/webmock.rb +12 -0
  24. data/spec/acceptance/em_http_request/em_http_request_spec.rb +60 -0
  25. data/spec/acceptance/http_rb/http_rb_spec.rb +2 -2
  26. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +1 -1
  27. data/spec/acceptance/httpclient/httpclient_spec.rb +8 -7
  28. data/spec/acceptance/manticore/manticore_spec.rb +56 -0
  29. data/spec/acceptance/manticore/manticore_spec_helper.rb +31 -0
  30. data/spec/acceptance/net_http/net_http_spec.rb +24 -1
  31. data/spec/acceptance/shared/request_expectations.rb +10 -10
  32. data/spec/spec_helper.rb +4 -0
  33. data/spec/unit/errors_spec.rb +45 -4
  34. data/spec/unit/request_body_diff_spec.rb +90 -0
  35. data/spec/unit/request_execution_verifier_spec.rb +48 -11
  36. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  37. data/spec/unit/request_signature_spec.rb +61 -23
  38. data/spec/unit/stub_registry_spec.rb +1 -1
  39. data/spec/unit/util/json_spec.rb +29 -3
  40. data/spec/unit/util/query_mapper_spec.rb +15 -4
  41. data/spec/unit/webmock_spec.rb +4 -0
  42. data/test/shared_test.rb +2 -2
  43. data/webmock.gemspec +4 -1
  44. metadata +63 -24
@@ -33,32 +33,16 @@ module WebMock
33
33
  end
34
34
  end
35
35
 
36
+ def description
37
+ "request #{request_pattern.to_s} #{quantity_phrase.strip}"
38
+ end
36
39
 
37
40
  def failure_message
38
- expected_times_executed = @expected_times_executed || 1
39
- text = if @at_least_times_executed
40
- %Q(The request #{request_pattern.to_s} was expected to execute at least #{times(@at_least_times_executed)} but it executed #{times(times_executed)})
41
- elsif @at_most_times_executed
42
- %Q(The request #{request_pattern.to_s} was expected to execute at most #{times(@at_most_times_executed)} but it executed #{times(times_executed)})
43
- else
44
- %Q(The request #{request_pattern.to_s} was expected to execute #{times(expected_times_executed)} but it executed #{times(times_executed)})
45
- end
46
- text << self.class.executed_requests_message
47
- text
41
+ failure_message_phrase(is_negated=false)
48
42
  end
49
43
 
50
44
  def failure_message_when_negated
51
- text = if @at_least_times_executed
52
- %Q(The request #{request_pattern.to_s} was not expected to execute at least #{times(@at_least_times_executed)} but it executed #{times(times_executed)})
53
- elsif @at_most_times_executed
54
- %Q(The request #{request_pattern.to_s} was not expected to execute at most #{times(@at_most_times_executed)} but it executed #{times(times_executed)})
55
- elsif @expected_times_executed
56
- %Q(The request #{request_pattern.to_s} was not expected to execute #{times(expected_times_executed)} but it executed #{times(times_executed)})
57
- else
58
- %Q(The request #{request_pattern.to_s} was expected to execute 0 times but it executed #{times(times_executed)})
59
- end
60
- text << self.class.executed_requests_message
61
- text
45
+ failure_message_phrase(is_negated=true)
62
46
  end
63
47
 
64
48
  def self.executed_requests_message
@@ -67,6 +51,25 @@ module WebMock
67
51
 
68
52
  private
69
53
 
54
+ def failure_message_phrase(is_negated=false)
55
+ negation = is_negated ? "was not" : "was"
56
+ text = "The request #{request_pattern.to_s} #{negation} expected to execute #{quantity_phrase(is_negated)}but it executed #{times(times_executed)}"
57
+ text << self.class.executed_requests_message
58
+ text
59
+ end
60
+
61
+ def quantity_phrase(is_negated=false)
62
+ if @at_least_times_executed
63
+ "at least #{times(@at_least_times_executed)} "
64
+ elsif @at_most_times_executed
65
+ "at most #{times(@at_most_times_executed)} "
66
+ elsif @expected_times_executed
67
+ "#{times(@expected_times_executed)} "
68
+ else
69
+ is_negated ? "" : "#{times(1)} "
70
+ end
71
+ end
72
+
70
73
  def times(times)
71
74
  "#{times} time#{ (times == 1) ? '' : 's'}"
72
75
  end
@@ -200,6 +200,8 @@ module WebMock
200
200
  'text/plain' => :plain
201
201
  }
202
202
 
203
+ attr_reader :pattern
204
+
203
205
  def initialize(pattern)
204
206
  @pattern = if pattern.is_a?(Hash)
205
207
  normalize_hash(pattern)
@@ -35,7 +35,11 @@ module WebMock
35
35
  alias == eql?
36
36
 
37
37
  def url_encoded?
38
- headers && headers['Content-Type'] == 'application/x-www-form-urlencoded'
38
+ !!(headers && headers['Content-Type'] == 'application/x-www-form-urlencoded')
39
+ end
40
+
41
+ def json_headers?
42
+ !!(headers && headers['Content-Type'] == 'application/json')
39
43
  end
40
44
 
41
45
  private
@@ -0,0 +1,61 @@
1
+ require "pp"
2
+
3
+ module WebMock
4
+ class RequestSignatureSnippet
5
+
6
+ attr_reader :request_signature, :request_stub
7
+
8
+ def initialize(request_signature)
9
+ @request_signature = request_signature
10
+ @request_stub = RequestStub.from_request_signature(request_signature)
11
+ end
12
+
13
+ def stubbing_instructions
14
+ return unless WebMock.show_stubbing_instructions?
15
+
16
+ text = "You can stub this request with the following snippet:\n\n"
17
+ text << WebMock::StubRequestSnippet.new(request_stub).to_s
18
+ end
19
+
20
+ def request_stubs
21
+ return if WebMock::StubRegistry.instance.request_stubs.empty?
22
+
23
+ text = "registered request stubs:\n"
24
+ WebMock::StubRegistry.instance.request_stubs.each do |stub|
25
+ text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
26
+ add_body_diff(stub, text) if WebMock.show_body_diff?
27
+ end
28
+ text
29
+ end
30
+
31
+ private
32
+
33
+ def add_body_diff(stub, text)
34
+ body_diff_str = signature_stub_body_diff(stub)
35
+ text << "\n\n#{body_diff_str}" unless body_diff_str.empty?
36
+ end
37
+
38
+ def signature_stub_body_diff(stub)
39
+ diff = RequestBodyDiff.new(request_signature, stub).body_diff
40
+ diff.empty? ? "" : "Body diff:\n #{pretty_print_to_string(diff)}"
41
+ end
42
+
43
+ def request_params
44
+ @request_params ||=
45
+ if request_signature.json_headers?
46
+ JSON.parse(request_signature.body)
47
+ else
48
+ ""
49
+ end
50
+ end
51
+
52
+ def pretty_print_to_string(string_to_print)
53
+ StringIO.open("") do |stream|
54
+ PP.pp(string_to_print, stream)
55
+ stream.rewind
56
+ stream.read
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -12,7 +12,6 @@ module WebMock
12
12
  WebMock::RequestPatternMatcher.new
13
13
  end
14
14
 
15
-
16
15
  def have_not_been_made
17
16
  WebMock::RequestPatternMatcher.new.times(0)
18
17
  end
@@ -68,6 +68,10 @@ module WebMock
68
68
  @request_execution_verifier.failure_message_when_negated
69
69
  end
70
70
 
71
+ def description
72
+ @request_execution_verifier.description
73
+ end
74
+
71
75
  # RSpec 2 compatibility:
72
76
  alias_method :negative_failure_message, :failure_message_when_negated
73
77
  end
@@ -42,6 +42,10 @@ module WebMock
42
42
  @request_execution_verifier.failure_message_when_negated
43
43
  end
44
44
 
45
+ def description
46
+ @request_execution_verifier.description
47
+ end
48
+
45
49
  # RSpec 2 compatibility:
46
50
  alias_method :negative_failure_message, :failure_message_when_negated
47
51
  end
@@ -4,6 +4,10 @@ module WebMock
4
4
  @request_stub = request_stub
5
5
  end
6
6
 
7
+ def body_pattern
8
+ request_pattern.body_pattern
9
+ end
10
+
7
11
  def to_s(with_response = true)
8
12
  request_pattern = @request_stub.request_pattern
9
13
  string = "stub_request(:#{request_pattern.method_pattern.to_s},"
@@ -8,10 +8,13 @@
8
8
  module WebMock
9
9
  module Util
10
10
  class JSON
11
+ class ParseError < StandardError; end
12
+
11
13
  def self.parse(json)
12
- YAML.load(unescape(convert_json_to_yaml(json)))
13
- rescue ArgumentError
14
- raise ParseError, "Invalid JSON string"
14
+ yaml = unescape(convert_json_to_yaml(json))
15
+ YAML.load(yaml)
16
+ rescue ArgumentError => e
17
+ raise ParseError, "Invalid JSON string: #{yaml}, Error: #{e.inspect}"
15
18
  end
16
19
 
17
20
  protected
@@ -42,11 +45,27 @@ module WebMock
42
45
  json.gsub(/\\\//, '/')
43
46
  else
44
47
  left_pos = [-1].push(*marks)
45
- right_pos = marks << json.length
48
+ right_pos = marks << json.bytesize
46
49
  output = []
47
- left_pos.each_with_index do |left, i|
48
- output << json[left.succ..right_pos[i]]
50
+
51
+ if RUBY_VERSION != "1.9.2"
52
+ left_pos.each_with_index do |left, i|
53
+ if json.respond_to?(:byteslice)
54
+ output << json.byteslice(left.succ..right_pos[i])
55
+ else
56
+ output << json[left.succ..right_pos[i]]
57
+ end
58
+ end
59
+ else
60
+ json_as_binary = json.force_encoding("binary")
61
+ left_pos.each_with_index do |left, i|
62
+ output << json_as_binary[left.succ..right_pos[i]]
63
+ end
64
+ output.map! do |binary_str|
65
+ binary_str.force_encoding("UTF-8")
66
+ end
49
67
  end
68
+
50
69
  output = output * " "
51
70
 
52
71
  times.each { |i| output[i-1] = ' ' }
@@ -196,7 +196,11 @@ module WebMock::Util
196
196
  end
197
197
  # Useful default for OAuth and caching.
198
198
  # Only to be used for non-Array inputs. Arrays should preserve order.
199
- new_query_values.sort!
199
+ begin
200
+ new_query_values.sort! # may raise for non-comparable values
201
+ rescue NoMethodError, ArgumentError
202
+ # ignore
203
+ end
200
204
  end
201
205
 
202
206
  buffer = ''
@@ -241,7 +245,7 @@ module WebMock::Util
241
245
  when ::Hash
242
246
  value = value.map do |key, val|
243
247
  [
244
- ::Addressable::URI.encode_component(key.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
248
+ ::Addressable::URI.encode_component(key.to_s.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
245
249
  val
246
250
  ]
247
251
  end
@@ -259,8 +263,6 @@ module WebMock::Util
259
263
  buffer << "#{to_query(new_parent, val, options)}&"
260
264
  end
261
265
  buffer.chop
262
- when TrueClass
263
- parent
264
266
  else
265
267
  encoded_value = Addressable::URI.encode_component(
266
268
  value.to_s.dup, Addressable::URI::CharacterClasses::UNRESERVED
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '1.21.0' unless defined?(::WebMock::VERSION)
2
+ VERSION = '1.22.1' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -75,6 +75,18 @@ module WebMock
75
75
  end
76
76
  end
77
77
 
78
+ def self.show_body_diff!
79
+ Config.instance.show_body_diff = true
80
+ end
81
+
82
+ def self.hide_body_diff!
83
+ Config.instance.show_body_diff = false
84
+ end
85
+
86
+ def self.show_body_diff?
87
+ Config.instance.show_body_diff
88
+ end
89
+
78
90
  def self.hide_stubbing_instructions!
79
91
  Config.instance.show_stubbing_instructions = false
80
92
  end
@@ -229,6 +229,66 @@ unless RUBY_PLATFORM =~ /java/
229
229
  expect(http_request(:post, "http://www.example.com").body.bytesize).to eq(body.bytesize)
230
230
  end
231
231
 
232
+ it "should work with multiple requests to the same connection" do
233
+ stub_request(:get, "www.example.com/foo").to_return(:body => "bar")
234
+ stub_request(:get, "www.example.com/baz").to_return(:body => "wombat")
235
+ err1 = nil
236
+ err2 = nil
237
+ body1 = nil
238
+ body2 = nil
239
+ i = 0
240
+
241
+ EM.run do
242
+ conn = EM::HttpRequest.new("http://www.example.com")
243
+ conn.get(:path => "/foo").callback do |resp|
244
+ body1 = resp.response
245
+ i += 1; EM.stop if i == 2
246
+ end.errback do |resp|
247
+ err1 = resp.error
248
+ i += 1; EM.stop if i == 2
249
+ end
250
+
251
+ conn.get(:path => "/baz").callback do |resp|
252
+ body2 = resp.response
253
+ i += 1; EM.stop if i == 2
254
+ end.errback do |resp|
255
+ err2 = resp.error
256
+ i += 1; EM.stop if i == 2
257
+ end
258
+ end
259
+
260
+ expect(err1).to be(nil)
261
+ expect(err2).to be(nil)
262
+ expect(body1).to eq("bar")
263
+ expect(body2).to eq("wombat")
264
+ end
265
+
266
+ it "should work with multiple requests to the same connection when the first request times out" do
267
+ stub_request(:get, "www.example.com/foo").to_timeout.then.to_return(:status => 200, :body => "wombat")
268
+ err = nil
269
+ body = nil
270
+
271
+ EM.run do
272
+ conn = EM::HttpRequest.new("http://www.example.com")
273
+ conn.get(:path => "/foo").callback do |resp|
274
+ err = :success_from_timeout
275
+ EM.stop
276
+ end.errback do |resp|
277
+ conn.get(:path => "/foo").callback do |resp|
278
+ expect(resp.response_header.status).to eq(200)
279
+ body = resp.response
280
+ EM.stop
281
+ end.errback do |resp|
282
+ err = resp.error
283
+ EM.stop
284
+ end
285
+ end
286
+ end
287
+
288
+ expect(err).to be(nil)
289
+ expect(body).to eq("wombat")
290
+ end
291
+
232
292
  describe "mocking EM::HttpClient API" do
233
293
  let(:uri) { "http://www.example.com/" }
234
294
 
@@ -49,11 +49,11 @@ describe "HTTP.rb" do
49
49
  end
50
50
 
51
51
  it "restores request uri on replayed response object" do
52
- uri = URI "http://example.com/foo"
52
+ uri = Addressable::URI.parse "http://example.com/foo"
53
53
 
54
54
  stub_request :get, "example.com/foo"
55
55
  response = HTTP.get uri
56
56
 
57
- expect(response.uri).to eq uri
57
+ expect(response.uri.to_s).to eq uri.to_s
58
58
  end
59
59
  end
@@ -7,7 +7,7 @@ module HttpRbSpecHelper
7
7
  OpenStruct.new({
8
8
  :body => response.body.to_s,
9
9
  :headers => normalize_headers(response.headers.to_h),
10
- :status => response.status.to_s,
10
+ :status => response.code.to_s,
11
11
  :message => response.reason
12
12
  })
13
13
  end
@@ -113,13 +113,14 @@ describe "HTTPClient" do
113
113
  nil # to let the request be made for real
114
114
  end
115
115
 
116
- # To make two requests that have the same request signature, the headers must match.
117
- # Since the webmock server has a Set-Cookie header, the 2nd request will automatically
118
- # include a Cookie header (due to how httpclient works), so we have to set the header
119
- # manually on the first request but not on the 2nd request.
120
- http_request(:get, webmock_server_url, :client => client,
121
- :headers => { "Cookie" => "bar=; foo=" })
122
- http_request(:get, webmock_server_url, :client => client)
116
+ http_request(:get, webmock_server_url, :client => client, :headers => { "Cookie" => "bar=; foo=" })
117
+
118
+ if defined? HTTP::CookieJar
119
+ http_request(:get, webmock_server_url, :client => client, :headers => { "Cookie" => "bar=; foo=" })
120
+ else
121
+ # If http-cookie is not present, then the cookie headers will saved between requests
122
+ http_request(:get, webmock_server_url, :client => client)
123
+ end
123
124
 
124
125
  expect(request_signatures.size).to eq(2)
125
126
  # Verify the request signatures were identical as needed by this example
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'acceptance/webmock_shared'
3
+
4
+ if RUBY_PLATFORM =~ /java/
5
+ require 'acceptance/manticore/manticore_spec_helper'
6
+
7
+ describe "Manticore" do
8
+ include ManticoreSpecHelper
9
+
10
+ include_context "with WebMock", :no_status_message
11
+
12
+ context "calling http methods on Manticore directly using Manticore's facade" do
13
+ it "handles GET" do
14
+ stub_request(:get, "http://example-foo.com").to_return(:status => 301)
15
+ response = Manticore.get("http://example-foo.com")
16
+ expect(response.code).to eq(301)
17
+ end
18
+
19
+ it "handles POST" do
20
+ stub_request(:post, "http://example-foo.com").to_return(:status => 201)
21
+ response = Manticore.post("http://example-foo.com", {:hello => "world"})
22
+ expect(response.code).to eq(201)
23
+ end
24
+
25
+ it "handles PUT" do
26
+ stub_request(:put, "http://example-foo.com").to_return(:status => 409)
27
+ response = Manticore.put("http://example-foo.com", {:hello => "world"})
28
+ expect(response.code).to eq(409)
29
+ end
30
+
31
+ it "handles PATCH" do
32
+ stub_request(:patch, "http://example-foo.com").to_return(:status => 409)
33
+ response = Manticore.patch("http://example-foo.com", {:hello => "world"})
34
+ expect(response.code).to eq(409)
35
+ end
36
+
37
+ it "handles DELETE" do
38
+ stub_request(:delete, "http://example-foo.com").to_return(:status => 204)
39
+ response = Manticore.delete("http://example-foo.com", {:id => 1})
40
+ expect(response.code).to eq(204)
41
+ end
42
+
43
+ it "handles OPTIONS" do
44
+ stub_request(:options, "http://example-foo.com").to_return(:status => 200)
45
+ response = Manticore.options("http://example-foo.com")
46
+ expect(response.code).to eq(200)
47
+ end
48
+
49
+ it "handles HEAD" do
50
+ stub_request(:head, "http://example-foo.com").to_return(:status => 204)
51
+ response = Manticore.head("http://example-foo.com")
52
+ expect(response.code).to eq(204)
53
+ end
54
+ end
55
+ end
56
+ end