webmock 1.8.6 → 3.14.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 (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