webmock 1.8.6 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/CI.yml +37 -0
  3. data/.gitignore +6 -0
  4. data/CHANGELOG.md +1198 -0
  5. data/Gemfile +3 -15
  6. data/README.md +761 -305
  7. data/Rakefile +13 -40
  8. data/lib/webmock/api.rb +63 -17
  9. data/lib/webmock/callback_registry.rb +1 -1
  10. data/lib/webmock/config.rb +8 -0
  11. data/lib/webmock/cucumber.rb +2 -0
  12. data/lib/webmock/errors.rb +8 -24
  13. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  14. data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
  15. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
  16. data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
  29. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
  33. data/lib/webmock/minitest.rb +29 -3
  34. data/lib/webmock/rack_response.rb +14 -7
  35. data/lib/webmock/request_body_diff.rb +64 -0
  36. data/lib/webmock/request_execution_verifier.rb +38 -17
  37. data/lib/webmock/request_pattern.rb +158 -38
  38. data/lib/webmock/request_registry.rb +3 -3
  39. data/lib/webmock/request_signature.rb +7 -3
  40. data/lib/webmock/request_signature_snippet.rb +61 -0
  41. data/lib/webmock/request_stub.rb +9 -6
  42. data/lib/webmock/response.rb +30 -15
  43. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
  44. data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
  45. data/lib/webmock/rspec/matchers.rb +0 -1
  46. data/lib/webmock/rspec.rb +11 -2
  47. data/lib/webmock/stub_registry.rb +31 -10
  48. data/lib/webmock/stub_request_snippet.rb +14 -6
  49. data/lib/webmock/test_unit.rb +4 -4
  50. data/lib/webmock/util/hash_counter.rb +20 -6
  51. data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
  52. data/lib/webmock/util/hash_validator.rb +17 -0
  53. data/lib/webmock/util/headers.rb +23 -2
  54. data/lib/webmock/util/json.rb +20 -7
  55. data/lib/webmock/util/query_mapper.rb +281 -0
  56. data/lib/webmock/util/uri.rb +29 -19
  57. data/lib/webmock/util/values_stringifier.rb +20 -0
  58. data/lib/webmock/util/version_checker.rb +40 -2
  59. data/lib/webmock/version.rb +1 -1
  60. data/lib/webmock/webmock.rb +56 -17
  61. data/lib/webmock.rb +56 -46
  62. data/minitest/test_helper.rb +8 -3
  63. data/minitest/test_webmock.rb +4 -1
  64. data/minitest/webmock_spec.rb +16 -6
  65. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  66. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  67. data/spec/acceptance/curb/curb_spec.rb +227 -68
  68. data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
  69. data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
  70. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
  71. data/spec/acceptance/excon/excon_spec.rb +66 -4
  72. data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
  73. data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
  74. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  75. data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
  76. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
  77. data/spec/acceptance/manticore/manticore_spec.rb +107 -0
  78. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  79. data/spec/acceptance/net_http/net_http_shared.rb +52 -24
  80. data/spec/acceptance/net_http/net_http_spec.rb +164 -50
  81. data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
  82. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  83. data/spec/acceptance/patron/patron_spec.rb +29 -40
  84. data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
  85. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
  86. data/spec/acceptance/shared/callbacks.rb +32 -30
  87. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
  88. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
  89. data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
  90. data/spec/acceptance/shared/request_expectations.rb +560 -296
  91. data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
  92. data/spec/acceptance/shared/stubbing_requests.rb +385 -154
  93. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
  94. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
  95. data/spec/acceptance/webmock_shared.rb +2 -2
  96. data/spec/fixtures/test.txt +1 -0
  97. data/spec/quality_spec.rb +27 -3
  98. data/spec/spec_helper.rb +11 -20
  99. data/spec/support/failures.rb +9 -0
  100. data/spec/support/my_rack_app.rb +8 -3
  101. data/spec/support/network_connection.rb +7 -13
  102. data/spec/support/webmock_server.rb +8 -3
  103. data/spec/unit/api_spec.rb +175 -0
  104. data/spec/unit/errors_spec.rb +116 -19
  105. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
  106. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
  107. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  108. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  109. data/spec/unit/rack_response_spec.rb +54 -16
  110. data/spec/unit/request_body_diff_spec.rb +90 -0
  111. data/spec/unit/request_execution_verifier_spec.rb +147 -39
  112. data/spec/unit/request_pattern_spec.rb +462 -198
  113. data/spec/unit/request_registry_spec.rb +29 -9
  114. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  115. data/spec/unit/request_signature_spec.rb +91 -49
  116. data/spec/unit/request_stub_spec.rb +71 -70
  117. data/spec/unit/response_spec.rb +100 -81
  118. data/spec/unit/stub_registry_spec.rb +37 -20
  119. data/spec/unit/stub_request_snippet_spec.rb +51 -31
  120. data/spec/unit/util/hash_counter_spec.rb +6 -6
  121. data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
  122. data/spec/unit/util/headers_spec.rb +4 -4
  123. data/spec/unit/util/json_spec.rb +29 -3
  124. data/spec/unit/util/query_mapper_spec.rb +157 -0
  125. data/spec/unit/util/uri_spec.rb +150 -36
  126. data/spec/unit/util/version_checker_spec.rb +15 -9
  127. data/spec/unit/webmock_spec.rb +57 -4
  128. data/test/http_request.rb +3 -3
  129. data/test/shared_test.rb +45 -13
  130. data/test/test_helper.rb +1 -1
  131. data/test/test_webmock.rb +6 -0
  132. data/webmock.gemspec +30 -11
  133. metadata +308 -199
  134. data/.rvmrc +0 -1
  135. data/.travis.yml +0 -11
  136. data/Guardfile +0 -24
  137. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
  138. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +0 -210
