webmock 3.7.1

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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +34 -0
  4. data/.rspec-tm +2 -0
  5. data/.travis.yml +19 -0
  6. data/CHANGELOG.md +1698 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE +20 -0
  9. data/README.md +1125 -0
  10. data/Rakefile +28 -0
  11. data/lib/webmock.rb +59 -0
  12. data/lib/webmock/api.rb +109 -0
  13. data/lib/webmock/assertion_failure.rb +11 -0
  14. data/lib/webmock/callback_registry.rb +35 -0
  15. data/lib/webmock/config.rb +18 -0
  16. data/lib/webmock/cucumber.rb +10 -0
  17. data/lib/webmock/deprecation.rb +9 -0
  18. data/lib/webmock/errors.rb +17 -0
  19. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +214 -0
  20. data/lib/webmock/http_lib_adapters/curb_adapter.rb +347 -0
  21. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +228 -0
  22. data/lib/webmock/http_lib_adapters/excon_adapter.rb +162 -0
  23. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +7 -0
  24. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +19 -0
  25. data/lib/webmock/http_lib_adapters/http_rb/client.rb +14 -0
  26. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  27. data/lib/webmock/http_lib_adapters/http_rb/response.rb +43 -0
  28. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  29. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  30. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  31. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +242 -0
  32. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +130 -0
  33. data/lib/webmock/http_lib_adapters/net_http.rb +361 -0
  34. data/lib/webmock/http_lib_adapters/net_http_response.rb +34 -0
  35. data/lib/webmock/http_lib_adapters/patron_adapter.rb +130 -0
  36. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +174 -0
  37. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  38. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  39. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  40. data/lib/webmock/matchers/hash_including_matcher.rb +17 -0
  41. data/lib/webmock/minitest.rb +41 -0
  42. data/lib/webmock/rack_response.rb +69 -0
  43. data/lib/webmock/request_body_diff.rb +64 -0
  44. data/lib/webmock/request_execution_verifier.rb +77 -0
  45. data/lib/webmock/request_pattern.rb +370 -0
  46. data/lib/webmock/request_registry.rb +35 -0
  47. data/lib/webmock/request_signature.rb +54 -0
  48. data/lib/webmock/request_signature_snippet.rb +61 -0
  49. data/lib/webmock/request_stub.rb +100 -0
  50. data/lib/webmock/response.rb +153 -0
  51. data/lib/webmock/responses_sequence.rb +40 -0
  52. data/lib/webmock/rspec.rb +41 -0
  53. data/lib/webmock/rspec/matchers.rb +27 -0
  54. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +78 -0
  55. data/lib/webmock/rspec/matchers/webmock_matcher.rb +67 -0
  56. data/lib/webmock/stub_registry.rb +67 -0
  57. data/lib/webmock/stub_request_snippet.rb +38 -0
  58. data/lib/webmock/test_unit.rb +22 -0
  59. data/lib/webmock/util/hash_counter.rb +39 -0
  60. data/lib/webmock/util/hash_keys_stringifier.rb +25 -0
  61. data/lib/webmock/util/hash_validator.rb +17 -0
  62. data/lib/webmock/util/headers.rb +64 -0
  63. data/lib/webmock/util/json.rb +67 -0
  64. data/lib/webmock/util/query_mapper.rb +281 -0
  65. data/lib/webmock/util/uri.rb +110 -0
  66. data/lib/webmock/util/values_stringifier.rb +20 -0
  67. data/lib/webmock/util/version_checker.rb +111 -0
  68. data/lib/webmock/version.rb +3 -0
  69. data/lib/webmock/webmock.rb +161 -0
  70. data/minitest/test_helper.rb +34 -0
  71. data/minitest/test_webmock.rb +9 -0
  72. data/minitest/webmock_spec.rb +60 -0
  73. data/spec/acceptance/async_http_client/async_http_client_spec.rb +349 -0
  74. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  75. data/spec/acceptance/curb/curb_spec.rb +492 -0
  76. data/spec/acceptance/curb/curb_spec_helper.rb +147 -0
  77. data/spec/acceptance/em_http_request/em_http_request_spec.rb +406 -0
  78. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +77 -0
  79. data/spec/acceptance/excon/excon_spec.rb +77 -0
  80. data/spec/acceptance/excon/excon_spec_helper.rb +50 -0
  81. data/spec/acceptance/http_rb/http_rb_spec.rb +82 -0
  82. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  83. data/spec/acceptance/httpclient/httpclient_spec.rb +217 -0
  84. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +57 -0
  85. data/spec/acceptance/manticore/manticore_spec.rb +56 -0
  86. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  87. data/spec/acceptance/net_http/net_http_shared.rb +153 -0
  88. data/spec/acceptance/net_http/net_http_spec.rb +331 -0
  89. data/spec/acceptance/net_http/net_http_spec_helper.rb +64 -0
  90. data/spec/acceptance/net_http/real_net_http_spec.rb +20 -0
  91. data/spec/acceptance/patron/patron_spec.rb +125 -0
  92. data/spec/acceptance/patron/patron_spec_helper.rb +54 -0
  93. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +313 -0
  94. data/spec/acceptance/shared/callbacks.rb +148 -0
  95. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +36 -0
  96. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +95 -0
  97. data/spec/acceptance/shared/precedence_of_stubs.rb +15 -0
  98. data/spec/acceptance/shared/request_expectations.rb +930 -0
  99. data/spec/acceptance/shared/returning_declared_responses.rb +409 -0
  100. data/spec/acceptance/shared/stubbing_requests.rb +643 -0
  101. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +135 -0
  102. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +60 -0
  103. data/spec/acceptance/webmock_shared.rb +41 -0
  104. data/spec/fixtures/test.txt +1 -0
  105. data/spec/quality_spec.rb +84 -0
  106. data/spec/spec_helper.rb +48 -0
  107. data/spec/support/example_curl_output.txt +22 -0
  108. data/spec/support/failures.rb +9 -0
  109. data/spec/support/my_rack_app.rb +53 -0
  110. data/spec/support/network_connection.rb +19 -0
  111. data/spec/support/webmock_server.rb +70 -0
  112. data/spec/unit/api_spec.rb +175 -0
  113. data/spec/unit/errors_spec.rb +129 -0
  114. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +17 -0
  115. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +12 -0
  116. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  117. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  118. data/spec/unit/rack_response_spec.rb +112 -0
  119. data/spec/unit/request_body_diff_spec.rb +90 -0
  120. data/spec/unit/request_execution_verifier_spec.rb +208 -0
  121. data/spec/unit/request_pattern_spec.rb +601 -0
  122. data/spec/unit/request_registry_spec.rb +95 -0
  123. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  124. data/spec/unit/request_signature_spec.rb +155 -0
  125. data/spec/unit/request_stub_spec.rb +199 -0
  126. data/spec/unit/response_spec.rb +282 -0
  127. data/spec/unit/stub_registry_spec.rb +103 -0
  128. data/spec/unit/stub_request_snippet_spec.rb +115 -0
  129. data/spec/unit/util/hash_counter_spec.rb +39 -0
  130. data/spec/unit/util/hash_keys_stringifier_spec.rb +27 -0
  131. data/spec/unit/util/headers_spec.rb +28 -0
  132. data/spec/unit/util/json_spec.rb +33 -0
  133. data/spec/unit/util/query_mapper_spec.rb +157 -0
  134. data/spec/unit/util/uri_spec.rb +361 -0
  135. data/spec/unit/util/version_checker_spec.rb +65 -0
  136. data/spec/unit/webmock_spec.rb +19 -0
  137. data/test/http_request.rb +24 -0
  138. data/test/shared_test.rb +108 -0
  139. data/test/test_helper.rb +23 -0
  140. data/test/test_webmock.rb +6 -0
  141. data/webmock.gemspec +45 -0
  142. metadata +496 -0
