Skip to content

Commit

Permalink
fix: maintain the original string query for the provider verification…
Browse files Browse the repository at this point in the history
… while also parsing the string query into a hash to allow the matching rules to be applied correctly for use in the mock service on the consumer side

:whackamole:
  • Loading branch information
bethesque committed Nov 25, 2020
1 parent 02e2602 commit 12105dd
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 13 deletions.
14 changes: 12 additions & 2 deletions lib/pact/consumer_contract/interaction_v2_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,22 @@ def self.call hash, options
end

def self.parse_request request_hash, options
if request_hash['query'].is_a?(String)
original_query_string = request_hash['query']
query_is_string = original_query_string.is_a?(String)
if query_is_string
request_hash = request_hash.dup
request_hash['query'] = Pact::Query.parse_string(request_hash['query'])
end
# The query has to be a hash at this stage for the matching rules to be applied
request_hash = Pact::MatchingRules.merge(request_hash, request_hash['matchingRules'], options)
Pact::Request::Expected.from_hash(request_hash)
# The original query string needs to be passed in to the constructor so it can be used
# when the request is replayed. Otherwise, we loose the square brackets because they get lost
# in the translation between string => structured object, as we don't know/store which
# query string convention was used.
if query_is_string
request_hash['query'] = Pact::QueryHash.new(request_hash['query'], original_query_string)
end
request = Pact::Request::Expected.from_hash(request_hash)
end

def self.parse_response response_hash, options
Expand Down
4 changes: 4 additions & 0 deletions lib/pact/consumer_contract/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def self.create query
end
end

def self.is_a_query_object?(object)
object.is_a?(Pact::QueryHash) || object.is_a?(Pact::QueryString)
end

def self.parse_string query_string
parsed_query = parse_query(query_string)

Expand Down
5 changes: 4 additions & 1 deletion lib/pact/consumer_contract/query_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ class QueryHash
include ActiveSupportSupport
include SymbolizeKeys

def initialize(query)
attr_reader :original_string

def initialize(query, original_string = nil)
@hash = query.nil? ? query : convert_to_hash_of_arrays(query)
@original_string = original_string
end

def as_json(opts = {})
Expand Down
22 changes: 13 additions & 9 deletions lib/pact/reification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ def self.from_term(term)
when Pact::QueryString
from_term(term.query)
when Pact::QueryHash
from_term(term.query).map { |k, v|
if v.nil?
k
elsif v.is_a?(Array) #For cases where there are multiple instance of the same parameter
v.map { |x| "#{k}=#{escape(x)}"}.join('&')
else
"#{k}=#{escape(v)}"
end
}.join('&')
if term.original_string
term.original_string
else
from_term(term.query).map { |k, v|
if v.nil?
k
elsif v.is_a?(Array) #For cases where there are multiple instance of the same parameter
v.map { |x| "#{k}=#{escape(x)}"}.join('&')
else
"#{k}=#{escape(v)}"
end
}.join('&')
end
when Pact::StringWithMatchingRules
String.new(term)
else
Expand Down
14 changes: 13 additions & 1 deletion lib/pact/shared/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def initialize(method, path, headers, body, query)
@path = path
@headers = Hash === headers ? Headers.new(headers) : headers # Could be a NullExpectation - TODO make this more elegant
@body = body
@query = is_unspecified?(query) ? query : Pact::Query.create(query)
set_query(query)
end

def to_hash
Expand Down Expand Up @@ -89,6 +89,18 @@ def display_path
def display_query
(query.nil? || query.empty?) ? '' : "?#{Pact::Reification.from_term(query)}"
end

def set_query(query)
@query = if is_unspecified?(query)
query
else
if Pact::Query.is_a_query_object?(query)
query
else
Pact::Query.create(query)
end
end
end
end
end
end
10 changes: 10 additions & 0 deletions spec/lib/pact/reification_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ module Pact
end
end

context "when Hash Query with an original_string" do
let(:query) { QueryHash.new( {param: 'hello', extra: 'world'}, "foo=bar")}

subject { Reification.from_term(query)}

it "returns the original string" do
expect(subject).to eq("foo=bar")
end
end

context "when Hash Query with UTF-8 string" do
subject { Reification.from_term(query)}

Expand Down
8 changes: 8 additions & 0 deletions spec/lib/pact/shared/request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ def self.key_not_found
expect(subject).to eq("/something")
end
end

context "with a pre-parsed query that has an original_string" do
let(:query) { Pact::QueryHash.new({ "foo" => "bar", "meep" => "moop" }, "foo=bar") }

it "uses the original string" do
expect(subject).to eq ("/something?foo=bar")
end
end
end

describe "#method_and_path" do
Expand Down

0 comments on commit 12105dd

Please sign in to comment.