-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for writing v3 matching rules
- Loading branch information
Showing
3 changed files
with
341 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
require 'pact/something_like' | ||
require 'pact/array_like' | ||
require 'pact/term' | ||
|
||
module Pact | ||
module MatchingRules::V3 | ||
class Extract | ||
|
||
def self.call matchable | ||
new(matchable).call | ||
end | ||
|
||
def initialize matchable | ||
@matchable = matchable | ||
@rules = Hash.new | ||
end | ||
|
||
def call | ||
recurse matchable, "$", nil | ||
rules | ||
end | ||
|
||
private | ||
|
||
attr_reader :matchable, :rules | ||
|
||
def recurse object, path, match_type | ||
case object | ||
when Hash then recurse_hash(object, path, match_type) | ||
when Array then recurse_array(object, path, match_type) | ||
when Pact::SomethingLike then handle_something_like(object, path, match_type) | ||
when Pact::ArrayLike then handle_array_like(object, path, match_type) | ||
when Pact::Term then record_regex_rule object, path | ||
when Pact::QueryString then recurse(object.query, path, match_type) | ||
when Pact::QueryHash then recurse_hash(object.query, path, match_type) | ||
end | ||
end | ||
|
||
def recurse_hash hash, path, match_type | ||
hash.each do | (key, value) | | ||
recurse value, "#{path}#{next_path_part(key)}", match_type | ||
end | ||
end | ||
|
||
def recurse_array new_array, path, match_type | ||
new_array.each_with_index do | value, index | | ||
recurse value, "#{path}[#{index}]", match_type | ||
end | ||
end | ||
|
||
def handle_something_like something_like, path, match_type | ||
record_match_type_rule path, "type" | ||
recurse something_like.contents, path, "type" | ||
end | ||
|
||
def handle_array_like array_like, path, match_type | ||
record_rule "#{path}", 'min' => array_like.min | ||
record_match_type_rule "#{path}[*].*", 'type' | ||
recurse array_like.contents, "#{path}[*]", :array_like | ||
end | ||
|
||
def record_rule path, rule | ||
rules[path] ||= {} | ||
rules[path]['matchers'] ||= [] | ||
rules[path]['matchers'] << rule | ||
end | ||
|
||
def record_regex_rule term, path | ||
rules[path] ||= {} | ||
rules[path]['matchers'] ||= [] | ||
rule = { 'match' => 'regex', 'regex' => term.matcher.inspect[1..-2]} | ||
rules[path]['matchers'] << rule | ||
end | ||
|
||
def record_match_type_rule path, match_type | ||
unless match_type == :array_like || match_type.nil? | ||
rules[path] ||= {} | ||
rules[path]['matchers'] ||= [] | ||
rules[path]['matchers'] << { 'match' => match_type } | ||
end | ||
end | ||
|
||
# Beth: there's a potential bug if the key contains a dot and a single quote. | ||
# Not sure what to do then. | ||
def next_path_part key | ||
if key.to_s.include?('.') | ||
"['#{key}']" | ||
else | ||
".#{key}" | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
require 'pact/matching_rules/v3/extract' | ||
require 'pact/support' | ||
|
||
module Pact | ||
module MatchingRules::V3 | ||
describe Extract do | ||
|
||
describe ".call" do | ||
|
||
subject { Extract.call(matchable) } | ||
|
||
context "with a Pact::SomethingLike" do | ||
let(:matchable) do | ||
{ | ||
body: Pact::SomethingLike.new(foo: 'bar', alligator: { name: 'Mary' }) | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.body" => { | ||
"matchers" => [ {"match" => "type"} ] | ||
} | ||
} | ||
end | ||
|
||
it "creates a rule that matches by type" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with a Pact::Term" do | ||
let(:matchable) do | ||
{ | ||
body: { | ||
alligator: { | ||
name: Pact::Term.new(generate: 'Mary', matcher: /.*a/) | ||
} | ||
} | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.body.alligator.name" => { | ||
"matchers" => [ {"match" => "regex", "regex" => ".*a"} ] | ||
} | ||
} | ||
end | ||
|
||
it "creates a rule that matches by regex" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with a Pact::SomethingLike containing a Term" do | ||
let(:matchable) do | ||
{ | ||
body: Pact::SomethingLike.new( | ||
foo: 'bar', | ||
alligator: { name: Pact::Term.new(generate: 'Mary', matcher: /.*a/) } | ||
) | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.body" => { | ||
"matchers" => [ {"match" => "type"} ] | ||
}, | ||
"$.body.alligator.name" => { | ||
"matchers" => [ {"match" => "regex", "regex"=>".*a"} ] | ||
}, | ||
} | ||
end | ||
|
||
it "the match:regex overrides the match:type" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with a Pact::SomethingLike containing an array" do | ||
let(:matchable) do | ||
{ | ||
body: Pact::SomethingLike.new( | ||
alligators: [ | ||
{name: 'Mary'}, | ||
{name: 'Betty'} | ||
] | ||
) | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.body" => { | ||
"matchers" => [ {"match" => "type"} ] | ||
} | ||
} | ||
end | ||
|
||
it "lists a rule for each item" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with an ArrayLike" do | ||
let(:matchable) do | ||
{ | ||
body: { | ||
alligators: Pact::ArrayLike.new( | ||
name: 'Fred' | ||
) | ||
} | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.body.alligators" => { | ||
"matchers" => [ {"min" => 1} ] | ||
}, | ||
"$.body.alligators[*].*" => { | ||
"matchers" => [ {"match" => "type"} ] | ||
} | ||
} | ||
end | ||
|
||
it "lists a rule for all items" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with an ArrayLike with a Pact::Term inside" do | ||
let(:matchable) do | ||
{ | ||
body: { | ||
alligators: Pact::ArrayLike.new( | ||
name: 'Fred', | ||
phoneNumber: Pact::Term.new(generate: '1234567', matcher: /\d+/) | ||
) | ||
} | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.body.alligators" => { | ||
"matchers" => [ {"min" => 1} ] | ||
}, | ||
"$.body.alligators[*].*" => { | ||
"matchers" => [ {"match" => "type"} ] | ||
}, | ||
"$.body.alligators[*].phoneNumber" => { | ||
"matchers" => [ {"match" => "regex", "regex" => "\\d+"} ] | ||
} | ||
} | ||
end | ||
|
||
it "lists a rule that specifies that the regular expression must match" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with a Pact::QueryString containing a Pact::Term" do | ||
let(:matchable) do | ||
{ | ||
query: Pact::QueryString.new(Pact::Term.new(generate: 'foobar', matcher: /foo/)) | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.query" => { | ||
"matchers" => [ {"match" => "regex", "regex" => "foo"} ] | ||
} | ||
} | ||
end | ||
|
||
it "lists a rule that specifies that the regular expression must match" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with a Pact::QueryHash containing a Pact::Term" do | ||
let(:matchable) do | ||
{ | ||
query: Pact::QueryHash.new(bar: Pact::Term.new(generate: 'foobar', matcher: /foo/)) | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{ | ||
"$.query.bar[0]" => { | ||
"matchers" => [ {"match" => "regex", "regex" => "foo"} ] | ||
} | ||
} | ||
end | ||
|
||
it "lists a rule that specifies that the regular expression must match" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with no special matching" do | ||
let(:matchable) do | ||
{ | ||
body: { alligator: { name: 'Mary' } } | ||
} | ||
end | ||
|
||
let(:rules) do | ||
{} | ||
end | ||
|
||
|
||
it "does not create any rules" do | ||
expect(subject).to eq rules | ||
end | ||
end | ||
|
||
context "with a key containing a dot" do | ||
let(:matchable) do | ||
{ | ||
"key" => { | ||
"key.with.dots" => Pact::SomethingLike.new("foo") | ||
} | ||
} | ||
end | ||
|
||
it "uses square brackets notation for the key with dots" do | ||
expect(subject.keys).to include "$.key['key.with.dots']" | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |