Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow values in a request to be changed by the provider state #57

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/pact/helpers.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'pact/something_like'
require 'pact/term'
require 'pact/provider_param'
require 'pact/array_like'

# Protected, exposed through Pact.term and Pact.like, and included in Pact::Consumer::RSpec
Expand All @@ -21,6 +22,10 @@ def term arg1, arg2 = nil
end
end

def provider_param arg1, arg2
Pact::ProviderParam.new(arg1, arg2)
end

def like content
Pact::SomethingLike.new(content)
end
Expand Down
10 changes: 10 additions & 0 deletions lib/pact/matchers/matchers.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'pact/term'
require 'pact/provider_param'
require 'pact/something_like'
require 'pact/array_like'
require 'pact/shared/null_expectation'
Expand Down Expand Up @@ -46,6 +47,7 @@ def calculate_diff expected, actual, opts = {}
when Pact::SomethingLike then calculate_diff(expected.contents, actual, options.merge(:type => true))
when Pact::ArrayLike then array_like_diff(expected, actual, options)
when Pact::Term then term_diff(expected, actual, options)
when Pact::ProviderParam then provider_param_diff(expected, actual)
else object_diff(expected, actual, options)
end
end
Expand All @@ -60,6 +62,14 @@ def term_diff term, actual, options
end
end

def provider_param_diff provider_param, actual
if provider_param.default_string == actual
return NO_DIFF
else
return Difference.new provider_param.default_string, actual, 'Given string did not match default string for provider param.'
end
end

def actual_term_diff term, actual, options
if term.matcher.match(actual)
NO_DIFF
Expand Down
8 changes: 8 additions & 0 deletions lib/pact/matching_rules/extract.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'pact/something_like'
require 'pact/array_like'
require 'pact/term'
require 'pact/provider_param'

module Pact
module MatchingRules
Expand Down Expand Up @@ -31,6 +32,7 @@ def recurse 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::ProviderParam then record_provider_param_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
Expand Down Expand Up @@ -70,6 +72,12 @@ def record_regex_rule term, path
rules[path]['regex'] = term.matcher.inspect[1..-2]
end

def record_provider_param_rule provider_param, path
rules[path] ||= {}
rules[path]['match'] = 'provider_param'
rules[path]['fill_string'] = provider_param.fill_string
end

def record_match_type_rule path, match_type
unless match_type == :array_like || match_type.nil?
rules[path] ||= {}
Expand Down
7 changes: 7 additions & 0 deletions lib/pact/matching_rules/merge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def wrap object, path
handle_match_type(object, path, rules)
elsif rules['regex']
handle_regex(object, path, rules)
elsif rules['fill_string']
handle_provider_param(object, path, rules)
else
log_ignored_rules(path, rules, {})
object
Expand All @@ -96,6 +98,11 @@ def handle_regex object, path, rules
Pact::Term.new(generate: object, matcher: Regexp.new(rules['regex']))
end

def handle_provider_param object, path, rules
log_ignored_rules(path, rules, {'match' => 'provider_param', 'fill_string' => rules['fill_string']})
Pact::ProviderParam.new(rules['fill_string'], object)
end

def log_ignored_rules path, rules, used_rules
dup_rules = rules.dup
used_rules.each_pair do | used_key, used_value |
Expand Down
138 changes: 138 additions & 0 deletions lib/pact/provider_param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
require 'pact/shared/active_support_support'

module Pact
class ProviderParam

include Pact::ActiveSupportSupport

attr_reader :fill_string, :default_string, :params

def self.json_create(obj)
new(obj['data']['fill_string'], obj['data']['params'])
end

def initialize(fill_string, arg2)
@fill_string = fill_string
if arg2.is_a? String
@default_string = arg2
@params = find_default_values
else
@params = stringify_params(arg2)
@default_string = default_string_from_params @params
end
end

def replace_params(params)
@params = stringify_params(params)
@default_string = default_string_from_params @params
end

def to_hash
{json_class: self.class.name, data: {fill_string: @fill_string, params: @params}}
end

def as_json
to_hash
end

def to_json(options = {})
as_json.to_json(options)
end

def ==(other)
if !other.respond_to?(:fill_string) || other.fill_string != @fill_string
return false
end
if !other.respond_to?(:params) || other.params != @params
return false
end

return true
end

def to_s
"Pact::ProviderParam: #{@fill_string} #{@params}"
end

def empty?
false
end

private

def stringify_params(params)
stringified_params = {}
params.each{ |k, v| stringified_params[k.to_s] = v }
stringified_params
end

def param_name_regex
/:{[a-zA-Z0-9_-]+}/
end

def parse_fill_string
matches = @fill_string.scan(param_name_regex)
matches.map do |match|
match[2..(match.length - 2)]
end
end

def find_strings_between_variables(var_names)
in_between_strings = []
previous_string_end = 0
matches = @fill_string.scan(param_name_regex)

matches.size.times do |index|
# get the locations of the string in between the matched variable names
variable_name_start = @fill_string.index(matches[index])
variable_name_end = variable_name_start + matches[index].length
string_text = @fill_string[previous_string_end...variable_name_start]
previous_string_end = variable_name_end
in_between_strings << string_text unless string_text.empty?
end
last_part = @fill_string[previous_string_end...@fill_string.length]
in_between_strings << last_part unless last_part.empty?

in_between_strings
end

def find_variable_values_in_default_string(in_between_strings)
previous_value_end = 0
values = []

