Skip to content

Commit

Permalink
Allowing requests with url encoded body to be matched by stubs declar…
Browse files Browse the repository at this point in the history
…ed with hash body with non-string values.
  • Loading branch information
bblimke committed Apr 29, 2018
1 parent 5979ad9 commit 76895a8
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 3 deletions.
1 change: 1 addition & 0 deletions lib/webmock.rb
Expand Up @@ -14,6 +14,7 @@
require 'webmock/util/headers'
require 'webmock/util/hash_counter'
require 'webmock/util/hash_keys_stringifier'
require 'webmock/util/values_stringifier'
require 'webmock/util/json'
require 'webmock/util/version_checker'
require 'webmock/util/hash_validator'
Expand Down
10 changes: 7 additions & 3 deletions lib/webmock/request_pattern.rb
Expand Up @@ -243,7 +243,7 @@ def matches?(body, content_type = "")

if (@pattern).is_a?(Hash)
return true if @pattern.empty?
matching_hashes?(body_as_hash(body, content_type), @pattern)
matching_body_hashes?(body_as_hash(body, content_type), @pattern, content_type)
elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
@pattern == body_as_hash(body, content_type)
else
Expand Down Expand Up @@ -298,15 +298,16 @@ def assert_non_multipart_body(content_type)
#
# @return [Boolean] true if the paramaters match the comparison
# hash, false if not.
def matching_hashes?(query_parameters, pattern)
def matching_body_hashes?(query_parameters, pattern, content_type)
return false unless query_parameters.is_a?(Hash)
return false unless query_parameters.keys.sort == pattern.keys.sort
query_parameters.each do |key, actual|
expected = pattern[key]

if actual.is_a?(Hash) && expected.is_a?(Hash)
return false unless matching_hashes?(actual, expected)
return false unless matching_body_hashes?(actual, expected, content_type)
else
expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
return false unless expected === actual
end
end
Expand All @@ -321,6 +322,9 @@ def normalize_hash(hash)
Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(hash, deep: true).sort]
end

def url_encoded_body?(content_type)
content_type =~ %r{^application/x-www-form-urlencoded}
end
end

class HeadersPattern
Expand Down
20 changes: 20 additions & 0 deletions lib/webmock/util/values_stringifier.rb
@@ -0,0 +1,20 @@
class WebMock::Util::ValuesStringifier
def self.stringify_values(value)
case value
when nil
value
when Hash
Hash[
value.map do |k, v|
[k, stringify_values(v)]
end
]
when Array
value.map do |v|
stringify_values(v)
end
else
value.to_s
end
end
end
42 changes: 42 additions & 0 deletions spec/acceptance/shared/stubbing_requests.rb
Expand Up @@ -155,6 +155,24 @@
body: 'c[d][]=f&a=1&c[d][]=e')
}.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: POST http://www.example.com/ with body 'c\[d\]\[\]=f&a=1&c\[d\]\[\]=e'))
end

describe "for request with form url encoded body and content type" do
it "should match if stubbed request body hash has string values matching string values in request body" do
WebMock.reset!
stub_request(:post, "www.example.com").with(body: {"foo" => '1'})
expect(http_request(
:post, "http://www.example.com/", headers: {'Content-Type' => 'application/x-www-form-urlencoded'},
body: "foo=1").status).to eq("200")
end

it "should match if stubbed request body hash has NON string values matching string values in request body" do
WebMock.reset!
stub_request(:post, "www.example.com").with(body: {"foo" => 1})
expect(http_request(
:post, "http://www.example.com/", headers: {'Content-Type' => 'application/x-www-form-urlencoded'},
body: "foo=1").status).to eq("200")
end
end
end


Expand Down Expand Up @@ -187,6 +205,30 @@
:post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
body: "{\"foo\":\"a b c\"}").status).to eq("200")
end

it "should match if stubbed request body hash has NON string values matching NON string values in request body" do
WebMock.reset!
stub_request(:post, "www.example.com").with(body: {"foo" => 1})
expect(http_request(
:post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
body: "{\"foo\":1}").status).to eq("200")
end

it "should not match if stubbed request body hash has string values matching NON string values in request body" do
WebMock.reset!
stub_request(:post, "www.example.com").with(body: {"foo" => '1'})
expect{http_request(
:post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
body: "{\"foo\":1}") }.to raise_error(WebMock::NetConnectNotAllowedError)
end

it "should not match if stubbed request body hash has NON string values matching string values in request body" do
WebMock.reset!
stub_request(:post, "www.example.com").with(body: {"foo" => 1})
expect{http_request(
:post, "http://www.example.com/", headers: {'Content-Type' => 'application/json'},
body: "{\"foo\":\"1\"}") }.to raise_error(WebMock::NetConnectNotAllowedError)
end
end

describe "for request with xml body and content type is set to xml" do
Expand Down

0 comments on commit 76895a8

Please sign in to comment.