webmock 3.0.1 → 3.1.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -7
  3. data/CHANGELOG.md +45 -0
  4. data/README.md +16 -2
  5. data/lib/webmock.rb +3 -0
  6. data/lib/webmock/api.rb +8 -0
  7. data/lib/webmock/http_lib_adapters/curb_adapter.rb +1 -1
  8. data/lib/webmock/http_lib_adapters/excon_adapter.rb +1 -1
  9. data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
  10. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +4 -0
  11. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +1 -1
  12. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +5 -3
  13. data/lib/webmock/http_lib_adapters/net_http.rb +2 -2
  14. data/lib/webmock/http_lib_adapters/patron_adapter.rb +2 -2
  15. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  16. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  17. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  18. data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
  19. data/lib/webmock/rack_response.rb +1 -1
  20. data/lib/webmock/request_execution_verifier.rb +2 -3
  21. data/lib/webmock/request_pattern.rb +9 -2
  22. data/lib/webmock/request_registry.rb +1 -1
  23. data/lib/webmock/request_signature.rb +1 -1
  24. data/lib/webmock/request_signature_snippet.rb +4 -4
  25. data/lib/webmock/stub_request_snippet.rb +2 -2
  26. data/lib/webmock/util/headers.rb +2 -2
  27. data/lib/webmock/util/query_mapper.rb +3 -3
  28. data/lib/webmock/version.rb +1 -1
  29. data/minitest/webmock_spec.rb +2 -2
  30. data/spec/acceptance/http_rb/http_rb_spec.rb +9 -0
  31. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +1 -1
  32. data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
  33. data/spec/acceptance/net_http/net_http_shared.rb +1 -1
  34. data/spec/acceptance/net_http/net_http_spec.rb +15 -1
  35. data/spec/acceptance/patron/patron_spec_helper.rb +1 -1
  36. data/spec/acceptance/shared/request_expectations.rb +7 -0
  37. data/spec/acceptance/shared/stubbing_requests.rb +5 -0
  38. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
  39. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
  40. data/spec/unit/api_spec.rb +84 -3
  41. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  42. data/spec/unit/request_execution_verifier_spec.rb +12 -12
  43. data/test/shared_test.rb +15 -2
  44. metadata +44 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b25e1f8ad8f3a8ffa79b1e54d0709069f9162b52
4
- data.tar.gz: 8169ef32ad67fecba074d929fb6d584410950617
3
+ metadata.gz: 1ba7b559bad31fd1150447c4895e4517a96e5c0b
4
+ data.tar.gz: 6682c3a0689f7aaf06b11cef43e170d584694187
5
5
  SHA512:
6
- metadata.gz: 3daea98f13930398bd12722ff33b727c4c3b02ce41f2aeb3060d437d246ed5284c0737979bea0b7a46fa6dba6c24e90b1e4519d9b7f4e6d8d9e8d212869d9469
7
- data.tar.gz: 8f9fdee207513b9741f6245ed4b575120bd3232165edfa941a2f8113b11a2413f55945a5e5ce160cfb12da455545282b9e3f766903940fe7b878f49b5b59dbb9
6
+ metadata.gz: f77249e99b5553be7f9afea5de6112c7e63526c4c6ca42124bfa5089061b64eb469d927f903bf37d54245213b66d2217ee8112863848108dc262d995ca64c1d3
7
+ data.tar.gz: 779df5762aa1d72513d4cae0221b44e1dc92dee9b698db5ae6daf92fe12c29fb29d88af04968baabefe186ca9d51f1c564da89aa4c1e53fb31f55ead0f72d008
@@ -1,14 +1,14 @@
1
+ before_install:
2
+ - gem update bundler
3
+
1
4
  rvm:
2
- - 2.0.0
3
- - 2.1.0
4
- - 2.2.1
5
- - 2.2.3
6
- - 2.3.0
7
- - 2.4.0
5
+ - 2.2.8
6
+ - 2.3.5
7
+ - 2.4.2
8
8
  - rbx-2
9
9
  - ruby-head