@@ -5,27 +5,94 @@ rescue LoadError
5
5
  end
6
6
 
7
7
  if defined?(Excon)
8
- WebMock::VersionChecker.new('Excon', Excon::VERSION, '0.9.6').check_version!
8
+ WebMock::VersionChecker.new('Excon', Excon::VERSION, '0.27.5').check_version!
9
9
 
10
10
  module WebMock
11
11
  module HttpLibAdapters
12
12
 
13
13
  class ExconAdapter < HttpLibAdapter
14
+ PARAMS_TO_DELETE = [:expects, :idempotent,
15
+ :instrumentor_name, :instrumentor,
16
+ :response_block,
17
+ :__construction_args, :stack,
18
+ :connection, :response]
19
+
14
20
  adapter_for :excon
15
21
 
22
+ instance_exec do
23
+ @original_excon_mock_default = nil
24
+ @stub = nil
25
+ end
26
+
16
27
  def self.enable!
17
- Excon.send(:remove_const, :Connection)
18
- Excon.send(:const_set, :Connection, ExconConnection)
28
+ self.add_excon_stub
19
29
  end
20
30
 
21
31
  def self.disable!
22
- Excon.send(:remove_const, :Connection)
23
- Excon.send(:const_set, :Connection, ExconConnection.superclass)
32
+ self.remove_excon_stub
33
+ end
34
+
35
+ def self.add_excon_stub
36
+ if not @stub
37
+ @original_excon_mock_default = ::Excon.defaults[:mock]
38
+ ::Excon.defaults[:mock] = true
39
+ @stub = ::Excon.stub({}) do |params|
40
+ self.handle_request(params)
41
+ end
42
+ end
43
+ end
44
+
45
+ def self.remove_excon_stub
46
+ ::Excon.defaults[:mock] = @original_excon_mock_default
47
+ @original_excon_mock_default = nil
48
+ Excon.stubs.delete(@stub)
49
+ @stub = nil
50
+ end
51
+
52
+ def self.handle_request(params)
53
+ mock_request = self.build_request params.dup
54
+ WebMock::RequestRegistry.instance.requested_signatures.put(mock_request)
55
+
56
+ if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request)
57
+ self.perform_callbacks(mock_request, mock_response, real_request: false)
58
+ response = self.real_response(mock_response)
59
+ response
60
+ elsif WebMock.net_connect_allowed?(mock_request.uri)
61
+ conn = new_excon_connection(params)
62
+ real_response = conn.request(request_params_from(params.merge(mock: false)))
63
+
64
+ ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), real_request: true)
65
+
66
+ real_response.data
67
+ else
68
+ raise WebMock::NetConnectNotAllowedError.new(mock_request)
69
+ end
70
+ end
71
+
72
+ def self.new_excon_connection(params)
73
+ # Ensure the connection is constructed with the exact same args
74
+ # that the orginal connection was constructed with.
75
+ args = params.fetch(:__construction_args)
76
+ ::Excon::Connection.new(connection_params_from args.merge(mock: false))
77
+ end
78
+
79
+ def self.connection_params_from(hash)
80
+ hash = hash.dup
81
+ PARAMS_TO_DELETE.each { |key| hash.delete(key) }
82
+ hash
24
83
  end