@@ -0,0 +1,69 @@
1
+ module WebMock
2
+ class RackResponse < Response
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def evaluate(request)
8
+ env = build_rack_env(request)
9
+
10
+ status, headers, response = @app.call(env)
11
+
12
+ Response.new(
13
+ body: body_from_rack_response(response),
14
+ headers: headers,
15
+ status: [status, Rack::Utils::HTTP_STATUS_CODES[status]]
16
+ )
17
+ end
18
+
19
+ def body_from_rack_response(response)
20
+ body = "".dup
21
+ response.each { |line| body << line }
22
+ response.close if response.respond_to?(:close)
23
+ return body
24
+ end
25
+
26
+ def build_rack_env(request)
27
+ uri = request.uri
28
+ headers = (request.headers || {}).dup
29
+ body = request.body || ''
30
+
31
+ env = {
32
+ # CGI variables specified by Rack
33
+ 'REQUEST_METHOD' => request.method.to_s.upcase,
34
+ 'CONTENT_TYPE' => headers.delete('Content-Type'),
35
+ 'CONTENT_LENGTH' => body.bytesize,
36
+ 'PATH_INFO' => uri.path,
37
+ 'QUERY_STRING' => uri.query || '',
38
+ 'SERVER_NAME' => uri.host,
39
+ 'SERVER_PORT' => uri.port,
40
+ 'SCRIPT_NAME' => ""
41
+ }
42
+
43
+ env['HTTP_AUTHORIZATION'] = 'Basic ' + [uri.userinfo].pack('m').delete("\r\n") if uri.userinfo
44
+
45
+ # Rack-specific variables
46
+ env['rack.input'] = StringIO.new(body)
47
+ env['rack.errors'] = $stderr
48
+ env['rack.version'] = Rack::VERSION
49
+ env['rack.url_scheme'] = uri.scheme
50
+ env['rack.run_once'] = true
51
+ env['rack.session'] = session
52
+ env['rack.session.options'] = session_options
53
+
54
+ headers.each do |k, v|
55
+ env["HTTP_#{k.tr('-','_').upcase}"] = v
56
+ end
57
+
58
+ env
59
+ end
60
+
61
+ def session
62
+ @session ||= {}
63
+ end
64
+
65
+ def session_options
66
+ @session_options ||= {}
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,64 @@
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
+ attr_reader :request_signature, :request_stub
19
+ private :request_signature, :request_stub
20
+
21
+ private
22
+
23
+ def request_signature_diffable?
24
+ request_signature.json_headers? && request_signature_parseable_json?
25
+ end
26
+
27
+ def request_stub_diffable?
28
+ request_stub_body.is_a?(Hash) || request_stub_parseable_json?
29
+ end
30
+
31
+ def request_signature_body_hash
32
+ JSON.parse(request_signature.body)
33
+ end
34
+
35
+ def request_stub_body_hash
36
+ return request_stub_body if request_stub_body.is_a?(Hash)
37
+
38
+ JSON.parse(request_stub_body)
39
+ end
40
+
41
+ def request_stub_body
42
+ request_stub.request_pattern &&
43
+ request_stub.request_pattern.body_pattern &&
44
+ request_stub.request_pattern.body_pattern.pattern
45
+ end
46
+
47
+ def request_signature_parseable_json?
48
+ parseable_json?(request_signature.body)
49
+ end
50
+
51
+ def request_stub_parseable_json?
52
+ parseable_json?(request_stub_body)
53
+ end
54
+
55
+ def parseable_json?(body_pattern)
56
+ return false unless body_pattern.is_a?(String)
57
+
58
+ JSON.parse(body_pattern)
59
+ true
60
+ rescue JSON::ParserError
61
+ false
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,77 @@
1
+ module WebMock
2
+ class RequestExecutionVerifier
3
+
4
+ attr_accessor :request_pattern, :expected_times_executed, :times_executed, :at_least_times_executed, :at_most_times_executed
5
+
6
+ def initialize(request_pattern = nil, expected_times_executed = nil, at_least_times_executed = nil, at_most_times_executed = nil)
7
+ @request_pattern = request_pattern
8
+ @expected_times_executed = expected_times_executed
9
+ @at_least_times_executed = at_least_times_executed
10
+ @at_most_times_executed = at_most_times_executed
11
+ end
12
+
13
+ def matches?
14
+ @times_executed =
15
+ RequestRegistry.instance.times_executed(@request_pattern)
16
+
17
+ if @at_least_times_executed
18
+ @times_executed >= @at_least_times_executed
19
+ elsif @at_most_times_executed
20
+ @times_executed <= @at_most_times_executed
21
+ else
22
+ @times_executed == (@expected_times_executed || 1)
23
+ end
24
+ end
25
+
26
+ def does_not_match?
27
+ @times_executed =
28
+ RequestRegistry.instance.times_executed(@request_pattern)
29
+ if @expected_times_executed
30
+ @times_executed != @expected_times_executed
31
+ else
32
+ @times_executed == 0
33
+ end
34
+ end
35
+
36
+ def description
37
+ "request #{request_pattern} #{quantity_phrase.strip}"
38
+ end
39
+
40
+ def failure_message
41
+ failure_message_phrase(false)
42
+ end
43
+
44
+ def failure_message_when_negated
45
+ failure_message_phrase(true)
46
+ end
47
+
48
+ def self.executed_requests_message
49
+ "\n\nThe following requests were made:\n\n#{RequestRegistry.instance}\n" + "="*60
50
+ end
51
+
52
+ private
53
+
54
+ def failure_message_phrase(is_negated=false)
55
+ negation = is_negated ? "was not" : "was"
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
58
+ end
59
+
60
+ def quantity_phrase(is_negated=false)
61
+ if @at_least_times_executed
62
+ "at least #{times(@at_least_times_executed)} "
63
+ elsif @at_most_times_executed
64
+ "at most #{times(@at_most_times_executed)} "
65
+ elsif @expected_times_executed
66
+ "#{times(@expected_times_executed)} "
67
+ else
68
+ is_negated ? "" : "#{times(1)} "
69
+ end
70
+ end
71
+
72
+ def times(times)
73
+ "#{times} time#{ (times == 1) ? '' : 's'}"
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,370 @@
1
+ module WebMock
2
+
3
+ module RSpecMatcherDetector
4
+ def rSpecHashIncludingMatcher?(matcher)
5
+ matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashIncludingMatcher/
6
+ end
7
+
8
+ def rSpecHashExcludingMatcher?(matcher)
9
+ matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashExcludingMatcher/
10
+ end
11
+ end
12
+
13
+ class RequestPattern
14
+
15
+ attr_reader :method_pattern, :uri_pattern, :body_pattern, :headers_pattern
16
+
17
+ def initialize(method, uri, options = {})
18
+ @method_pattern = MethodPattern.new(method)
19
+ @uri_pattern = create_uri_pattern(uri)
20
+ @body_pattern = nil
21
+ @headers_pattern = nil
22
+ @with_block = nil
23
+ assign_options(options)
24
+ end
25
+
26
+ def with(options = {}, &block)
27
+ raise ArgumentError.new('#with method invoked with no arguments. Either options hash or block must be specified. Created a block with do..end? Try creating it with curly braces {} instead.') if options.empty? && !block_given?
28
+ assign_options(options)
29
+ @with_block = block
30
+ self
31
+ end
32
+
33
+ def matches?(request_signature)
34
+ content_type = request_signature.headers['Content-Type'] if request_signature.headers
35
+ content_type = content_type.split(';').first if content_type
36
+ @method_pattern.matches?(request_signature.method) &&
37
+ @uri_pattern.matches?(request_signature.uri) &&
38
+ (@body_pattern.nil? || @body_pattern.matches?(request_signature.body, content_type || "")) &&
39
+ (@headers_pattern.nil? || @headers_pattern.matches?(request_signature.headers)) &&
40
+ (@with_block.nil? || @with_block.call(request_signature))
41
+ end
42
+
43
+ def to_s
44
+ string = "#{@method_pattern.to_s.upcase}".dup
45
+ string << " #{@uri_pattern.to_s}"
46
+ string << " with body #{@body_pattern.to_s}" if @body_pattern
47
+ string << " with headers #{@headers_pattern.to_s}" if @headers_pattern
48
+ string << " with given block" if @with_block
49
+ string
50
+ end
51
+
52
+ private
53
+
54
+
55
+ def assign_options(options)
56
+ options = WebMock::Util::HashKeysStringifier.stringify_keys!(options, deep: true)
57
+ HashValidator.new(options).validate_keys('body', 'headers', 'query', 'basic_auth')
58
+ set_basic_auth_as_headers!(options)
59
+ @body_pattern = BodyPattern.new(options['body']) if options.has_key?('body')
60
+ @headers_pattern = HeadersPattern.new(options['headers']) if options.has_key?('headers')
61
+ @uri_pattern.add_query_params(options['query']) if options.has_key?('query')
62
+ end
63
+
64
+ def set_basic_auth_as_headers!(options)
65
+ if basic_auth = options.delete('basic_auth')
66
+ validate_basic_auth!(basic_auth)
67
+ options['headers'] ||= {}
68
+ options['headers']['Authorization'] = WebMock::Util::Headers.basic_auth_header(basic_auth[0],basic_auth[1])
69
+ end
70
+ end
71
+
72
+ def validate_basic_auth!(basic_auth)
73
+ if !basic_auth.is_a?(Array) || basic_auth.map{|e| e.is_a?(String)}.uniq != [true]
74
+ raise "The basic_auth option value should be an array which contains 2 strings: username and password"
75
+ end
76
+ end
77
+
78
+ def create_uri_pattern(uri)
79
+ if uri.is_a?(Regexp)
80
+ URIRegexpPattern.new(uri)
81
+ elsif uri.is_a?(Addressable::Template)
82
+ URIAddressablePattern.new(uri)
83
+ else
84
+ URIStringPattern.new(uri)
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+
91
+ class MethodPattern
92
+ def initialize(pattern)
93
+ @pattern = pattern
94
+ end
95
+
96
+ def matches?(method)
97
+ @pattern == method || @pattern == :any
98
+ end
99
+
100
+ def to_s
101
+ @pattern.to_s
102
+ end
103
+ end
104
+
105
+
106
+ class URIPattern
107
+ include RSpecMatcherDetector
108
+
109
+ def initialize(pattern)
110
+ @pattern = case pattern
111
+ when Addressable::URI, Addressable::Template
112
+ pattern
113
+ else
114
+ WebMock::Util::URI.normalize_uri(pattern)
115
+ end
116
+ @query_params = nil
117
+ end
118
+
119
+ def add_query_params(query_params)
120
+ @query_params = if query_params.is_a?(Hash)
121
+ query_params
122
+ elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher) \
123
+ || query_params.is_a?(WebMock::Matchers::HashExcludingMatcher)
124
+ query_params
125
+ elsif rSpecHashIncludingMatcher?(query_params)
126
+ WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params)
127
+ elsif rSpecHashExcludingMatcher?(query_params)
128
+ WebMock::Matchers::HashExcludingMatcher.from_rspec_matcher(query_params)
129
+ else
130
+ WebMock::Util::QueryMapper.query_to_values(query_params, notation: Config.instance.query_values_notation)
131
+ end
132
+ end
133
+
134
+ def to_s
135
+ str = @pattern.inspect
136
+ str += " with query params #{@query_params.inspect}" if @query_params
137
+ str
138
+ end
139
+ end
140
+
141
+ class URIRegexpPattern < URIPattern
142
+ def matches?(uri)
143
+ WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } &&
144
+ (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation))
145
+ end
146
+
147
+ def to_s
148
+ str = @pattern.inspect
149
+ str += " with query params #{@query_params.inspect}" if @query_params
150
+ str
151
+ end
152
+ end
153
+
154
+ class URIAddressablePattern < URIPattern
155
+ def matches?(uri)
156
+ if @query_params.nil?
157
+ # Let Addressable check the whole URI
158
+ matches_with_variations?(uri)
159
+ else
160
+ # WebMock checks the query, Addressable checks everything else
161
+ matches_with_variations?(uri.omit(:query)) &&
162
+ @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query)
163
+ end
164
+ end
165
+
166
+ def add_query_params(query_params)
167
+ @@add_query_params_warned ||= false
168
+ if not @@add_query_params_warned
169
+ @@add_query_params_warned = true
170
+ warn "WebMock warning: ignoring query params in RFC 6570 template and checking them with WebMock"
171
+ end
172
+ super(query_params)
173
+ end
174
+
175
+ def to_s
176
+ str = @pattern.pattern.inspect
177
+ str += " with variables #{@pattern.variables.inspect}" if @pattern.variables
178
+ str
179
+ end
180
+
181
+ private
182
+
183
+ def matches_with_variations?(uri)
184
+ normalized_template = Addressable::Template.new(WebMock::Util::URI.heuristic_parse(@pattern.pattern))
185
+
186
+ WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| normalized_template.match(u) }
187
+ end
188
+ end
189
+
190
+ class URIStringPattern < URIPattern
191
+ def matches?(uri)
192
+ if @pattern.is_a?(Addressable::URI)
193
+ if @query_params
194
+ uri.omit(:query) === @pattern &&
195
+ (@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, notation: Config.instance.query_values_notation))
196
+ else
197
+ uri === @pattern
198
+ end
199
+ else
200
+ false
201
+ end
202
+ end
203
+
204
+ def add_query_params(query_params)
205
+ super
206
+ if @query_params.is_a?(Hash) || @query_params.is_a?(String)
207
+ query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, notation: Config.instance.query_values_notation) || {}).merge(@query_params)
208
+ @pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, notation: WebMock::Config.instance.query_values_notation)
209
+ @query_params = nil
210
+ end
211
+ end
212
+
213
+ def to_s
214
+ str = WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
215
+ str += " with query params #{@query_params.inspect}" if @query_params
216
+ str
217
+ end
218
+ end
219
+
220
+
221
+ class BodyPattern
222
+ include RSpecMatcherDetector
223
+
224
+ BODY_FORMATS = {
225
+ 'text/xml' => :xml,
226
+ 'application/xml' => :xml,
227
+ 'application/json' => :json,
228
+ 'text/json' => :json,
229
+ 'application/javascript' => :json,
230
+ 'text/javascript' => :json,
231
+ 'text/html' => :html,
232
+ 'application/x-yaml' => :yaml,
233
+ 'text/yaml' => :yaml,
234
+ 'text/plain' => :plain
235
+ }
236
+
237
+ attr_reader :pattern
238
+
239
+ def initialize(pattern)
240
+ @pattern = if pattern.is_a?(Hash)
241
+ normalize_hash(pattern)
242
+ elsif rSpecHashIncludingMatcher?(pattern)
243
+ WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(pattern)
244
+ else
245
+ pattern
246
+ end
247
+ end
248
+
249
+ def matches?(body, content_type = "")
250
+ assert_non_multipart_body(content_type)
251
+
252
+ if (@pattern).is_a?(Hash)
253
+ return true if @pattern.empty?
254
+ matching_body_hashes?(body_as_hash(body, content_type), @pattern, content_type)
255
+ elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
256
+ @pattern == body_as_hash(body, content_type)
257
+ else
258
+ empty_string?(@pattern) && empty_string?(body) ||
259
+ @pattern == body ||
260
+ @pattern === body
261
+ end
262
+ end
263
+
264
+ def to_s
265
+ @pattern.inspect
266
+ end
267
+
268
+ private
269
+ def body_as_hash(body, content_type)
270
+ case BODY_FORMATS[content_type]
271
+ when :json then
272
+ WebMock::Util::JSON.parse(body)
273
+ when :xml then
274
+ Crack::XML.parse(body)
275
+ else
276
+ WebMock::Util::QueryMapper.query_to_values(body, notation: Config.instance.query_values_notation)
277
+ end
278
+ end
279
+
280
+ def assert_non_multipart_body(content_type)
281
+ if content_type =~ %r{^multipart/form-data}
282
+ raise ArgumentError.new("WebMock does not support matching body for multipart/form-data requests yet :(")
283
+ end
284
+ end
285
+
286
+ # Compare two hashes for equality
287
+ #
288
+ # For two hashes to match they must have the same length and all
289
+ # values must match when compared using `#===`.
290
+ #
291
+ # The following hashes are examples of matches:
292
+ #
293
+ # {a: /\d+/} and {a: '123'}
294
+ #
295
+ # {a: '123'} and {a: '123'}
296
+ #
297
+ # {a: {b: /\d+/}} and {a: {b: '123'}}
298
+ #
299
+ # {a: {b: 'wow'}} and {a: {b: 'wow'}}
300
+ #
301
+ # @param [Hash] query_parameters typically the result of parsing
302
+ # JSON, XML or URL encoded parameters.
303
+ #
304
+ # @param [Hash] pattern which contains keys with a string, hash or
305
+ # regular expression value to use for comparison.
306
+ #
307
+ # @return [Boolean] true if the paramaters match the comparison
308
+ # hash, false if not.
309
+ def matching_body_hashes?(query_parameters, pattern, content_type)
310
+ return false unless query_parameters.is_a?(Hash)
311
+ return false unless query_parameters.keys.sort == pattern.keys.sort
312
+ query_parameters.each do |key, actual|
313
+ expected = pattern[key]
314
+
315
+ if actual.is_a?(Hash) && expected.is_a?(Hash)
316
+ return false unless matching_body_hashes?(actual, expected, content_type)
317
+ else
318
+ expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
319
+ return false unless expected === actual
320
+ end
321
+ end
322
+ true
323
+ end
324
+
325
+ def empty_string?(string)
326
+ string.nil? || string == ""
327
+ end
328
+
329
+ def normalize_hash(hash)
330
+ Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(hash, deep: true).sort]
331
+ end
332
+
333
+ def url_encoded_body?(content_type)
334
+ content_type =~ %r{^application/x-www-form-urlencoded}
335
+ end
336
+ end
337
+
338
+ class HeadersPattern
339
+ def initialize(pattern)
340
+ @pattern = WebMock::Util::Headers.normalize_headers(pattern) || {}
341
+ end
342
+
343
+ def matches?(headers)
344
+ if empty_headers?(@pattern)
345
+ empty_headers?(headers)
346
+ else
347
+ return false if empty_headers?(headers)
348
+ @pattern.each do |key, value|
349
+ return false unless headers.has_key?(key) && value === headers[key]
350
+ end
351
+ true
352
+ end
353
+ end
354
+
355
+ def to_s
356
+ WebMock::Util::Headers.sorted_headers_string(@pattern)
357
+ end
358
+
359
+ def pp_to_s
360
+ WebMock::Util::Headers.pp_headers_string(@pattern)
361
+ end
362
+
363
+ private
364
+
365
+ def empty_headers?(headers)
366
+ headers.nil? || headers == {}
367
+ end
368
+ end
369
+
370
+ end