10
10
  - jruby-9.0.5.0
11
- - jruby-9.1.5.0
11
+ - jruby-9.1.13.0
12
12
  - jruby-head
13
13
  matrix:
14
14
  allow_failures:
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.0
4
+
5
+ * http.rb 3.0.0 compatibility
6
+
7
+ Thanks to [Piotr Boniecki](https://github.com/Bonias)
8
+
9
+ * Typhoeus 1.3.0 support
10
+
11
+ Thanks to [NARUSE, Yui](https://github.com/nurse)
12
+
13
+ * Added support for matching partial query params using hash_excluding
14
+
15
+ stub_request(:get, "www.example.com").
16
+ with(query: hash_excluding({"a" => "b"}))
17
+
18
+ RestClient.get("http://www.example.com/?a=b") # ===> Failure
19
+ RestClient.get("http://www.example.com/?a=c") # ===> Success
20
+
21
+ Thanks to [Olexandr Hoshylyk](https://github.com/Warrior109)
22
+
23
+ * Added MRI 2.3+ frozen string literal compatibility
24
+
25
+ Thanks to [Pat Allan](https://github.com/pat)
26
+
27
+ * Ensured that HTTPClient adapter does not yield block on empty response body if a block is provided
28
+
29
+ Thanks to [NARUSE, Yui](https://github.com/nurse)
30
+
31
+ * Fixed issue with `to_timeout` incorrectly raising `HTTP::ConnectionError` instead of `HTTP::TimeoutError` when using http.rb
32
+
33
+ Thanks to [Rick Song](https://github.com/RickCSong)
34
+
35
+ * Fixed problem with `response.connection.close` method being undefined when using http.rb
36
+
37
+ Thanks to [Janko Marohnić](https://github.com/janko-m)
38
+
39
+ * Fixed problem with matching Net::HTTP request header values assigned as numbers.
40
+
41
+ Thanks to [Felipe Constantino de Oliveira](https://github.com/felipecdo) for reporting the issue.
42
+
43
+ * Fixed problem with Net::HTTP adapter converting empty response body to nil for non 204 responses.
44
+
45
+ Thanks to [Jeffrey Charles](https://github.com/jeffcharles) for reporting the issue.
46
+
47
+
3
48
  ## 3.0.1
4
49
 
5
50
  * Suppressed \`warning: \`&' interpreted as argument prefix\`
data/README.md CHANGED
@@ -32,8 +32,6 @@ Supported HTTP libraries
32
32
  Supported Ruby Interpreters
33
33
  ---------------------------
34
34
 
35
- * MRI 2.0
36
- * MRI 2.1
37
35
  * MRI 2.2
38
36
  * MRI 2.3
39
37
  * MRI 2.4
@@ -273,6 +271,16 @@ stub_request(:get, "www.example.com").
273
271
  RestClient.get("http://www.example.com/?a[]=b&a[]=c&x=1") # ===> Success
274
272
  ```
275
273
 
274
+ ### Matching partial query params using hash_excluding
275
+
276
+ ```ruby
277
+ stub_request(:get, "www.example.com").
278
+ with(query: hash_excluding({"a" => "b"}))
279
+
280
+ RestClient.get("http://www.example.com/?a=b") # ===> Failure
281
+ RestClient.get("http://www.example.com/?a=c") # ===> Success
282
+ ```
283
+
276
284
  ### Stubbing with custom response
277
285
 
278
286
  ```ruby
@@ -1035,6 +1043,12 @@ People who submitted patches and new features or suggested improvements. Many th
1035
1043
  * George Ulmer
1036
1044
  * Christof Koenig
1037
1045
  * Chung-Yi Chi
1046
+ * Olexandr Hoshylyk
1047
+ * Janko Marohnić
1048
+ * Pat Allan
1049
+ * Rick Song
1050
+ * NARUSE, Yui
1051
+ * Piotr Boniecki
1038
1052
 
1039
1053
  For a full list of contributors you can visit the
1040
1054
  [contributors](https://github.com/bblimke/webmock/contributors) page.
@@ -18,7 +18,10 @@ require 'webmock/util/json'
18
18
  require 'webmock/util/version_checker'
19
19
  require 'webmock/util/hash_validator'
20
20
 
21
+ require 'webmock/matchers/hash_argument_matcher'
22
+ require 'webmock/matchers/hash_excluding_matcher'
21
23
  require 'webmock/matchers/hash_including_matcher'
24
+ require 'webmock/matchers/any_arg_matcher'
22
25
 
23
26
  require 'webmock/request_pattern'
24
27
  require 'webmock/request_signature'
@@ -54,6 +54,14 @@ module WebMock
54
54
  end
55
55
  end
56
56
 
57
+ def hash_excluding(*args)
58
+ if defined?(super)
59
+ super
60
+ else
61
+ WebMock::Matchers::HashExcludingMatcher.new(anythingize_lonely_keys(*args))
62
+ end
63
+ end
64
+
57
65
  def remove_request_stub(stub)
58
66
  WebMock::StubRegistry.instance.remove_request_stub(stub)
59
67
  end
@@ -153,7 +153,7 @@ if defined?(Curl)
153
153
  @body_str = webmock_response.body
154
154
  @response_code = webmock_response.status[0]
155
155
 
156
- @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n"
156
+ @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n".dup
157
157
 
158
158
  @on_debug.call(@header_str, 1) if defined?( @on_debug )
159
159
 
@@ -92,7 +92,7 @@ if defined?(Excon)
92
92
  end
93
93
 
94
94
  def self.to_query(hash)
95
- string = ""
95
+ string = "".dup
96
96
  for key, values in hash
97
97
  if values.nil?
98
98
  string << key.to_s << '&'
@@ -1,9 +1,15 @@
1
1
  module HTTP
2
2
  class Request
3
3
  def webmock_signature
4
+ request_body = if defined?(HTTP::Request::Body)
5
+ ''.tap { |string| body.each { |part| string << part } }
6
+ else
7
+ body
8
+ end
9
+
4
10
  ::WebMock::RequestSignature.new(verb, uri.to_s, {
5
11
  headers: headers.to_h,
6
- body: body
12
+ body: request_body
7
13
  })
8
14
  end
9
15
  end
@@ -17,6 +17,10 @@ module HTTP
17
17
  @io.read size
18
18
  end
19
19
 
20
+ def close
21
+ @io.close
22
+ end
23
+
20
24
  def sequence_id
21
25
  -1
22
26
  end
@@ -43,7 +43,7 @@ module HTTP
43
43
 
44
44
  def raise_timeout_error
45
45
  raise Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
46
- raise HTTP::ConnectionError, "connection error: #{Errno::ETIMEDOUT.new}"
46
+ raise HTTP::TimeoutError, "connection error: #{Errno::ETIMEDOUT.new}"
47
47
  end
48
48
 
49
49
  def perform
@@ -75,8 +75,10 @@ if defined?(::HTTPClient)
75
75
  elsif block
76
76
  body = ''
77
77
  do_get_block_without_webmock(req, proxy, conn) do |http_res, chunk|
78
- body += chunk
79
- block.call(http_res, chunk)
78
+ if chunk && chunk.bytesize > 0
79
+ body += chunk
80
+ block.call(http_res, chunk)
81
+ end
80
82
  end
81
83
  else
82
84
  do_get_block_without_webmock(req, proxy, conn)
@@ -117,7 +119,7 @@ if defined?(::HTTPClient)
117
119
  raise HTTPClient::TimeoutError if webmock_response.should_timeout
118
120
  webmock_response.raise_error_if_any
119
121
 
120
- block.call(response, body) if block
122
+ block.call(response, body) if block && body && body.bytesize > 0
121
123
 
122
124
  response
123
125
  end
@@ -154,7 +154,7 @@ module WebMock
154
154
  def build_net_http_response(webmock_response, &block)
155
155
  response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
156
156
  body = webmock_response.body
157
- body = nil if body.to_s == ''
157
+ body = nil if webmock_response.status[0].to_s == '204'
158
158
 
159
159
  response.instance_variable_set(:@body, body)
160
160
  webmock_response.headers.to_a.each do |name, values|
@@ -258,7 +258,7 @@ module Net #:nodoc: all
258
258
  class WebMockNetBufferedIO < BufferedIO
259
259
  def initialize(io, read_timeout: 60, continue_timeout: nil, debug_output: nil)
260
260
  @read_timeout = read_timeout
261
- @rbuf = ''
261
+ @rbuf = ''.dup
262
262
  @debug_output = debug_output
263
263
 
264
264
  @io = case io
@@ -106,11 +106,11 @@ if defined?(::Patron)
106
106
  header_data = ([status_line] + header_fields).join("\r\n")
107
107
 
108
108
  ::Patron::Response.new(
109
- "",
109
+ "".dup,
110
110
  webmock_response.status[0],
111
111
  0,
112
112
  header_data,
113
- webmock_response.body,
113
+ webmock_response.body.dup,
114
114
  default_response_charset
115
115
  )
116
116
  end
@@ -0,0 +1,13 @@
1
+ module WebMock
2
+ module Matchers
3
+ # this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
4
+ class AnyArgMatcher
5
+ def initialize(ignore)
6
+ end
7
+
8
+ def ==(other)
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module WebMock
2
+ module Matchers
3
+ # Base class for Hash matchers
4
+ # https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
5
+ class HashArgumentMatcher
6
+ def initialize(expected)
7
+ @expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected, deep: true).sort]
8
+ end
9
+
10
+ def ==(_actual, &block)
11
+ @expected.all?(&block)
12
+ rescue NoMethodError
13
+ false
14
+ end
15
+
16
+ def self.from_rspec_matcher(matcher)
17
+ new(matcher.instance_variable_get(:@expected))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module WebMock
2
+ module Matchers
3
+ # this is a based on RSpec::Mocks::ArgumentMatchers::HashExcludingMatcher
4
+ # https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
5
+ class HashExcludingMatcher < HashArgumentMatcher
6
+ def ==(actual)
7
+ super { |key, value| !actual.key?(key) || value != actual[key] }
8
+ end
9
+
10
+ def inspect
11
+ "hash_excluding(#{@expected.inspect})"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,14 +1,10 @@
1
1
  module WebMock
2
2
  module Matchers
3
- #this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
4
- #https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
5
- class HashIncludingMatcher
6
- def initialize(expected)
7
- @expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected, deep: true).sort]
8
- end
9
-
3
+ # this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
4
+ # https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
5
+ class HashIncludingMatcher < HashArgumentMatcher
10
6
  def ==(actual)
11
- @expected.all? {|k,v| actual.has_key?(k) && v === actual[k]}
7
+ super { |key, value| actual.key?(key) && value === actual[key] }
12
8
  rescue NoMethodError
13
9
  false
14
10
  end
@@ -16,21 +12,6 @@ module WebMock
16
12
  def inspect
17
13
  "hash_including(#{@expected.inspect})"
18
14
  end
19
-
20
- def self.from_rspec_matcher(matcher)
21
- new(matcher.instance_variable_get(:@expected))
22
- end
23
- end
24
-
25
- #this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
26
- class AnyArgMatcher
27
- def initialize(ignore)
28
- end
29
-
30
- def ==(other)
31
- true
32
- end
33
15
  end
34
-
35
16
  end
36
17
  end
@@ -17,7 +17,7 @@ module WebMock
17
17
  end
18
18
 
19
19
  def body_from_rack_response(response)
20
- body = ""
20
+ body = "".dup
21
21
  response.each { |line| body << line }
22
22
  response.close if response.respond_to?(:close)
23
23
  return body
@@ -53,9 +53,8 @@ module WebMock
53
53
 
54
54
  def failure_message_phrase(is_negated=false)
55
55
  negation = is_negated ? "was not" : "was"
56
- text = "The request #{request_pattern} #{negation} expected to execute #{quantity_phrase(is_negated)}but it executed #{times(times_executed)}"
57
- text << self.class.executed_requests_message
58
- text
56
+ "The request #{request_pattern} #{negation} expected to execute #{quantity_phrase(is_negated)}but it executed #{times(times_executed)}" +
57
+ self.class.executed_requests_message
59
58
  end
60
59
 
61
60
  def quantity_phrase(is_negated=false)
@@ -4,6 +4,10 @@ module WebMock
4
4
  def rSpecHashIncludingMatcher?(matcher)
5
5
  matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashIncludingMatcher/
6
6
  end
7
+
8
+ def rSpecHashExcludingMatcher?(matcher)
9
+ matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashExcludingMatcher/
10
+ end
7
11
  end
8
12
 
9
13
  class RequestPattern
@@ -37,7 +41,7 @@ module WebMock
37
41
  end
38
42
 
39
43
  def to_s
40
- string = "#{@method_pattern.to_s.upcase}"
44
+ string = "#{@method_pattern.to_s.upcase}".dup
41
45
  string << " #{@uri_pattern.to_s}"
42
46
  string << " with body #{@body_pattern.to_s}" if @body_pattern
43
47
  string << " with headers #{@headers_pattern.to_s}" if @headers_pattern
@@ -115,10 +119,13 @@ module WebMock
115
119
  def add_query_params(query_params)
116
120
  @query_params = if query_params.is_a?(Hash)
117
121
  query_params
118
- elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher)
122
+ elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher) \
123
+ || query_params.is_a?(WebMock::Matchers::HashExcludingMatcher)
119
124
  query_params
120
125
  elsif rSpecHashIncludingMatcher?(query_params)
121
126
  WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params)
127
+ elsif rSpecHashExcludingMatcher?(query_params)
128
+ WebMock::Matchers::HashExcludingMatcher.from_rspec_matcher(query_params)
122
129
  else
123
130
  WebMock::Util::QueryMapper.query_to_values(query_params, notation: Config.instance.query_values_notation)
124
131
  end
@@ -23,7 +23,7 @@ module WebMock
23
23
  if requested_signatures.hash.empty?
24
24
  "No requests were made."
25
25
  else
26
- text = ""
26
+ text = "".dup
27
27
  self.requested_signatures.each do |request_signature, times_executed|
28
28
  text << "#{request_signature} was made #{times_executed} time#{times_executed == 1 ? '' : 's' }\n"
29
29
  end
@@ -12,7 +12,7 @@ module WebMock
12
12
  end
13
13
 
14
14
  def to_s
15
- string = "#{self.method.to_s.upcase}"
15
+ string = "#{self.method.to_s.upcase}".dup
16
16
  string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}"
17
17
  string << " with body '#{body.to_s}'" if body && body.to_s != ''
18
18
  if headers && !headers.empty?
@@ -13,14 +13,14 @@ module WebMock
13
13
  def stubbing_instructions
14
14
  return unless WebMock.show_stubbing_instructions?
15
15
 
16
- text = "You can stub this request with the following snippet:\n\n"
17
- text << WebMock::StubRequestSnippet.new(request_stub).to_s
16
+ "You can stub this request with the following snippet:\n\n" +
17
+ WebMock::StubRequestSnippet.new(request_stub).to_s
18
18
  end
19
19
 
20
20
  def request_stubs
21
21
  return if WebMock::StubRegistry.instance.request_stubs.empty?
22
22
 
23
- text = "registered request stubs:\n"
23
+ text = "registered request stubs:\n".dup
24
24
  WebMock::StubRegistry.instance.request_stubs.each do |stub|
25
25
  text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
26
26
  add_body_diff(stub, text) if WebMock.show_body_diff?
@@ -50,7 +50,7 @@ module WebMock
50
50
  end
51
51
 
52
52
  def pretty_print_to_string(string_to_print)
53
- StringIO.open("") do |stream|
53
+ StringIO.open("".dup) do |stream|
54
54
  PP.pp(string_to_print, stream)
55
55
  stream.rewind
56
56
  stream.read