Skip to content

Commit

Permalink
feat(generators): Pass context and add ProviderState generator
Browse files Browse the repository at this point in the history
  • Loading branch information
hhhonzik authored and slt committed Sep 13, 2022
1 parent 91da38f commit d5007bb
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 7 deletions.
26 changes: 26 additions & 0 deletions lib/pact/provider/generators.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

require 'pact/provider/generators/provider_state';

module Pact
module Provider
class Generators
def self.add_generator generator
generators.unshift(generator)
end

def self.generators
@generators ||= []
end

def self.execute_generators object, interaction_context = nil
generators.each do | parser |
return parser.call(object, interaction_context) if parser.can_generate?(object)
end

raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact generator: #{object}")
end

add_generator(ProviderStateGenerator.new)
end
end
end
58 changes: 58 additions & 0 deletions lib/pact/provider/generators/provider_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'pact/provider/generators'

module Pact
module Provider
class ProviderStateGenerator


# rewrite of https:/DiUS/pact-jvm/blob/master/core/support/src/main/kotlin/au/com/dius/pact/core/support/expressions/ExpressionParser.kt#L27
VALUES_SEPARATOR = ","
START_EXPRESSION = "\${"
END_EXPRESSION = '}'
def parse_expression expression, params

return_string = []

buffer = expression;
# initial value
position = buffer.index(START_EXPRESSION)

while (position && position >= 0)
if (position > 0)
# add string
return_string.push(buffer[0...position])
end
end_position = buffer.index(END_EXPRESSION, position)
if (end_position < 0)
raise "Missing closing brace in expression string \"#{$value}\""
end

variable = ""

if (end_position - position > 2)
expression = params[buffer[position+2...end_position]] || ""
end
return_string.push(expression)

buffer = buffer[end_position + 1...-1]
position = buffer.index(START_EXPRESSION)
end

return_string.join("")
end

def call hash, interaction_context = nil
params = interaction_context.state_params || {}

parse_expression hash["expression"], params
end

def can_generate?(hash)
hash.key?('type') && hash['type'] === 'ProviderState'
end
end
end
end



9 changes: 8 additions & 1 deletion lib/pact/provider/request.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'json'
require 'pact/reification'
require 'pact/shared/null_expectation'
require 'pact/provider/generators'

module Pact
module Provider
Expand All @@ -10,15 +11,21 @@ class Replayable
# See https:/rack/rack/blob/e7d741c6282ca4cf4e01506f5681e6e6b14c0b32/SPEC#L87-89
NO_HTTP_PREFIX = ["CONTENT-TYPE", "CONTENT-LENGTH"]

def initialize expected_request
def initialize expected_request, interaction_context = nil
@expected_request = expected_request
@interaction_context = interaction_context
end

def method
expected_request.method
end

def path
if expected_request.methods.include? :generators
if expected_request.generators["path"]
return Pact::Provider::Generators.execute_generators(expected_request.generators["path"], @interaction_context)
end
end
expected_request.full_path
end

Expand Down
10 changes: 7 additions & 3 deletions lib/pact/provider/rspec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def honour_pactfile pact_source, pact_json, options
pact_uri = pact_source.uri
Pact.configuration.output_stream.puts "INFO: Reading pact at #{pact_uri}"
consumer_contract = Pact::ConsumerContract.from_json(pact_json)

suffix = pact_uri.metadata[:pending] ? " [PENDING]": ""
example_group_description = "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}#{suffix}"
example_group_metadata = { pactfile_uri: pact_uri, pact_criteria: options[:criteria] }
Expand Down Expand Up @@ -77,7 +78,6 @@ def describe_interaction_with_provider_state interaction, options
end

def describe_interaction interaction, options

# pact_uri and pact_interaction are used by
# Pact::Provider::RSpec::PactBrokerFormatter

Expand All @@ -103,8 +103,9 @@ def describe_interaction interaction, options
before do | example |
interaction_context.run_once :before do
Pact.configuration.logger.info "Running example '#{Pact::RSpec.full_description(example)}'"
set_up_provider_states interaction.provider_states, options[:consumer]
replay_interaction interaction, options[:request_customizer]
state_params = set_up_provider_states interaction.provider_states, options[:consumer]
interaction_context.state_params = state_params
replay_interaction interaction, options[:request_customizer], interaction_context
interaction_context.last_response = last_response
end
end
Expand All @@ -129,6 +130,7 @@ def describe_message expected_response, interaction_context
include Pact::RSpec::Matchers
extend Pact::Matchers::Messages


let(:expected_contents) { expected_response.body[:contents].as_json }
let(:response) { interaction_context.last_response }
let(:differ) { Pact.configuration.body_differ_for_content_type diff_content_type }
Expand Down Expand Up @@ -214,6 +216,8 @@ class InteractionContext

attr_accessor :last_response

attr_accessor :state_params

def initialize
@already_run = []
end
Expand Down
12 changes: 9 additions & 3 deletions lib/pact/provider/test_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ module TestMethods
include Pact::Logging
include Rack::Test::Methods

def replay_interaction interaction, request_customizer = nil
request = Request::Replayable.new(interaction.request)
def replay_interaction interaction, request_customizer = nil, interaction_context
request = Request::Replayable.new(interaction.request, interaction_context)
request = request_customizer.call(request, interaction) if request_customizer
args = [request.path, request.body, request.headers]

Expand All @@ -42,11 +42,17 @@ def parse_body_from_response rack_response
end

def set_up_provider_states provider_states, consumer, options = {}
state_params = {};
# If there are no provider state, execute with an nil state to ensure global and base states are executed
Pact.configuration.provider_state_set_up.call(nil, consumer, options) if provider_states.nil? || provider_states.empty?
provider_states.each do | provider_state |
Pact.configuration.provider_state_set_up.call(provider_state.name, consumer, options.merge(params: provider_state.params))
result = Pact.configuration.provider_state_set_up.call(provider_state.name, consumer, options.merge(params: provider_state.params))
if result.is_a?(Hash)
state_params = state_params.merge(result)
end
end

state_params
end

def tear_down_provider_states provider_states, consumer, options = {}
Expand Down

0 comments on commit d5007bb

Please sign in to comment.