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
data/.travis.yml CHANGED
@@ -5,14 +5,22 @@ rvm:
5
5
  - 2.0.0
6
6
  - 2.1.0
7
7
  - 2.2.1
8
+ - 2.2.3
9
+ - rbx-2
8
10
  - ruby-head
9
11
  - ree
12
+ - jruby-9.0.0.0
13
+ - jruby-9.0.1.0
10
14
  - jruby-18mode
11
15
  - jruby-19mode
12
16
  - jruby
13
17
  - jruby-head
14
18
  matrix:
15
19
  allow_failures:
20
+ - rvm: jruby-9.0.0.0
21
+ - rvm: jruby-9.0.1.0
16
22
  - rvm: jruby-18mode
17
23
  - rvm: jruby-head
18
24
  - rvm: ruby-head
25
+ - rvm: rbx-2
26
+ sudo: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.1
4
+
5
+ * Adds Rack as a development dependency and removes require rack/utils in main lib.
6
+
7
+ Thanks to [Keenan Brock](https://github.com/kbrock)
8
+
9
+ ## 1.22.0
10
+
11
+ All the credit for preparing this release go to [David Begin](https://github.com/davidbegin)!
12
+
13
+ * Adds [Manticore](https://github.com/cheald/manticore) support.
14
+
15
+ Thanks to [Mike Knepper](https://github.com/mikeknep), [David Abdemoulaie](https://github.com/hobodave)
16
+
17
+ * Update to Show a hash diff for requests that have stubs with a body.
18
+
19
+ Thanks to [yurivm](https://github.com/yurivm)
20
+
21
+ * Update to mirror Net::HTTP handling of headers as symbols
22
+
23
+ * Update to ignore non-comparable-values error when sorting
24
+ query values, because sorting is just a convience.
25
+
26
+ Thanks to [Magne Land](https://github.com/magneland)
27
+
28
+ * Covert Boolean values to Strings when using them to define
29
+ the body of a request.
30
+
31
+ Thanks to [Krzysztof Rygielski](https://github.com/riggy)
32
+
33
+ * Fixes WebMock's parsing Multibyte characters
34
+
35
+ Thanks to [Zhao Wen](https://github.com/VincentZhao)
36
+
37
+ * Updates to be compatible with httpclient 2.6.0
38
+
39
+ * Converts keys from symbols to strings when for QueryMapper.to_query
40
+
41
+ Thanks to [Ramon Tayag](https://github.com/ramontayag)
42
+
43
+ * Restricts http.rb version to 0.7.3 for Ruby 1.8.7
44
+
45
+ * Fixes issue emulating em-http-request's handling of multiple requests.
46
+
47
+ Thanks to [Matt Palmer](https://github.com/mpalmer)
48
+
49
+ * WebMock requires only the necessary parts of crack to avoid pulling in safe_yaml
50
+
51
+ Thanks to [Johannes Schlumberger](https://github.com/spjsschl)
52
+
3
53
  ## 1.21.0
4
54
 
5
55
  * Support for http.rb >= 0.8.0
@@ -10,7 +60,7 @@
10
60
 
11
61
  Thanks to [Mattias Putman](https://github.com/challengee)
12
62
 
13
- * Added support for RSpec3-like `and_return`, `and_raise`, `and_timeout` sytnax.
63
+ * Added support for RSpec3-like `and_return`, `and_raise`, `and_timeout` syntax.
14
64
 
15
65
  Thanks to [Franky Wahl](https://github.com/frankywahl)
16
66
 
data/Gemfile CHANGED
@@ -10,7 +10,6 @@ group :development do
10
10
  end
11
11
 
12
12
  group :test do
13
- gem 'rack'
14
13
  gem 'minitest_tu_shim', '1.3.2'
15
14
  end
16
15
 
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
- WebMock [![Build Status](https://secure.travis-ci.org/bblimke/webmock.png?branch=master)](http://travis-ci.org/bblimke/webmock) [![Dependency Status](https://gemnasium.com/bblimke/webmock.png)](http://gemnasium.com/bblimke/webmock)
1
+ WebMock
2
2
  =======
3
+ [![Gem Version](https://badge.fury.io/rb/webmock.svg)](http://badge.fury.io/rb/webmock) [![Build Status](https://secure.travis-ci.org/bblimke/webmock.svg?branch=master)](http://travis-ci.org/bblimke/webmock) [![Dependency Status](https://gemnasium.com/bblimke/webmock.svg)](http://gemnasium.com/bblimke/webmock) [![Code Climate](https://codeclimate.com/github/bblimke/webmock/badges/gpa.svg)](https://codeclimate.com/github/bblimke/webmock) [![Inline docs](http://inch-ci.org/github/bblimke/webmock.svg?branch=master)](http://inch-ci.org/github/bblimke/webmock)
3
4
 
4
5
  Library for stubbing and setting expectations on HTTP requests in Ruby.
5
6
 
@@ -26,6 +27,7 @@ Supported HTTP libraries
26
27
  * Typhoeus (currently only Typhoeus::Hydra)
27
28
  * Excon
28
29
  * HTTP Gem
30
+ * Manticore
29
31
 
30
32
  Supported Ruby Interpreters
31
33
  ---------------------------
@@ -736,13 +738,13 @@ If you use any of the WebMock methods for matching query params, then Addressabl
736
738
  WebMock will match request headers against stubbed request headers in the following situations:
737
739
 
738
740
  1. Stubbed request has headers specified and request headers are the same as stubbed headers <br/>
739
- i.e stubbed headers: `{ 'Header1' => 'Value1', 'Header1' => 'Value1' }`, requested: `{ 'Header1' => 'Value1', 'Header1' => 'Value1' }`
741
+ i.e stubbed headers: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`
740
742
 
741
743
  2. Stubbed request has headers specified and stubbed request headers are a subset of request headers <br/>
742
- i.e stubbed headers: `{ 'Header1' => 'Value1' }`, requested: `{ 'Header1' => 'Value1', 'Header1' => 'Value1' }`
744
+ i.e stubbed headers: `{ 'Header1' => 'Value1' }`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`
743
745
 
744
746
  3. Stubbed request has no headers <br/>
745
- i.e stubbed headers: `nil`, requested: `{ 'Header1' => 'Value1', 'Header1' => 'Value1' }`
747
+ i.e stubbed headers: `nil`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`
746
748
 
747
749
  WebMock normalises headers and treats all forms of same headers as equal:
748
750
  i.e the following two sets of headers are equal:
@@ -827,7 +829,7 @@ all version with just one command.
827
829
 
828
830
  This command is wrapped up in to a rake task and can be invoked like so:
829
831
 
830
- rake spec:rubies
832
+ rake rvm:specs
831
833
 
832
834
  ## Credits
833
835
 
@@ -972,7 +974,16 @@ People who submitted patches and new features or suggested improvements. Many th
972
974
  * Steve Mitchell
973
975
  * Mattias Putman
974
976
  * Zachary Anker
975
-
977
+ * Emmanuel Sambo
978
+ * Ramon Tayag
979
+ * Johannes Schlumberger
980
+ * Siôn Le Roux
981
+ * Matt Palmer
982
+ * Zhao Wen
983
+ * Krzysztof Rygielski
984
+ * Magne Land
985
+ * yurivm
986
+ * Mike Knepper
976
987
 
977
988
  For a full list of contributors you can visit the
978
989
  [contributors](https://github.com/bblimke/webmock/contributors) page.
data/lib/webmock.rb CHANGED
@@ -2,7 +2,7 @@ require 'singleton'
2
2
 
3
3
  require 'addressable/uri'
4
4
  require 'addressable/template'
5
- require 'crack'
5
+ require 'crack/xml'
6
6
 
7
7
  require 'webmock/deprecation'
8
8
  require 'webmock/version'
@@ -28,6 +28,8 @@ require 'webmock/response'
28
28
  require 'webmock/rack_response'
29
29
 
30
30
  require 'webmock/stub_request_snippet'
31
+ require 'webmock/request_signature_snippet'
32
+ require 'webmock/request_body_diff'
31
33
 
32
34
  require 'webmock/assertion_failure'
33
35
  require 'webmock/request_execution_verifier'
@@ -47,6 +49,7 @@ require 'webmock/http_lib_adapters/curb_adapter'
47
49
  require 'webmock/http_lib_adapters/em_http_request_adapter'
48
50
  require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
49
51
  require 'webmock/http_lib_adapters/excon_adapter'
52
+ require 'webmock/http_lib_adapters/manticore_adapter'
50
53
 
51
54
  require 'webmock/webmock'
52
55
 
@@ -4,6 +4,7 @@ module WebMock
4
4
 
5
5
  def initialize
6
6
  @show_stubbing_instructions = true
7
+ @show_body_diff = true
7
8
  end
8
9
 
9
10
  attr_accessor :allow_net_connect
@@ -12,5 +13,6 @@ module WebMock
12
13
  attr_accessor :net_http_connect_on_start
13
14
  attr_accessor :show_stubbing_instructions
14
15
  attr_accessor :query_values_notation
16
+ attr_accessor :show_body_diff
15
17
  end
16
18
  end
@@ -2,34 +2,16 @@ module WebMock
2
2
 
3
3
  class NetConnectNotAllowedError < Exception
4
4
  def initialize(request_signature)
5
+ request_signature_snippet = RequestSignatureSnippet.new(request_signature)
5
6
  text = [
6
7
  "Real HTTP connections are disabled. Unregistered request: #{request_signature}",
7
- stubbing_instructions(request_signature),
8
- request_stubs,
8
+ request_signature_snippet.stubbing_instructions,
9
+ request_signature_snippet.request_stubs,
9
10
  "="*60
10
11
  ].compact.join("\n\n")
11
12
  super(text)
12
13
  end
13
14
 
14
- private
15
-
16
- def request_stubs
17
- return if WebMock::StubRegistry.instance.request_stubs.empty?
18
- text = "registered request stubs:\n"
19
- WebMock::StubRegistry.instance.request_stubs.each do |stub|
20
- text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
21
- end
22
- text
23
- end
24
-
25
- def stubbing_instructions(request_signature)
26
- return unless WebMock.show_stubbing_instructions?
27
- text = ""
28
- request_stub = RequestStub.from_request_signature(request_signature)
29
- text << "You can stub this request with the following snippet:\n\n"
30
- text << WebMock::StubRequestSnippet.new(request_stub).to_s
31
- text
32
- end
33
15
  end
34
16
 
35
17
  end
@@ -70,6 +70,10 @@ if defined?(EventMachine::HttpClient)
70
70
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
71
71
  end
72
72
  end
73
+
74
+ def drop_client
75
+ @clients.shift
76
+ end
73
77
  end
74
78
 
75
79
  class WebMockHttpClient < EventMachine::HttpClient
@@ -83,6 +87,7 @@ if defined?(EventMachine::HttpClient)
83
87
  @last_effective_url = @uri = uri
84
88
  if error
85
89
  on_error(error)
90
+ @conn.drop_client
86
91
  fail(self)
87
92
  else
88
93
  @conn.receive_data(response)
@@ -0,0 +1,123 @@
1
+ begin
2
+ require 'manticore'
3
+ rescue LoadError
4
+ # manticore not found
5
+ end
6
+
7
+ if defined?(Manticore)
8
+ module WebMock
9
+ module HttpLibAdapters
10
+ class ManticoreAdapter < HttpLibAdapter
11
+ adapter_for :manticore
12
+
13
+ OriginalManticoreClient = Manticore::Client
14
+
15
+ def self.enable!
16
+ Manticore.send(:remove_const, :Client)
17
+ Manticore.send(:const_set, :Client, WebMockManticoreClient)
18
+ Manticore.instance_variable_set(:@manticore_facade, WebMockManticoreClient.new)
19
+ end
20
+
21
+ def self.disable!
22
+ Manticore.send(:remove_const, :Client)
23
+ Manticore.send(:const_set, :Client, OriginalManticoreClient)
24
+ Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new)
25
+ end
26
+
27
+ class WebMockManticoreClient < Manticore::Client
28
+ def request(klass, url, options={}, &block)
29
+ super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options))
30
+ end
31
+
32
+ private
33
+
34
+ def format_options(options)
35
+ return options unless headers = options[:headers]
36
+
37
+ options.merge(:headers => join_array_values(headers))
38
+ end
39
+
40
+ def join_array_values(headers)
41
+ headers.reduce({}) do |h, (k,v)|
42
+ v = v.join(', ') if v.is_a?(Array)
43
+ h.merge(k => v)
44
+ end
45
+ end
46
+
47
+ def response_object_for(request, context, &block)
48
+ request_signature = generate_webmock_request_signature(request)
49
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
50
+
51
+ if webmock_response = registered_response_for(request_signature)
52
+ webmock_response.raise_error_if_any
53
+ manticore_response = generate_manticore_response(webmock_response).call
54
+ real_request = false
55
+
56
+ elsif real_request_allowed?(request_signature.uri)
57
+ manticore_response = Manticore::Response.new(self, request, context, &block).call
58
+ webmock_response = generate_webmock_response(manticore_response)
59
+ real_request = true
60
+
61
+ else
62
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
63
+ end
64
+
65
+ WebMock::CallbackRegistry.invoke_callbacks({:lib => :manticore, :real_request => real_request}, request_signature, webmock_response)
66
+ manticore_response
67
+ end
68
+
69
+ def registered_response_for(request_signature)
70
+ WebMock::StubRegistry.instance.response_for_request(request_signature)
71
+ end
72
+
73
+ def real_request_allowed?(uri)
74
+ WebMock.net_connect_allowed?(uri)
75
+ end
76
+
77
+ def generate_webmock_request_signature(request)
78
+ method = request.method.downcase
79
+ uri = request.uri.to_s
80
+ body = read_body(request)
81
+ headers = split_array_values(request.headers)
82
+
83
+ WebMock::RequestSignature.new(method, uri, {:body => body, :headers => headers})
84
+ end
85
+
86
+ def read_body(request)
87
+ if request.respond_to?(:entity) && !request.entity.nil?
88
+ Manticore::EntityConverter.new.read_entity(request.entity)
89
+ end
90
+ end
91
+
92
+ def split_array_values(headers = [])
93
+ headers.each_with_object({}) do |(k, v), h|
94
+ h[k] = case v
95
+ when /,/ then v.split(',').map(&:strip)
96
+ else v
97
+ end
98
+ end
99
+ end
100
+
101
+ def generate_manticore_response(webmock_response)
102
+ raise Manticore::ConnectTimeout if webmock_response.should_timeout
103
+
104
+ Manticore::StubbedResponse.stub(
105
+ :code => webmock_response.status[0],
106
+ :body => webmock_response.body,
107
+ :headers => webmock_response.headers,
108
+ :cookies => {}
109
+ )
110
+ end
111
+
112
+ def generate_webmock_response(manticore_response)
113
+ webmock_response = WebMock::Response.new
114
+ webmock_response.status = [manticore_response.code, manticore_response.message]
115
+ webmock_response.body = manticore_response.body
116
+ webmock_response.headers = manticore_response.headers
117
+ webmock_response
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -272,10 +272,9 @@ module WebMock
272
272
  method = request.method.downcase.to_sym
273
273
 
274
274
  headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
275
-
275
+ validate_headers(headers)
276
276
  headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v.first =~ /^Basic / } #we added it to url userinfo
277
277
 
278
-
279
278
  if request.body_stream
280
279
  body = request.body_stream.read
281
280
  request.body_stream = nil
@@ -290,6 +289,21 @@ module WebMock
290
289
  WebMock::RequestSignature.new(method, uri, :body => request.body, :headers => headers)
291
290
  end
292
291
 
292
+ def self.validate_headers(headers)
293
+ # If you make a request with headers that are symbols Net::HTTP raises a NoMethodError
294
+ #
295
+ # WebMock normalizes headers when creating a RequestSignature,
296
+ # and will update all headers from symbols to strings.
297
+ #
298
+ # This could create a false positive in a test suite with WebMock.
299
+ #
300
+ # So before this point, WebMock raises an ArgumentError if any of the headers are symbols
301
+ # instead of the cryptic NoMethodError "undefined method `split' ...` from Net::HTTP
302
+ header_as_symbol = headers.keys.find {|header| header.is_a? Symbol}
303
+ if header_as_symbol
304
+ raise ArgumentError.new("Net:HTTP does not accept headers as symbols")
305
+ end
306
+ end
293
307
 
294
308
  def self.check_right_http_connection
295
309
  @was_right_http_connection_loaded = defined?(RightHttpConnection)
@@ -0,0 +1,63 @@
1
+ require "hashdiff"
2
+ require "json"
3
+
4
+ module WebMock
5
+ class RequestBodyDiff
6
+
7
+ def initialize(request_signature, request_stub)
8
+ @request_signature = request_signature
9
+ @request_stub = request_stub
10
+ end
11
+
12
+ def body_diff
13
+ return {} unless request_signature_diffable? && request_stub_diffable?
14
+
15
+ HashDiff.diff(request_signature_body_hash, request_stub_body_hash)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :request_signature, :request_stub
21
+
22
+ def request_signature_diffable?
23
+ request_signature.json_headers? && request_signature_parseable_json?
24
+ end
25
+
26
+ def request_stub_diffable?
27
+ request_stub_body.is_a?(Hash) || request_stub_parseable_json?
28
+ end
29
+
30
+ def request_signature_body_hash
31
+ JSON.parse(request_signature.body)
32
+ end
33
+
34
+ def request_stub_body_hash
35
+ return request_stub_body if request_stub_body.is_a?(Hash)
36
+
37
+ JSON.parse(request_stub_body)
38
+ end
39
+
40
+ def request_stub_body
41
+ request_stub.request_pattern &&
42
+ request_stub.request_pattern.body_pattern &&
43
+ request_stub.request_pattern.body_pattern.pattern
44
+ end
45
+
46
+ def request_signature_parseable_json?
47
+ parseable_json?(request_signature.body)
48
+ end
49
+
50
+ def request_stub_parseable_json?
51
+ parseable_json?(request_stub_body)
52
+ end
53
+
54
+ def parseable_json?(body_pattern)
55
+ return false unless body_pattern.is_a?(String)
56
+
57
+ JSON.parse(body_pattern)
58
+ true
59
+ rescue JSON::ParserError
60
+ false
61
+ end
62
+ end
63
+ end