diff --git a/lib/pact/provider/request.rb b/lib/pact/provider/request.rb index ad4b1998..89e196ef 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/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, state_params = nil @expected_request = expected_request + @state_params = state_params end def method @@ -19,7 +21,7 @@ def method end def path - expected_request.full_path + Pact::Generators.apply_generators(expected_request, "path", expected_request.full_path, @state_params) end def body @@ -27,17 +29,20 @@ def body when String then expected_request.body when NullExpectation then '' else - reified_body + Pact::Generators.apply_generators(expected_request, "body", reified_body, @state_params) end end def headers request_headers = {} return request_headers if expected_request.headers.is_a?(Pact::NullExpectation) + expected_request.headers.each do |key, value| - request_headers[rack_request_header_for(key)] = Pact::Reification.from_term(value) + request_headers[key] = Pact::Reification.from_term(value) end - request_headers + + request_headers = Pact::Generators.apply_generators(expected_request, "header", request_headers, @state_params) + request_headers.map{ |key,value| [rack_request_header_for(key), value]}.to_h end private diff --git a/lib/pact/provider/rspec.rb b/lib/pact/provider/rspec.rb index a6e7372c..b498def5 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] + provider_states_result = set_up_provider_states interaction.provider_states, options[:consumer] + state_params = provider_states_result[interaction.provider_state]; + replay_interaction interaction, options[:request_customizer], state_params 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 } diff --git a/lib/pact/provider/test_methods.rb b/lib/pact/provider/test_methods.rb index f27466af..1619633d 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, state_params = nil + request = Request::Replayable.new(interaction.request, state_params) 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 = {} + provider_states_result = {}; # 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) + provider_states_result[provider_state.name] = result + end end + + provider_states_result end def tear_down_provider_states provider_states, consumer, options = {} diff --git a/pact.gemspec b/pact.gemspec index 61481cf7..38d459c7 100644 --- a/pact.gemspec +++ b/pact.gemspec @@ -30,8 +30,10 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency 'rack-test', '>= 0.6.3', '< 3.0.0' gem.add_runtime_dependency 'thor', '>= 0.20', '< 2.0' gem.add_runtime_dependency "rainbow", '~> 3.1' + gem.add_runtime_dependency 'string_pattern', '~> 2.0' + gem.add_runtime_dependency 'jsonpath', '~> 1.0' - gem.add_runtime_dependency 'pact-support', '~> 1.16', '>= 1.16.9' + gem.add_runtime_dependency 'pact-support', '~> 1.19', '>= 1.19.0' gem.add_runtime_dependency 'pact-mock_service', '~> 3.0', '>= 3.3.1' gem.add_development_dependency 'rake', '~> 13.0' diff --git a/spec/lib/pact/provider/generators_spec.rb b/spec/lib/pact/provider/generators_spec.rb new file mode 100644 index 00000000..575dae80 --- /dev/null +++ b/spec/lib/pact/provider/generators_spec.rb @@ -0,0 +1,68 @@ +require 'pact/generators' +require 'pact/provider/request' + +describe Pact::Generators do + it 'apply_generators for path' do + expected_request = Pact::Request::Expected.from_hash({ + method: 'GET', + path: '/path/1', + generators: { + 'path' => { + 'type' => 'ProviderState', + 'expression' => '/path/${itemID}' + } + } + }) + state_params = { + 'itemID' => 2 + } + request = Pact::Provider::Request::Replayable.new(expected_request, state_params) + expect(request.path).to eq('/path/2') + end + + it 'apply_generators for headers' do + expected_request = Pact::Request::Expected.from_hash({ + method: 'GET', + path: '/path/1', + headers: { + 'Authorization' => 'Bearer 123' + }, + generators: { + 'header' => { + '$.Authorization' => { + 'expression' => 'Bearer ${accessToken}', + 'type' => 'ProviderState' + } + } + } + }) + state_params = { + 'accessToken' => 'ABC' + } + request = Pact::Provider::Request::Replayable.new(expected_request, state_params) + expect(request.headers).to eq({ + 'HTTP_AUTHORIZATION' => 'Bearer ABC' + }) + end + + it 'apply_generators for body' do + expected_request = Pact::Request::Expected.from_hash({ + method: 'GET', + path: '/path/1', + body: { + 'result' => [ + '12345F' + ] + }, + generators: { + 'body' => { + '$.result[0]' => { + 'type' => 'RandomHexadecimal' + } + } + } + }) + request = Pact::Provider::Request::Replayable.new(expected_request) + expect(request.body['result'][0].length).to eq(8) + end +end