Skip to content

Commit

Permalink
fix: dynamically parse actual query to match expected format
Browse files Browse the repository at this point in the history
  • Loading branch information
bethesque committed Jan 28, 2021
1 parent a097abd commit a86a3e3
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 18 deletions.
2 changes: 1 addition & 1 deletion lib/pact/consumer_contract/interaction_v2_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def self.parse_request request_hash, options
# 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)
request_hash['query'] = Pact::QueryHash.new(request_hash['query'], original_query_string, Pact::Query.parsed_as_nested?(request_hash['query']))
end
request = Pact::Request::Expected.from_hash(request_hash)
end
Expand Down
16 changes: 11 additions & 5 deletions lib/pact/consumer_contract/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class Query
DEFAULT_SEP = /[&;] */n
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }

class NestedQuery < Hash; end

def self.create query
if query.is_a? Hash
Pact::QueryHash.new(query)
Expand All @@ -18,12 +20,16 @@ def self.is_a_query_object?(object)
object.is_a?(Pact::QueryHash) || object.is_a?(Pact::QueryString)
end

def self.parsed_as_nested?(object)
object.is_a?(NestedQuery)
end

def self.parse_string query_string
parsed_query = parse_query(query_string)
parsed_query = parse_string_as_non_nested_query(query_string)

# If Rails nested params...
if parsed_query.keys.any?{ | key| key =~ /\[.*\]/ }
parse_nested_query(query_string)
parse_string_as_nested_query(query_string)
else
parsed_query.each_with_object({}) do | (key, value), new_hash |
new_hash[key] = [*value]
Expand All @@ -33,7 +39,7 @@ def self.parse_string query_string

# Ripped from Rack to avoid adding an unnecessary dependency, thank you Rack
# https://github.com/rack/rack/blob/649c72bab9e7b50d657b5b432d0c205c95c2be07/lib/rack/utils.rb
def self.parse_query(qs, d = nil, &unescaper)
def self.parse_string_as_non_nested_query(qs, d = nil, &unescaper)
unescaper ||= method(:unescape)

params = {}
Expand All @@ -56,7 +62,7 @@ def self.parse_query(qs, d = nil, &unescaper)
return params.to_h
end

def self.parse_nested_query(qs, d = nil)
def self.parse_string_as_nested_query(qs, d = nil)
params = {}

unless qs.nil? || qs.empty?
Expand All @@ -67,7 +73,7 @@ def self.parse_nested_query(qs, d = nil)
end
end

return params.to_h
return NestedQuery[params.to_h]
end

def self.normalize_params(params, name, v)
Expand Down
20 changes: 18 additions & 2 deletions lib/pact/consumer_contract/query_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@ class QueryHash

attr_reader :original_string

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

def nested?
@nested
end

def any_key_contains_square_brackets?
query.keys.any?{ |key| key =~ /\[.*\]/ }
end

def as_json(opts = {})
Expand All @@ -35,7 +44,14 @@ def ==(other)
# from the actual query string.
def difference(other)
require 'pact/matchers' # avoid recursive loop between this file, pact/reification and pact/matchers
Pact::Matchers.diff(query, symbolize_keys(convert_to_hash_of_arrays(Query.parse_string(other.query))), allow_unexpected_keys: false)

if any_key_contains_square_brackets?
other_query_hash_non_nested = Query.parse_string_as_non_nested_query(other.query)
Pact::Matchers.diff(query, convert_to_hash_of_arrays(other_query_hash_non_nested), allow_unexpected_keys: false)
else
other_query_hash = Query.parse_string(other.query)
Pact::Matchers.diff(query, symbolize_keys(convert_to_hash_of_arrays(other_query_hash)), allow_unexpected_keys: false)
end
end

def query
Expand Down
18 changes: 18 additions & 0 deletions spec/lib/pact/consumer_contract/query_hash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ module Pact
end
end

context "when the key in an expected hash contains [] and the actual query string also contains []" do
let(:query) { { "catId[]" => Pact.each_like("1") } }
let(:other) { QueryString.new("catId[]=1&catId[]=2")}

it "returns an empty diff" do
expect(subject.difference(other)).to be_empty
end
end

context "when the key in an expected hash does not contain [] and the actual query string contains [], GAH!!! something is going to get broken/bug missed no matter which way I code this." do
let(:query) { { "catId" => Pact.each_like("1") } }
let(:other) { QueryString.new("catId[]=1&catId[]=2")}

it "returns an empty diff and it probably shouldn't but if I change it now, all the Rails people are going to get mad at me" do
expect(subject.difference(other)).to be_empty
end
end

context "when there is an ArrayLike" do
let(:query) { { param: Pact.each_like("1") } }
let(:other) { QueryString.new('param=1&param=2') }
Expand Down
4 changes: 4 additions & 0 deletions spec/lib/pact/consumer_contract/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ module Pact
expect(subject).to eq "foo" => "bar2", "baz" => ["thing1", "thing2"]
end

it "returns a NestedQuery" do
expect(subject).to be_a(Query::NestedQuery)
end

it "handles arrays and hashes" do
expect(Query.parse_string("a[]=1&a[]=2&b[c]=3")).to eq "a" => ["1","2"], "b" => { "c" => "3" }
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ module Pact::Matchers
extend RubyVersionHelpers

describe "diff" do
STRING = "foo"
INT = 1
FLOAT = 1.0
HASH = {foo: "bar"}
ARRAY = ["foo"]
STRING ||= "foo"
INT ||= 1
FLOAT ||= 1.0
HASH ||= {foo: "bar"}
ARRAY ||= ["foo"]

COMBINATIONS = [
[STRING, "bar", "Expected \"foo\" but got \"bar\" at <path>"],
Expand Down
10 changes: 5 additions & 5 deletions spec/lib/pact/matchers/matchers_messages_regexp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ module Pact::Matchers
include RubyVersionHelpers

describe "diff" do
STRING = "foo"
INT = 1
FLOAT = 1.0
HASH = {foo: "bar"}
ARRAY = ["foo"]
STRING ||= "foo"
INT ||= 1
FLOAT ||= 1.0
HASH ||= {foo: "bar"}
ARRAY ||= ["foo"]


let(:term) { Pact.term(/foo/, "food") }
Expand Down

0 comments on commit a86a3e3

Please sign in to comment.