From 4471df3e07ef6d2fe664436a555a9e99afbb9c7a Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Mon, 1 Oct 2018 21:13:27 +1000 Subject: [PATCH] feat(v3): parse array of provider states with params --- lib/pact/consumer_contract/interaction.rb | 20 ++++--- .../interaction_v2_parser.rb | 8 ++- .../interaction_v3_parser.rb | 14 ++++- lib/pact/consumer_contract/provider_state.rb | 34 ++++++++++++ lib/pact/consumer_contract/query.rb | 2 - lib/pact/consumer_contract/query_hash.rb | 4 ++ .../consumer_contract/interaction_spec.rb | 5 +- .../interaction_v2_parser_spec.rb | 54 +++++++++++++++++++ .../interaction_v3_parser_spec.rb | 48 +++++++++++++++++ spec/support/factories.rb | 5 ++ 10 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 lib/pact/consumer_contract/provider_state.rb create mode 100644 spec/lib/pact/consumer_contract/interaction_v2_parser_spec.rb create mode 100644 spec/lib/pact/consumer_contract/interaction_v3_parser_spec.rb diff --git a/lib/pact/consumer_contract/interaction.rb b/lib/pact/consumer_contract/interaction.rb index c000933..86fa629 100644 --- a/lib/pact/consumer_contract/interaction.rb +++ b/lib/pact/consumer_contract/interaction.rb @@ -5,13 +5,14 @@ module Pact class Interaction include ActiveSupportSupport - attr_accessor :description, :request, :response, :provider_state + attr_accessor :description, :request, :response, :provider_state, :provider_states def initialize attributes = {} @description = attributes[:description] @request = attributes[:request] @response = attributes[:response] @provider_state = attributes[:provider_state] || attributes[:providerState] + @provider_states = attributes[:provider_states] end def self.from_hash hash, options = {} @@ -19,12 +20,17 @@ def self.from_hash hash, options = {} end def to_hash - { - description: description, - provider_state: provider_state, - request: request.to_hash, - response: response.to_hash - } + h = { description: description } + + if provider_states + h[:provider_states] = provider_states.collect(&:to_hash) + else + h[:provider_state] = provider_state + end + + h[:request] = request.to_hash + h[:response] = response.to_hash + h end def http? diff --git a/lib/pact/consumer_contract/interaction_v2_parser.rb b/lib/pact/consumer_contract/interaction_v2_parser.rb index 6804829..bf4c505 100644 --- a/lib/pact/consumer_contract/interaction_v2_parser.rb +++ b/lib/pact/consumer_contract/interaction_v2_parser.rb @@ -1,5 +1,6 @@ require 'pact/consumer_contract/request' require 'pact/consumer_contract/response' +require 'pact/consumer_contract/provider_state' require 'pact/symbolize_keys' require 'pact/matching_rules' require 'pact/errors' @@ -12,7 +13,8 @@ class InteractionV2Parser def self.call hash, options request = parse_request(hash['request'], options) response = parse_response(hash['response'], options) - Interaction.new(symbolize_keys(hash).merge(request: request, response: response)) + provider_states = parse_provider_states(hash['providerState'] || hash['provider_state']) + Interaction.new(symbolize_keys(hash).merge(request: request, response: response, provider_states: provider_states)) end def self.parse_request request_hash, options @@ -24,5 +26,9 @@ def self.parse_response response_hash, options response_hash = Pact::MatchingRules.merge(response_hash, response_hash['matchingRules'], options) Pact::Response.from_hash(response_hash) end + + def self.parse_provider_states provider_state_name + provider_state_name ? [Pact::ProviderState.new(provider_state_name)] : [] + end end end diff --git a/lib/pact/consumer_contract/interaction_v3_parser.rb b/lib/pact/consumer_contract/interaction_v3_parser.rb index 18d129a..2ca9b29 100644 --- a/lib/pact/consumer_contract/interaction_v3_parser.rb +++ b/lib/pact/consumer_contract/interaction_v3_parser.rb @@ -1,5 +1,6 @@ require 'pact/consumer_contract/request' require 'pact/consumer_contract/response' +require 'pact/consumer_contract/provider_state' require 'pact/symbolize_keys' require 'pact/matching_rules' require 'pact/errors' @@ -13,7 +14,12 @@ class InteractionV3Parser def self.call hash, options request = parse_request(hash['request'], options) response = parse_response(hash['response'], options) - Interaction.new(symbolize_keys(hash).merge(request: request, response: response)) + provider_states = parse_provider_states(hash['providerStates']) + provider_state = provider_states.any? ? provider_states.first.name : nil + if provider_states && provider_states.size > 1 + Pact.configuration.error_stream.puts("WARN: Currently only 1 provider state is supported. Ignoring ") + end + Interaction.new(symbolize_keys(hash).merge(request: request, response: response, provider_states: provider_states, provider_state: provider_state)) end def self.parse_request request_hash, options @@ -57,5 +63,11 @@ def self.parse_response_with_string_body response_hash, response_matching_rules, string_with_matching_rules = StringWithMatchingRules.new(response_hash['body'], options[:pact_specification_version], response_matching_rules) Pact::Response.from_hash(response_hash.merge('body' => string_with_matching_rules)) end + + def self.parse_provider_states provider_states + (provider_states || []).collect do | provider_state_hash | + Pact::ProviderState.new(provider_state_hash['name'], provider_state_hash['params']) + end + end end end diff --git a/lib/pact/consumer_contract/provider_state.rb b/lib/pact/consumer_contract/provider_state.rb new file mode 100644 index 0000000..f65db2e --- /dev/null +++ b/lib/pact/consumer_contract/provider_state.rb @@ -0,0 +1,34 @@ +module Pact + class ProviderState + + attr_reader :name, :params + + def initialize name, params = {} + @name = name + @params = params + end + + def self.from_hash(hash) + new(hash["name"], hash["params"]) + end + + def ==(other) + other.is_a?(Pact::ProviderState) && other.name == self.name && other.params == self.params + end + + def to_hash + { + "name" => name, + "params" => params + } + end + + def to_json(opts = {}) + as_json(opts).to_json(opts) + end + + def as_json(opts = {}) + to_hash + end + end +end diff --git a/lib/pact/consumer_contract/query.rb b/lib/pact/consumer_contract/query.rb index e8b888c..ab191d2 100644 --- a/lib/pact/consumer_contract/query.rb +++ b/lib/pact/consumer_contract/query.rb @@ -3,7 +3,6 @@ module Pact class Query - def self.create query if query.is_a? Hash Pact::QueryHash.new(query) @@ -11,6 +10,5 @@ def self.create query Pact::QueryString.new(query) end end - end end diff --git a/lib/pact/consumer_contract/query_hash.rb b/lib/pact/consumer_contract/query_hash.rb index 7f5eb79..2e7d9c6 100644 --- a/lib/pact/consumer_contract/query_hash.rb +++ b/lib/pact/consumer_contract/query_hash.rb @@ -47,6 +47,10 @@ def empty? @hash && @hash.empty? end + def to_hash + @hash + end + private def convert_to_hash_of_arrays(query) diff --git a/spec/lib/pact/consumer_contract/interaction_spec.rb b/spec/lib/pact/consumer_contract/interaction_spec.rb index 08fd7f6..0fda68d 100644 --- a/spec/lib/pact/consumer_contract/interaction_spec.rb +++ b/spec/lib/pact/consumer_contract/interaction_spec.rb @@ -38,12 +38,14 @@ module Consumer describe "matches_criteria?" do subject { InteractionFactory.create(:description => 'a request for food') } + context "by description" do context "when the interaction matches" do it "returns true" do expect(subject.matches_criteria?(:description => /request.*food/)).to be true end end + context "when the interaction does not match" do it "returns false" do expect(subject.matches_criteria?(:description => /blah/)).to be false @@ -52,10 +54,7 @@ module Consumer end end - - describe "request_modifies_resource_without_checking_response_body?" do - let(:interaction) { Interaction.new(request: request, response: response)} subject { interaction.request_modifies_resource_without_checking_response_body?} diff --git a/spec/lib/pact/consumer_contract/interaction_v2_parser_spec.rb b/spec/lib/pact/consumer_contract/interaction_v2_parser_spec.rb new file mode 100644 index 0000000..dd8c948 --- /dev/null +++ b/spec/lib/pact/consumer_contract/interaction_v2_parser_spec.rb @@ -0,0 +1,54 @@ +require 'pact/consumer_contract/interaction_v2_parser' + +module Pact + describe InteractionV2Parser do + describe ".call" do + let(:interaction_hash) do + { + "description" => "description", + "request" => { "method" => "GET", "path" => "/" }, + "response" => { "status" => 200 }, + "providerState" => "foo" + } + end + + let(:options) do + { + pact_specification_version: Pact::SpecificationVersion.new("3.0") + } + end + + subject { InteractionV2Parser.call(interaction_hash, options) } + + describe "provider_states" do + it "returns an array of provider states with size 1" do + expect(subject.provider_states.size).to eq 1 + end + + it "sets the name of the provider state to the string provided" do + expect(subject.provider_states.first.name) + end + + it "sets the params to an empty hash" do + expect(subject.provider_states.first.params).to eq({}) + end + + context "when the providerState is nil" do + before do + interaction_hash["providerState"] = nil + end + + it "returns an empty list" do + expect(subject.provider_states).to be_empty + end + end + end + + describe "provider_state" do + it "sets the name from the hash" do + expect(subject.provider_state).to eq "foo" + end + end + end + end +end diff --git a/spec/lib/pact/consumer_contract/interaction_v3_parser_spec.rb b/spec/lib/pact/consumer_contract/interaction_v3_parser_spec.rb new file mode 100644 index 0000000..c40b8c0 --- /dev/null +++ b/spec/lib/pact/consumer_contract/interaction_v3_parser_spec.rb @@ -0,0 +1,48 @@ +require 'pact/consumer_contract/interaction_v3_parser' + +module Pact + describe InteractionV3Parser do + describe ".call" do + + let(:interaction_hash) do + { + "description" => "description", + "request" => { "method" => "GET", "path" => "/" }, + "response" => { "status" => 200 }, + "providerStates" => [{ + "name" => "foo", + "params" => {"a" => "b"} + }] + } + end + + let(:options) do + { + pact_specification_version: Pact::SpecificationVersion.new("3.0") + } + end + + subject { InteractionV3Parser.call(interaction_hash, options) } + + describe "provider_states" do + it "parses the array of provider states" do + expect(subject.provider_states.size).to eq 1 + end + + it "parses the name of each" do + expect(subject.provider_states.first.name) + end + + it "parses the params of each" do + expect(subject.provider_states.first.params).to eq "a" => "b" + end + end + + describe "provider_state" do + it "sets the provider_state string to the name of the first providerState for backwards compatibility while we implement v3" do + expect(subject.provider_state).to eq "foo" + end + end + end + end +end diff --git a/spec/support/factories.rb b/spec/support/factories.rb index 2c9b325..22d07d8 100644 --- a/spec/support/factories.rb +++ b/spec/support/factories.rb @@ -40,6 +40,11 @@ def self.create hash = {} 'body' => {a: 'response body'} } } + + if hash.key?(:provider_states) || hash.key?('provider_states') + defaults.delete('provider_state') + end + Pact::Interaction.from_hash(stringify_keys(deep_merge(defaults, stringify_keys(hash)))) end end