require 'json' require 'pact_broker/client/error' module PactBroker module Client class PactMergeError < PactBroker::Client::Error; end module MergePacts extend self def call pact_hashes pact_hashes.reduce{|p1, p2| merge(p1, p2) } end # Accepts two hashes representing pacts, outputs a merged hash # Does not make any guarantees about order of interactions def merge original, additional new_pact = JSON.parse(original.to_json, symbolize_names: true) merge_interactions_or_messages(new_pact, original, additional, :interactions) merge_interactions_or_messages(new_pact, original, additional, :messages) new_pact end private def merge_interactions_or_messages(new_pact, original, additional, key) return unless additional[key] || original[key] additional_messages_or_interactions = additional[key] || [] original_messages_or_interactions = original[key] || [] new_pact[key] ||= [] additional_messages_or_interactions.each do |new_interaction| # check to see if this interaction matches an existing interaction overwrite_index = original_messages_or_interactions.find_index do |original_interaction| same_description_and_state?(original_interaction, new_interaction) end # overwrite existing interaction if a match is found, otherwise appends the new interaction if overwrite_index if new_interaction == original_messages_or_interactions[overwrite_index] new_pact[key][overwrite_index] = new_interaction else raise PactMergeError, almost_duplicate_message(original_messages_or_interactions[overwrite_index], new_interaction) end else new_pact[key] << new_interaction end end end def almost_duplicate_message(original, new_interaction) "Two interactions have been found with same description (#{new_interaction[:description].inspect}) and provider state (#{(new_interaction[:providerState] || new_interaction[:providerStates]).inspect}) but a different request or response. " + "Please use a different description or provider state, or hard-code any random data.\n\n" + original.to_json + "\n\n" + new_interaction.to_json end def same_description_and_state? original, additional original[:description] == additional[:description] && ( (original[:providerState] && original[:providerState] == additional[:providerState]) || (original[:providerStates] && original[:providerStates] == additional[:providerStates]) ) end end end end