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
@@ -1,12 +1,20 @@
1
1
  # This is a copy of https://github.com/jnunemaker/crack/blob/master/lib/crack/json.rb
2
2
  # with date parsing removed
3
+ # Copyright (c) 2004-2008 David Heinemeier Hansson
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7
+
3
8
  module WebMock
4
9
  module Util
5
10
  class JSON
11
+ class ParseError < StandardError; end
12
+
6
13
  def self.parse(json)
7
- YAML.load(unescape(convert_json_to_yaml(json)))
8
- rescue ArgumentError
9
- raise ParseError, "Invalid JSON string"
14
+ yaml = unescape(convert_json_to_yaml(json))
15
+ YAML.load(yaml)
16
+ rescue ArgumentError => e
17
+ raise ParseError, "Invalid JSON string: #{yaml}, Error: #{e.inspect}"
10
18
  end
11
19
 
12
20
  protected
@@ -16,13 +24,12 @@ module WebMock
16
24
 
17
25
  # Ensure that ":" and "," are always followed by a space
18
26
  def self.convert_json_to_yaml(json) #:nodoc:
19
- scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
27
+ scanner, quoting, marks, times = StringScanner.new(json), false, [], []
20
28
  while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
21
29
  case char = scanner[1]
22
30
  when '"', "'"
23
31
  if !quoting
24
32
  quoting = char
25
- pos = scanner.pos
26
33
  elsif quoting == char
27
34
  quoting = false
28
35
  end
