diff --git a/lib/pact/consumer_contract/consumer_contract.rb b/lib/pact/consumer_contract/consumer_contract.rb index e445792..517559a 100644 --- a/lib/pact/consumer_contract/consumer_contract.rb +++ b/lib/pact/consumer_contract/consumer_contract.rb @@ -9,6 +9,7 @@ require 'pact/consumer_contract/service_consumer' require 'pact/consumer_contract/service_provider' require 'pact/consumer_contract/interaction' +require 'pact/consumer_contract/message' require 'pact/consumer_contract/pact_file' module Pact @@ -31,10 +32,18 @@ def initialize(attributes = {}) def self.from_hash(hash) hash = symbolize_keys(hash) + interactions = if hash[:interactions] + hash[:interactions].collect { |hash| Interaction.from_hash(hash)} + elsif hash[:messages] + hash[:messages].collect { |hash| Message.from_hash(hash)} + else + [] + end + new( :consumer => ServiceConsumer.from_hash(hash[:consumer]), :provider => ServiceProvider.from_hash(hash[:provider]), - :interactions => hash[:interactions].collect { |hash| Interaction.from_hash(hash)} + :interactions => interactions ) end @@ -54,9 +63,9 @@ def self.maintain_backwards_compatiblity_with_producer_keys string def find_interaction criteria interactions = find_interactions criteria if interactions.size == 0 - raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}." + raise Pact::Error.new("Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}.") elsif interactions.size > 1 - raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}." + raise Pact::Error.new("Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}.") end interactions.first end diff --git a/lib/pact/consumer_contract/message.rb b/lib/pact/consumer_contract/message.rb new file mode 100644 index 0000000..1ab6c72 --- /dev/null +++ b/lib/pact/consumer_contract/message.rb @@ -0,0 +1,67 @@ +require 'pact/consumer_contract/message/content' +require 'pact/symbolize_keys' +require 'pact/shared/active_support_support' +require 'pact/matching_rules' +require 'pact/errors' + +module Pact + class Message + include ActiveSupportSupport + include SymbolizeKeys + + attr_accessor :description, :content, :provider_state + + def initialize attributes = {} + @description = attributes[:description] + @request = attributes[:content] + @provider_state = attributes[:provider_state] || attributes[:providerState] + end + + def self.from_hash hash + content_hash = Pact::MatchingRules.merge(hash['content'], hash['content']['matchingRules']) + content = Pact::Message::Content.new(content_hash) + new(symbolize_keys(hash).merge(content: content)) + end + + def to_hash + { + description: description, + provider_state: provider_state, + content: content.to_hash, + } + end + + def validate! + raise Pact::InvalidMessageError.new(self) unless description && content + end + + def == other + other.is_a?(Message) && to_hash == other.to_hash + end + + def matches_criteria? criteria + criteria.each do | key, value | + unless match_criterion self.send(key.to_s), value + return false + end + end + true + end + + def match_criterion target, criterion + target == criterion || (criterion.is_a?(Regexp) && criterion.match(target)) + end + + def eq? other + self == other + end + + def description_with_provider_state_quoted + provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\"" + end + + def to_s + to_hash.to_s + end + end +end diff --git a/lib/pact/consumer_contract/message/content.rb b/lib/pact/consumer_contract/message/content.rb new file mode 100644 index 0000000..d337e2d --- /dev/null +++ b/lib/pact/consumer_contract/message/content.rb @@ -0,0 +1,9 @@ +module Pact + class Message + class Content < Hash + include ActiveSupportSupport + include SymbolizeKeys + + end + end +end diff --git a/lib/pact/errors.rb b/lib/pact/errors.rb index c591895..d48ee32 100644 --- a/lib/pact/errors.rb +++ b/lib/pact/errors.rb @@ -2,6 +2,9 @@ module Pact class Error < ::StandardError end + class InvalidMessageError < Error + end + # Raised when the interaction is not defined correctly class InvalidInteractionError < Error def initialize(interaction) diff --git a/spec/lib/pact/consumer_contract/consumer_contract_spec.rb b/spec/lib/pact/consumer_contract/consumer_contract_spec.rb index 28e6492..481a15e 100644 --- a/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +++ b/spec/lib/pact/consumer_contract/consumer_contract_spec.rb @@ -3,37 +3,66 @@ module Pact describe ConsumerContract do - describe ".from_json" do + let(:loaded_pact) { ConsumerContract.from_json(string) } - context "when the top level object is a ConsumerContract" do - let(:string) { '{"interactions":[{"request": {"path":"/path", "method" : "get"}, "response": {"status" : 200}}], "consumer": {"name" : "Bob"} , "provider": {"name" : "Mary"} }' } - it "should create a Pact" do - expect(loaded_pact).to be_instance_of ConsumerContract - end + context "with an HTTP contract" do + context "when the top level object is a ConsumerContract" do + let(:string) { '{"interactions":[{"request": {"path":"/path", "method" : "get"}, "response": {"status" : 200}}], "consumer": {"name" : "Bob"} , "provider": {"name" : "Mary"} }' } - it "should have interactions" do - expect(loaded_pact.interactions).to be_instance_of Array - end + it "should create a Pact" do + expect(loaded_pact).to be_instance_of ConsumerContract + end - it "should have a consumer" do - expect(loaded_pact.consumer).to be_instance_of Pact::ServiceConsumer + it "should have interactions" do + expect(loaded_pact.interactions).to be_instance_of Array + end + + it "should have a consumer" do + expect(loaded_pact.consumer).to be_instance_of Pact::ServiceConsumer + end + + it "should have a provider" do + expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider + end end - it "should have a provider" do - expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider + context "with old 'producer' key" do + let(:string) { File.read('./spec/support/a_consumer-a_producer.json')} + it "should create a Pact" do + expect(loaded_pact).to be_instance_of ConsumerContract + end + + it "should have interactions" do + expect(loaded_pact.interactions).to be_instance_of Array + end + + it "should have a consumer" do + expect(loaded_pact.consumer).to be_instance_of Pact::ServiceConsumer + end + + it "should have a provider" do + expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider + expect(loaded_pact.provider.name).to eq "an old producer" + end + + it "should have a provider_state" do + expect(loaded_pact.interactions.first.provider_state).to eq 'state one' + end end end - context "with old 'producer' key" do - let(:string) { File.read('./spec/support/a_consumer-a_producer.json')} + context "with a Message contract" do + let(:string) { '{"messages":[{"content": {"foo": "bar"}}], "consumer": {"name" : "Bob"} , "provider": {"name" : "Mary"}}' } + it "should create a Pact" do expect(loaded_pact).to be_instance_of ConsumerContract end - it "should have interactions" do + it "should have messages" do expect(loaded_pact.interactions).to be_instance_of Array + expect(loaded_pact.interactions.first).to be_instance_of Pact::Message end it "should have a consumer" do @@ -42,12 +71,8 @@ module Pact it "should have a provider" do expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider - expect(loaded_pact.provider.name).to eq "an old producer" end - it "should have a provider_state" do - expect(loaded_pact.interactions.first.provider_state).to eq 'state one' - end end end