webmock 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
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