in_between_strings.each do |string|
string_start = @default_string.index(string)
value = @default_string[previous_value_end...string_start]
values << value unless string_start == 0
previous_value_end = string_start + string.length
end

last_string = @default_string[previous_value_end..@default_string.length - 1]
values << last_string unless last_string.empty?

values
end

def find_default_values
var_names = parse_fill_string
in_between_strings = find_strings_between_variables(var_names)

values = find_variable_values_in_default_string(in_between_strings)

param_hash = {}
new_params_arr = var_names.zip(values)
new_params_arr.each do |key, value|
param_hash[key] = value
end
param_hash
end

def default_string_from_params(params)
default_string = @fill_string
params.each do |key, value|
default_string = default_string.gsub(':{' + key + '}', value)
end
default_string
end
end
end
6 changes: 5 additions & 1 deletion lib/pact/reification.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'randexp'
require 'pact/term'
require 'pact/provider_param'
require 'pact/something_like'
require 'pact/array_like'
require 'pact/shared/request'
Expand All @@ -10,10 +11,13 @@ module Pact
module Reification
include ActiveSupportSupport

def self.from_term(term)
def self.from_term(term, replacement_params = {})
case term
when Pact::Term, Regexp, Pact::SomethingLike, Pact::ArrayLike
from_term(term.generate)
when Pact::ProviderParam
term.replace_params(replacement_params) unless replacement_params.empty?
from_term(term.default_string)
when Hash
term.inject({}) do |mem, (key,t)|
mem[key] = from_term(t)
Expand Down
12 changes: 6 additions & 6 deletions lib/pact/shared/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def method_and_path
"#{method.upcase} #{full_path}"
end

def full_path
display_path + display_query
def full_path(provider_params = {})
display_path(provider_params) + display_query(provider_params)
end

def content_type
Expand Down Expand Up @@ -83,13 +83,13 @@ def to_hash_without_body_or_query
hash
end

def display_path
reified_path = Pact::Reification.from_term(path)
def display_path(provider_params)
reified_path = Pact::Reification.from_term(path, provider_params)
reified_path.empty? ? "/" : reified_path
end

def display_query
(query.nil? || query.empty?) ? '' : "?#{Pact::Reification.from_term(query)}"
def display_query(provider_params)
(query.nil? || query.empty?) ? '' : "?#{Pact::Reification.from_term(query, provider_params)}"
end

end
Expand Down
1 change: 1 addition & 0 deletions lib/pact/support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'pact/matchers'
require 'pact/logging'
require 'pact/term'
require 'pact/provider_param'
require 'pact/helpers'
require 'pact/configuration'
require 'pact/reification'
Expand Down
16 changes: 16 additions & 0 deletions spec/lib/pact/helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ module Pact

include Pact::Helpers

describe "#provider_param" do
pp = Pact::ProviderParam.new('some/:{var}/here', {var: 'url'})

context 'with a hash argument' do
it "creates a Pact::ProviderParam" do
expect(provider_param('some/:{var}/here', {var: 'url'})).to eq(pp)
end
end

context 'with two string argumnets' do
it 'creates a Pact::ProviderParam' do
expect(provider_param('some/:{var}/here', 'some/url/here').params).to eq({'var' => 'url'})
end
end
end

describe "#term" do

context "with a Hash argument" do
Expand Down
19 changes: 19 additions & 0 deletions spec/lib/pact/matchers/matchers_provider_param_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'pact/provider_param'

module Pact
describe Matchers do

let(:expected) do
{
url: Pact::ProviderParam.new('/some/:{url_var}/here', {url_var: 'url'})
}
end

it 'should not have a difference' do
actual = {
url: '/some/url/here'
}
expect(Pact::Matchers.diff(expected, actual)).to be_empty
end
end
end
45 changes: 45 additions & 0 deletions spec/lib/pact/provider_param_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'spec_helper'

module Pact
describe ProviderParam do

describe 'initialize' do
it 'creates a ProviderParam' do
pp = ProviderParam.new('/some/url/with/:{param}', {param: 'a parameter'})
expect(pp).to be_instance_of(Pact::ProviderParam)
end
end

describe 'default_string' do
it 'returns the default string' do
pp = ProviderParam.new('/some/url/with/:{param}', {param: 'a parameter'})
expect(pp.default_string).to eq('/some/url/with/a parameter')
end
end

describe 'fill_string' do
it 'returns the fill string' do
pp = ProviderParam.new('/some/:{var}/here:{blah}', {var: 'var', blah: 'aoeu'})
expect(pp.fill_string).to eq('/some/:{var}/here:{blah}')
expect(pp.default_string).to eq('/some/var/hereaoeu')
end
end

describe 'initialize with fill string' do
it 'finds the param names' do
pp = ProviderParam.new('/some/:{id}/path_:{here}', {id: '5', here: 'something'})
expect(pp.params).to eq({'id' => '5', 'here' => 'something'})
end

it 'finds the param names from a given default string' do
pp = ProviderParam.new('/some/:{id}/path_:{here}', '/some/4/path_blah')
expect(pp.params).to eq({'id' => '4', 'here' => 'blah'})
end

it 'finds the param names with a param at the start' do
pp = ProviderParam.new(':{first}and:{second}then:{third}', 'oneandtwothenthree')
expect(pp.params).to eq({'first' => 'one', 'second' => 'two', 'third' => 'three'})
end
end
end
end