25
84
 
85
+ def self.request_params_from(hash)
86
+ hash = hash.dup
87
+ if defined?(Excon::VALID_REQUEST_KEYS)
88
+ hash.reject! {|key,_| !Excon::VALID_REQUEST_KEYS.include?(key) }
89
+ end
90
+ PARAMS_TO_DELETE.each { |key| hash.delete(key) }
91
+ hash
92
+ end
26
93
 
27
94
  def self.to_query(hash)
28
- string = ""
95
+ string = "".dup
29
96
  for key, values in hash
30
97
  if values.nil?
31
98
  string << key.to_s << '&'
@@ -40,56 +107,59 @@ if defined?(Excon)
40
107
 
41
108
  def self.build_request(params)
42
109
  params = params.dup
110
+ params.delete(:user)
111
+ params.delete(:password)
43
112
  method = (params.delete(:method) || :get).to_s.downcase.to_sym
44
113
  params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash)
45
- uri = Addressable::URI.new(params).to_s
46
- WebMock::RequestSignature.new method, uri, :body => params[:body], :headers => params[:headers]
114
+ uri = Addressable::URI.new(params).to_s
115
+ WebMock::RequestSignature.new method, uri, body: body_from(params), headers: params[:headers]
116
+ end
117
+
118
+ def self.body_from(params)
119
+ body = params[:body]
120
+ return body unless body.respond_to?(:read)
121
+
122
+ contents = body.read
123
+ body.rewind if body.respond_to?(:rewind)
124
+ contents
47
125
  end
48
126
 
49
127
  def self.real_response(mock)
50
128
  raise Excon::Errors::Timeout if mock.should_timeout
51
129
  mock.raise_error_if_any
52
- Excon::Response.new \
53
- :body => mock.body,
54
- :status => mock.status[0].to_i,
55
- :headers => mock.headers
130
+ {
131
+ body: mock.body,
132
+ status: mock.status[0].to_i,
133
+ reason_phrase: mock.status[1],
134
+ headers: mock.headers || {}
135
+ }
56
136
  end
57
137
 
58
138
  def self.mock_response(real)
59
139
  mock = WebMock::Response.new
60
- mock.status = real.status
140
+ mock.status = [real.status, real.reason_phrase]
61
141
  mock.headers = real.headers
62
- mock.body = real.body
142
+ mock.body = real.body.dup
63
143
  mock
64
144
  end
65
145
 
66
146
  def self.perform_callbacks(request, response, options = {})
67
147
  return unless WebMock::CallbackRegistry.any_callbacks?
68
- WebMock::CallbackRegistry.invoke_callbacks(options.merge(:lib => :excon), request, response)
148
+ WebMock::CallbackRegistry.invoke_callbacks(options.merge(lib: :excon), request, response)
69
149
  end
70
-
71
150
  end
151
+ end
152
+ end
72
153
 
73
- class ExconConnection < ::Excon::Connection
74
-
75
- def request_kernel(params, &block)
76
- mock_request = ExconAdapter.build_request params.dup
77
- WebMock::RequestRegistry.instance.requested_signatures.put(mock_request)
78
-
79
- if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request)
80
- ExconAdapter.perform_callbacks(mock_request, mock_response, :real_request => false)
81
- ExconAdapter.real_response(mock_response)
82
- elsif WebMock.net_connect_allowed?(mock_request.uri)
83
- real_response = super
84
- ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), :real_request => true)
85
- real_response
86
- else
87
- raise WebMock::NetConnectNotAllowedError.new(mock_request)
88
- end
89
- end
90
-
154
+ Excon::Connection.class_eval do
155
+ def self.new(args = {})
156
+ args.delete(:__construction_args)
157
+ super(args).tap do |instance|
158
+ instance.data[:__construction_args] = args
91
159
  end
92
160
  end
93
161
  end
94
162
 
163
+ # Suppresses Excon connection argument validation warning
164
+ Excon::VALID_CONNECTION_KEYS << :__construction_args
95
165
  end
@@ -0,0 +1,17 @@
1
+ module HTTP
2
+ class Client
3
+ alias_method :__perform__, :perform
4
+
5
+ def perform(request, options)
6
+ return __perform__(request, options) unless webmock_enabled?
7
+
8
+ response = WebMockPerform.new(request) { __perform__(request, options) }.exec
9
+ options.features.each { |_name, feature| response = feature.wrap_response(response) }
10
+ response
11
+ end
12
+
13
+ def webmock_enabled?
14
+ ::WebMock::HttpLibAdapters::HttpRbAdapter.enabled?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module HTTP
2
+ class Request
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
+
10
+ ::WebMock::RequestSignature.new(verb, uri.to_s, {
11
+ headers: headers.to_h,
12
+ body: request_body
13
+ })
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,64 @@
1
+ module HTTP
2
+ class Response
3
+ def to_webmock
4
+ webmock_response = ::WebMock::Response.new
5
+
6
+ webmock_response.status = [status.to_i, reason]
7
+ webmock_response.body = body.to_s
8
+ webmock_response.headers = headers.to_h
9
+
10
+ webmock_response
11
+ end
12
+
13
+ class << self
14
+ def from_webmock(request, webmock_response, request_signature = nil)
15
+ status = Status.new(webmock_response.status.first)
16
+ headers = webmock_response.headers || {}
17
+ uri = normalize_uri(request_signature && request_signature.uri)
18
+
19
+ # HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x
20
+ # and 2.x use a positional argument, and 0.x don't support supplying
21
+ # the encoding.
22
+ body = if HTTP::VERSION < "1.0.0"
23
+ Body.new(Streamer.new(webmock_response.body))
24
+ elsif HTTP::VERSION < "3.0.0"
25
+ Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
26
+ else
27
+ Body.new(Streamer.new(webmock_response.body), encoding: webmock_response.body.encoding)
28
+ end
29
+
30
+ return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
31
+
32
+ # 5.0.0 had a breaking change to require request instead of uri.
33
+ if HTTP::VERSION < '5.0.0'
34
+ return new({
35
+ status: status,
36
+ version: "1.1",
37
+ headers: headers,
38
+ body: body,
39
+ uri: uri
40
+ })
41
+ end
42
+
43
+ new({
44
+ status: status,
45
+ version: "1.1",
46
+ headers: headers,
47
+ body: body,
48
+ request: request,
49
+ })
50
+ end
51
+
52
+ private
53
+
54
+ def normalize_uri(uri)
55
+ return unless uri
56
+
57
+ uri = Addressable::URI.parse uri
58
+ uri.port = nil if uri.default_port && uri.port == uri.default_port
59
+
60
+ uri
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,29 @@
1
+ module HTTP
2
+ class Response
3
+ class Streamer
4
+ def initialize(str)
5
+ @io = StringIO.new str
6
+ end
7
+
8
+ def readpartial(size = nil, outbuf = nil)
9
+ unless size
10
+ if defined?(HTTP::Client::BUFFER_SIZE)
11
+ size = HTTP::Client::BUFFER_SIZE
12
+ elsif defined?(HTTP::Connection::BUFFER_SIZE)
13
+ size = HTTP::Connection::BUFFER_SIZE
14
+ end
15
+ end
16
+
17
+ @io.read size, outbuf
18
+ end
19
+
20
+ def close
21
+ @io.close
22
+ end
23
+
24
+ def sequence_id
25
+ -1
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ module HTTP
2
+ class WebMockPerform
3
+ def initialize(request, &perform)
4
+ @request = request
5
+ @perform = perform
6
+ @request_signature = nil
7
+ end
8
+
9
+ def exec
10
+ replay || perform || halt
11
+ end
12
+
13
+ def request_signature
14
+ unless @request_signature
15
+ @request_signature = @request.webmock_signature
16
+ register_request(@request_signature)
17
+ end
18
+
19
+ @request_signature
20
+ end
21
+
22
+ protected
23
+
24
+ def response_for_request(signature)
25
+ ::WebMock::StubRegistry.instance.response_for_request(signature)
26
+ end
27
+
28
+ def register_request(signature)
29
+ ::WebMock::RequestRegistry.instance.requested_signatures.put(signature)
30
+ end
31
+
32
+ def replay
33
+ webmock_response = response_for_request request_signature
34
+
35
+ return unless webmock_response
36
+
37
+ raise_timeout_error if webmock_response.should_timeout
38
+ webmock_response.raise_error_if_any
39
+
40
+ invoke_callbacks(webmock_response, real_request: false)
41
+ ::HTTP::Response.from_webmock @request, webmock_response, request_signature
42
+ end
43
+
44
+ def raise_timeout_error
45
+ raise Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
46
+ raise HTTP::TimeoutError, "connection error: #{Errno::ETIMEDOUT.new}"
47
+ end
48
+
49
+ def perform
50
+ return unless ::WebMock.net_connect_allowed?(request_signature.uri)
51
+ response = @perform.call
52
+ invoke_callbacks(response.to_webmock, real_request: true)
53
+ response
54
+ end
55
+
56
+ def halt
57
+ raise ::WebMock::NetConnectNotAllowedError.new request_signature
58
+ end
59
+
60
+ def invoke_callbacks(webmock_response, options = {})
61
+ ::WebMock::CallbackRegistry.invoke_callbacks(
62
+ options.merge({ lib: :http_rb }),
63
+ request_signature,
64
+ webmock_response
65
+ )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ begin
2
+ require "http"
3
+ rescue LoadError
4
+ # HTTP gem not found
5
+ end
6
+
7
+ if defined?(HTTP) && defined?(HTTP::VERSION)
8
+ WebMock::VersionChecker.new("HTTP Gem", HTTP::VERSION, "0.6.0").check_version!
9
+
10
+ module WebMock
11
+ module HttpLibAdapters
12
+ class HttpRbAdapter < HttpLibAdapter
13
+ adapter_for :http_rb
14
+
15
+ class << self
16
+ def enable!
17
+ @enabled = true
18
+ end
19
+
20
+ def disable!
21
+ @enabled = false
22
+ end
23
+
24
+ def enabled?
25
+ @enabled
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require "webmock/http_lib_adapters/http_rb/client"
33
+ require "webmock/http_lib_adapters/http_rb/request"
34
+ require "webmock/http_lib_adapters/http_rb/response"
35
+ require "webmock/http_lib_adapters/http_rb/streamer"
36
+ require "webmock/http_lib_adapters/http_rb/webmock"
37
+ end