webmock 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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