From 7a1cf3b24746554d71d8157b475016355cd87a83 Mon Sep 17 00:00:00 2001 From: Honza2 Date: Sun, 23 Feb 2020 23:55:43 +0100 Subject: [PATCH] feat(generators): Pass context and add ProviderState generator --- lib/pact/provider/generators.rb | 26 +++++++++ .../provider/generators/provider_state.rb | 58 +++++++++++++++++++ lib/pact/provider/request.rb | 9 ++- lib/pact/provider/rspec.rb | 10 +++- lib/pact/provider/test_methods.rb | 12 +++- 5 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 lib/pact/provider/generators.rb create mode 100644 lib/pact/provider/generators/provider_state.rb diff --git a/lib/pact/provider/generators.rb b/lib/pact/provider/generators.rb new file mode 100644 index 00000000..bbafd09a --- /dev/null +++ b/lib/pact/provider/generators.rb @@ -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 diff --git a/lib/pact/provider/generators/provider_state.rb b/lib/pact/provider/generators/provider_state.rb new file mode 100644 index 00000000..fc86b67d --- /dev/null +++ b/lib/pact/provider/generators/provider_state.rb @@ -0,0 +1,58 @@ +require 'pact/provider/generators' + +module Pact + module Provider + class ProviderStateGenerator + + + # rewrite of https://github.com/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 + + + diff --git a/lib/pact/provider/request.rb b/lib/pact/provider/request.rb index ad4b1998..3701421a 100644 --- a/lib/pact/provider/request.rb +++ b/lib/pact/provider/request.rb @@ -1,6 +1,7 @@ require 'json' require 'pact/reification' require 'pact/shared/null_expectation' +require 'pact/provider/generators' module Pact module Provider @@ -10,8 +11,9 @@ class Replayable # See https://github.com/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 @@ -19,6 +21,11 @@ def 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 diff --git a/lib/pact/provider/rspec.rb b/lib/pact/provider/rspec.rb index a6e7372c..57ed8acc 100644 --- a/lib/pact/provider/rspec.rb +++ b/lib/pact/provider/rspec.rb @@ -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] } @@ -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 @@ -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 @@ -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 } @@ -214,6 +216,8 @@ class InteractionContext attr_accessor :last_response + attr_accessor :state_params + def initialize @already_run = [] end diff --git a/lib/pact/provider/test_methods.rb b/lib/pact/provider/test_methods.rb index f27466af..94e8207c 100644 --- a/lib/pact/provider/test_methods.rb +++ b/lib/pact/provider/test_methods.rb @@ -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] @@ -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 = {}