@@ -37,11 +44,17 @@ module WebMock
37
44
  json.gsub(/\\\//, '/')
38
45
  else
39
46
  left_pos = [-1].push(*marks)
40
- right_pos = marks << json.length
47
+ right_pos = marks << json.bytesize
41
48
  output = []
49
+
42
50
  left_pos.each_with_index do |left, i|
43
- output << json[left.succ..right_pos[i]]
51
+ if json.respond_to?(:byteslice)
52
+ output << json.byteslice(left.succ..right_pos[i])
53
+ else
54
+ output << json[left.succ..right_pos[i]]
55
+ end
44
56
  end
57
+
45
58
  output = output * " "
46
59
 
47
60
  times.each { |i| output[i-1] = ' ' }
@@ -0,0 +1,281 @@
1
+ module WebMock::Util
2
+ class QueryMapper
3
+ class << self
4
+ #This class is based on Addressable::URI pre 2.3.0
5
+
6
+ ##
7
+ # Converts the query component to a Hash value.
8
+ #
9
+ # @option [Symbol] notation
10
+ # May be one of <code>:flat</code>, <code>:dot</code>, or
11
+ # <code>:subscript</code>. The <code>:dot</code> notation is not
12
+ # supported for assignment. Default value is <code>:subscript</code>.
13
+ #
14
+ # @return [Hash, Array] The query string parsed as a Hash or Array object.
15
+ #
16
+ # @example
17
+ # WebMock::Util::QueryMapper.query_to_values("?one=1&two=2&three=3")
18
+ # #=> {"one" => "1", "two" => "2", "three" => "3"}
19
+ # WebMock::Util::QueryMapper("?one[two][three]=four").query_values
20
+ # #=> {"one" => {"two" => {"three" => "four"}}}
21
+ # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
22
+ # :notation => :dot
23
+ # )
24
+ # #=> {"one" => {"two" => {"three" => "four"}}}
25
+ # WebMock::Util::QueryMapper.query_to_values("?one[two][three]=four",
26
+ # :notation => :flat
27
+ # )
28
+ # #=> {"one[two][three]" => "four"}
29
+ # WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
30
+ # :notation => :flat
31
+ # )
32
+ # #=> {"one.two.three" => "four"}
33
+ # WebMock::Util::QueryMapper(
34
+ # "?one[two][three][]=four&one[two][three][]=five"
35
+ # )
36
+ # #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
37
+ # WebMock::Util::QueryMapper.query_to_values(
38
+ # "?one=two&one=three").query_values(:notation => :flat_array)
39
+ # #=> [['one', 'two'], ['one', 'three']]
40
+ def query_to_values(query, options={})
41
+ return nil if query.nil?
42
+ query = query.dup.force_encoding('utf-8') if query.respond_to?(:force_encoding)
43
+
44
+ options[:notation] ||= :subscript
45
+
46
+ if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
47
+ raise ArgumentError,
48
+ 'Invalid notation. Must be one of: ' +
49
+ '[:flat, :dot, :subscript, :flat_array].'
50
+ end
51
+
52
+ empty_accumulator = :flat_array == options[:notation] ? [] : {}
53
+
54
+ query_array = collect_query_parts(query)
55
+
56
+ query_hash = collect_query_hash(query_array, empty_accumulator, options)
57
+
58
+ normalize_query_hash(query_hash, empty_accumulator, options)
59
+ end
60
+
61
+ def normalize_query_hash(query_hash, empty_accumulator, options)
62
+ query_hash.inject(empty_accumulator.dup) do |accumulator, (key, value)|
63
+ if options[:notation] == :flat_array
64
+ accumulator << [key, value]
65
+ else
66
+ accumulator[key] = value.kind_of?(Hash) ? dehash(value) : value
67
+ end
68
+ accumulator
69
+ end
70
+ end
71
+
72
+ def collect_query_parts(query)
73
+ query_parts = query.split('&').map do |pair|
74
+ pair.split('=', 2) if pair && !pair.empty?
75
+ end
76
+ query_parts.compact
77
+ end
78
+
79
+ def collect_query_hash(query_array, empty_accumulator, options)
80
+ query_array.compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
81
+ value = if value.nil?
82
+ nil
83
+ else
84
+ ::Addressable::URI.unencode_component(value.tr('+', ' '))
85
+ end
86
+ key = Addressable::URI.unencode_component(key)
87
+ key = key.dup.force_encoding(Encoding::ASCII_8BIT) if key.respond_to?(:force_encoding)
88
+ self.__send__("fill_accumulator_for_#{options[:notation]}", accumulator, key, value)
89
+ accumulator
90
+ end
91
+ end
92
+
93
+ def fill_accumulator_for_flat(accumulator, key, value)
94
+ if accumulator[key]
95
+ raise ArgumentError, "Key was repeated: #{key.inspect}"
96
+ end
97
+ accumulator[key] = value
98
+ end
99
+
100
+ def fill_accumulator_for_flat_array(accumulator, key, value)
101
+ accumulator << [key, value]
102
+ end
103
+
104
+ def fill_accumulator_for_dot(accumulator, key, value)
105
+ array_value = false
106
+ subkeys = key.split(".")
107
+ current_hash = accumulator
108
+ subkeys[0..-2].each do |subkey|
109
+ current_hash[subkey] = {} unless current_hash[subkey]
110
+ current_hash = current_hash[subkey]
111
+ end
112
+ if array_value
113
+ if current_hash[subkeys.last] && !current_hash[subkeys.last].is_a?(Array)
114
+ current_hash[subkeys.last] = [current_hash[subkeys.last]]
115
+ end
116
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
117
+ current_hash[subkeys.last] << value
118
+ else
119
+ current_hash[subkeys.last] = value
120
+ end
121
+ end
122
+
123
+ def fill_accumulator_for_subscript(accumulator, key, value)
124
+ current_node = accumulator
125
+ subkeys = key.split(/(?=\[[^\[\]]+)/)
126
+ subkeys[0..-2].each do |subkey|
127
+ node = subkey =~ /\[\]\z/ ? [] : {}
128
+ subkey = subkey.gsub(/[\[\]]/, '')
129
+ if current_node.is_a? Array
130
+ container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(subkey) }
131
+ if container
132
+ current_node = container[subkey]
133
+ else
134
+ current_node << {subkey => node}
135
+ current_node = node
136
+ end
137
+ else
138
+ current_node[subkey] = node unless current_node[subkey]
139
+ current_node = current_node[subkey]
140
+ end
141
+ end
142
+ last_key = subkeys.last
143
+ array_value = !!(last_key =~ /\[\]$/)
144
+ last_key = last_key.gsub(/[\[\]]/, '')
145
+ if current_node.is_a? Array
146
+ last_container = current_node.select { |n| n.is_a?(Hash) }.last
147
+ if last_container && !last_container.has_key?(last_key)
148
+ if array_value
149
+ last_container[last_key] ||= []
150
+ last_container[last_key] << value
151
+ else
152
+ last_container[last_key] = value
153
+ end
154
+ else
155
+ if array_value
156
+ current_node << {last_key => [value]}
157
+ else
158
+ current_node << {last_key => value}
159
+ end
160
+ end
161
+ else
162
+ if array_value
163
+ current_node[last_key] ||= []
164
+ current_node[last_key] << value unless value.nil?
165
+ else
166
+ current_node[last_key] = value
167
+ end
168
+ end
169
+ end
170
+
171
+ ##
172
+ # Sets the query component for this URI from a Hash object.
173
+ # This method produces a query string using the :subscript notation.
174
+ # An empty Hash will result in a nil query.
175
+ #
176
+ # @param [Hash, #to_hash, Array] new_query_values The new query values.
177
+ def values_to_query(new_query_values, options = {})
178
+ options[:notation] ||= :subscript
179
+ return if new_query_values.nil?
180
+
181
+ unless new_query_values.is_a?(Array)
182
+ unless new_query_values.respond_to?(:to_hash)
183
+ raise TypeError,
184
+ "Can't convert #{new_query_values.class} into Hash."
185
+ end
186
+ new_query_values = new_query_values.to_hash
187
+ new_query_values = new_query_values.inject([]) do |object, (key, value)|
188
+ key = key.to_s if key.is_a?(::Symbol) || key.nil?
189
+ if value.is_a?(Array) && value.empty?
190
+ object << [key.to_s + '[]']
191
+ elsif value.is_a?(Array)
192
+ value.each { |v| object << [key.to_s + '[]', v] }
193
+ elsif value.is_a?(Hash)
194
+ value.each { |k, v| object << ["#{key.to_s}[#{k}]", v]}
195
+ else
196
+ object << [key.to_s, value]
197
+ end
198
+ object
199
+ end
200
+ # Useful default for OAuth and caching.
201
+ # Only to be used for non-Array inputs. Arrays should preserve order.
202
+ begin
203
+ new_query_values.sort! # may raise for non-comparable values
204
+ rescue NoMethodError, ArgumentError
205
+ # ignore
206
+ end
207
+ end
208
+
209
+ buffer = ''.dup
210
+ new_query_values.each do |parent, value|
211
+ encoded_parent = ::Addressable::URI.encode_component(
212
+ parent.dup, ::Addressable::URI::CharacterClasses::UNRESERVED
213
+ )
214
+ buffer << "#{to_query(encoded_parent, value, options)}&"
215
+ end
216
+ buffer.chop
217
+ end
218
+
219
+ def dehash(hash)
220
+ hash.each do |(key, value)|
221
+ if value.is_a?(::Hash)
222
+ hash[key] = self.dehash(value)
223
+ end
224
+ end
225
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
226
+ hash.sort.inject([]) do |accu, (_, value)|
227
+ accu << value; accu
228
+ end
229
+ else
230
+ hash
231
+ end
232
+ end
233
+
234
+ ##
235
+ # Joins and converts parent and value into a properly encoded and
236
+ # ordered URL query.
237
+ #
238
+ # @private
239
+ # @param [String] parent an URI encoded component.
240
+ # @param [Array, Hash, Symbol, #to_str] value
241
+ #
242
+ # @return [String] a properly escaped and ordered URL query.
243
+
244
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
245
+ def to_query(parent, value, options = {})
246
+ options[:notation] ||= :subscript
247
+ case value
248
+ when ::Hash
249
+ value = value.map do |key, val|
250
+ [
251
+ ::Addressable::URI.encode_component(key.to_s.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
252
+ val
253
+ ]
254
+ end
255
+ value.sort!
256
+ buffer = ''.dup
257
+ value.each do |key, val|
258
+ new_parent = options[:notation] != :flat_array ? "#{parent}[#{key}]" : parent
259
+ buffer << "#{to_query(new_parent, val, options)}&"
260
+ end
261
+ buffer.chop
262
+ when ::Array
263
+ buffer = ''.dup
264
+ value.each_with_index do |val, i|
265
+ new_parent = options[:notation] != :flat_array ? "#{parent}[#{i}]" : parent
266
+ buffer << "#{to_query(new_parent, val, options)}&"
267
+ end
268
+ buffer.chop
269
+ when NilClass
270
+ parent
271
+ else
272
+ encoded_value = Addressable::URI.encode_component(
273
+ value.to_s.dup, Addressable::URI::CharacterClasses::UNRESERVED
274
+ )
275
+ "#{parent}=#{encoded_value}"
276
+ end
277
+ end
278
+ end
279
+
280
+ end
281
+ end
@@ -1,24 +1,24 @@
1
- module Addressable
2
- class URI
3
- module CharacterClasses
4
- USERINFO = UNRESERVED + SUB_DELIMS + "\\:"
5
- end
6
- end
7
- end
8
-
9
1
  module WebMock
10
2
 
11
3
  module Util
12
4
 
13
5
  class URI
6
+ module CharacterClasses
7
+ USERINFO = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS + "\\:"
8
+ end
9
+
14
10
  ADDRESSABLE_URIS = Hash.new do |hash, key|
15
11
  hash[key] = Addressable::URI.heuristic_parse(key)
16
12
  end
17
13
 
18
14
  NORMALIZED_URIS = Hash.new do |hash, uri|
19
15
  normalized_uri = WebMock::Util::URI.heuristic_parse(uri)
20
- normalized_uri.query_values = sort_query_values(normalized_uri.query_values) if normalized_uri.query_values
16
+ if normalized_uri.query_values
17
+ sorted_query_values = sort_query_values(WebMock::Util::QueryMapper.query_to_values(normalized_uri.query, notation: Config.instance.query_values_notation) || {})
18
+ normalized_uri.query = WebMock::Util::QueryMapper.values_to_query(sorted_query_values, notation: WebMock::Config.instance.query_values_notation)
19
+ end
21
20
  normalized_uri = normalized_uri.normalize #normalize! is slower
21
+ normalized_uri.query = normalized_uri.query.gsub("+", "%2B") if normalized_uri.query
22
22
  normalized_uri.port = normalized_uri.inferred_port unless normalized_uri.port
23
23
  hash[uri] = normalized_uri
24
24
  end
@@ -33,7 +33,7 @@ module WebMock
33
33
  NORMALIZED_URIS[uri].dup
34
34
  end
35
35
 
36
- def self.variations_of_uri_as_strings(uri_object)
36
+ def self.variations_of_uri_as_strings(uri_object, only_with_scheme: false)
37
37
  normalized_uri = normalize_uri(uri_object.dup).freeze
38
38
  uris = [ normalized_uri ]
39
39
 
@@ -41,13 +41,13 @@ module WebMock
41
41
  uris = uris_with_trailing_slash_and_without(uris)
42
42
  end
43
43
 
44
- uris = uris_encoded_and_unencoded(uris)
45
-
46
44
  if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme]
47
45
  uris = uris_with_inferred_port_and_without(uris)
48
46
  end
49
47
 
50
- if normalized_uri.scheme == "http"
48
+ uris = uris_encoded_and_unencoded(uris)
49
+
50
+ if normalized_uri.scheme == "http" && !only_with_scheme
51
51
  uris = uris_with_scheme_and_without(uris)
52
52
  end
53
53
 
@@ -63,7 +63,7 @@ module WebMock
63
63
  end
64
64
 
65
65
  def self.encode_unsafe_chars_in_userinfo(userinfo)
66
- Addressable::URI.encode_component(userinfo, Addressable::URI::CharacterClasses::USERINFO)
66
+ Addressable::URI.encode_component(userinfo, WebMock::Util::URI::CharacterClasses::USERINFO)
67
67
  end
68
68
 
69
69
  def self.is_uri_localhost?(uri)
@@ -74,25 +74,35 @@ module WebMock
74
74
  private
75
75
 
76
76
  def self.sort_query_values(query_values)
77
- Hash[*query_values.sort.inject([]) { |values, pair| values + pair}]
77
+ sorted_query_values = query_values.sort
78
+ query_values.is_a?(Hash) ? Hash[*sorted_query_values.inject([]) { |values, pair| values + pair}] : sorted_query_values
78
79
  end
79
80
 
80
81
  def self.uris_with_inferred_port_and_without(uris)
81
- uris.map { |uri| [ uri, uri.gsub(%r{(:80)|(:443)}, "").freeze ] }.flatten
82
+ uris.map { |uri|
83
+ [ uri, uri.omit(:port)]
84
+ }.flatten
82
85
  end
83
86
 
84
87
  def self.uris_encoded_and_unencoded(uris)
85
88
  uris.map do |uri|
86
- [ uri.to_s, Addressable::URI.unencode(uri, String).freeze ]
89
+ [
90
+ uri.to_s.force_encoding(Encoding::ASCII_8BIT),
91
+ Addressable::URI.unencode(uri, String).force_encoding(Encoding::ASCII_8BIT).freeze
92
+ ]
87
93
  end.flatten
88
94
  end
89
95
 
90
96
  def self.uris_with_scheme_and_without(uris)
91
- uris.map { |uri| [ uri, uri.gsub(%r{^https?://},"").freeze ] }.flatten
97
+ uris.map { |uri|
98
+ [ uri, uri.gsub(%r{^https?://},"").freeze ]
99
+ }.flatten
92
100
  end
93
101
 
94
102
  def self.uris_with_trailing_slash_and_without(uris)
95
- uris = uris.map { |uri| [ uri, uri.omit(:path).freeze ] }.flatten
103
+ uris.map { |uri|
104
+ [ uri, uri.omit(:path).freeze ]
105
+ }.flatten
96
106
  end
97
107
 
98
108
  end
@@ -0,0 +1,20 @@
1
+ class WebMock::Util::ValuesStringifier
2
+ def self.stringify_values(value)
3
+ case value
4
+ when String, Numeric, TrueClass, FalseClass
5
+ value.to_s
6
+ when Hash
7
+ Hash[
8
+ value.map do |k, v|
9
+ [k, stringify_values(v)]
10
+ end
11
+ ]
12
+ when Array
13
+ value.map do |v|
14
+ stringify_values(v)
15
+ end
16
+ else
17
+ value
18
+ end
19
+ end
20
+ end
@@ -1,15 +1,41 @@
1
1
  # This code was created based on https://github.com/myronmarston/vcr/blob/master/lib/vcr/util/version_checker.rb
2
2
  # Thanks to @myronmarston
3
3
 
4
+ # Copyright (c) 2010-2012 Myron Marston
5
+
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
4
25
  module WebMock
5
26
  class VersionChecker
6
- def initialize(library_name, library_version, min_patch_level, max_minor_version = nil)
27
+ def initialize(library_name, library_version, min_patch_level, max_minor_version = nil, unsupported_versions = [])
7
28
  @library_name, @library_version = library_name, library_version
8
29
  @min_patch_level, @max_minor_version = min_patch_level, max_minor_version
30
+ @unsupported_versions = unsupported_versions || []
9
31
 
10
32
  @major, @minor, @patch = parse_version(library_version)
11
33
  @min_major, @min_minor, @min_patch = parse_version(min_patch_level)
12
- @max_major, @max_minor = parse_version(max_minor_version) if max_minor_version
34
+ if max_minor_version
35
+ @max_major, @max_minor = parse_version(max_minor_version)
36
+ else
37
+ @max_major, @max_minor = nil, nil
38
+ end
13
39
 
14
40
  @comparison_result = compare_version
15
41
  end
@@ -17,6 +43,7 @@ module WebMock
17
43
  def check_version!
18
44
  warn_about_too_low if too_low?
19
45
  warn_about_too_high if too_high?
46
+ warn_about_unsupported_version if unsupported_version?
20
47
  end
21
48
 
22
49
  private
@@ -29,6 +56,10 @@ module WebMock
29
56
  @comparison_result == :too_high
30
57
  end
31
58
 
59
+ def unsupported_version?
60
+ @unsupported_versions.include?(@library_version)
61
+ end
62
+
32
63
  def warn_about_too_low
33
64
  warn_in_red "You are using #{@library_name} #{@library_version}. " +
34
65
  "WebMock supports version #{version_requirement}."
@@ -40,6 +71,12 @@ module WebMock
40
71
  "It may not work with this version."
41
72
  end
42
73
 
74
+ def warn_about_unsupported_version
75
+ warn_in_red "You are using #{@library_name} #{@library_version}. " +
76
+ "WebMock does not support this version. " +
77
+ "WebMock supports versions #{version_requirement}."
78
+ end
79
+
43
80
  def warn_in_red(text)
44
81
  Kernel.warn colorize(text, "\e[31m")
45
82
  end
@@ -59,6 +96,7 @@ module WebMock
59
96
  def version_requirement
60
97
  req = ">= #{@min_patch_level}"
61
98
  req += ", < #{@max_major}.#{@max_minor + 1}" if @max_minor
99
+ req += ", except versions #{@unsupported_versions.join(',')}" unless @unsupported_versions.empty?
62
100
  req
63
101
  end
64
102
 
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '1.8.6' unless defined?(::WebMock::VERSION)
2
+ VERSION = '3.14.0' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -53,24 +53,65 @@ module WebMock
53
53
  Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start]
54
54
  end
55
55
 
56
+ class << self
57
+ alias :enable_net_connect! :allow_net_connect!
58
+ alias :disallow_net_connect! :disable_net_connect!
59
+ end
60
+
56
61
  def self.net_connect_allowed?(uri = nil)
62
+ return !!Config.instance.allow_net_connect if uri.nil?
63
+
57
64
  if uri.is_a?(String)
58
65
  uri = WebMock::Util::URI.normalize_uri(uri)
59
66
  end
60
67
 
61
- Config.instance.allow_net_connect ||
62
- (
63
- Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri)) ||
64
- Config.instance.allow && (
65
- (Config.instance.allow.kind_of?(Regexp) && uri.to_s =~ Config.instance.allow) ||
66
- (
67
- Config.instance.allow.respond_to?(:include?) &&
68
- (
69
- Config.instance.allow.include?(uri.host) ||
70
- Config.instance.allow.include?("#{uri.host}:#{uri.port}")
71
- )
72
- )
73
- )
68
+ !!Config.instance.allow_net_connect ||
69
+ ( Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri) ||
70
+ Config.instance.allow && net_connect_explicit_allowed?(Config.instance.allow, uri) )
71
+ end
72
+
73
+ def self.net_connect_explicit_allowed?(allowed, uri=nil)
74
+ case allowed
75
+ when Array
76
+ allowed.any? { |allowed_item| net_connect_explicit_allowed?(allowed_item, uri) }
77
+ when Regexp
78
+ (uri.to_s =~ allowed) != nil ||
79
+ (uri.omit(:port).to_s =~ allowed) != nil && uri.port == uri.default_port
80
+ when String
81
+ allowed == uri.to_s ||
82
+ allowed == uri.host ||
83
+ allowed == "#{uri.host}:#{uri.port}" ||
84
+ allowed == "#{uri.scheme}://#{uri.host}:#{uri.port}" ||
85
+ allowed == "#{uri.scheme}://#{uri.host}" && uri.port == uri.default_port
86
+ else
87
+ if allowed.respond_to?(:call)
88
+ allowed.call(uri)
89
+ end
90
+ end
91
+ end
92
+
93
+ def self.show_body_diff!
94
+ Config.instance.show_body_diff = true
95
+ end
96
+
97
+ def self.hide_body_diff!
98
+ Config.instance.show_body_diff = false
99
+ end
100
+
101
+ def self.show_body_diff?
102
+ Config.instance.show_body_diff
103
+ end
104
+
105
+ def self.hide_stubbing_instructions!
106
+ Config.instance.show_stubbing_instructions = false
107
+ end
108
+
109
+ def self.show_stubbing_instructions!
110
+ Config.instance.show_stubbing_instructions = true
111
+ end
112
+
113
+ def self.show_stubbing_instructions?
114
+ Config.instance.show_stubbing_instructions
74
115
  end
75
116
 
76
117
  def self.reset!
@@ -99,8 +140,8 @@ module WebMock
99
140
  puts WebMock::RequestExecutionVerifier.executed_requests_message
100
141
  end
101
142
 
102
- def self.globally_stub_request(&block)
103
- WebMock::StubRegistry.instance.register_global_stub(&block)
143
+ def self.globally_stub_request(order = :before_local_stubs, &block)
144
+ WebMock::StubRegistry.instance.register_global_stub(order, &block)
104
145
  end
105
146
 
106
147
  %w(
@@ -119,6 +160,4 @@ module WebMock
119
160
  end
120
161
  ))
121
162
  end
122
-
123
- self.enable!
124
163
  end