diff --git a/lib/pact_broker/matrix/query_builder.rb b/lib/pact_broker/matrix/query_builder.rb new file mode 100644 index 000000000..b707ae56d --- /dev/null +++ b/lib/pact_broker/matrix/query_builder.rb @@ -0,0 +1,100 @@ +module PactBroker + module Matrix + class QueryBuilder + LV = :latest_verification_id_for_pact_version_and_provider_version + LP = :latest_pact_publication_ids_for_consumer_versions + + def self.provider_or_provider_version_or_verification_in(selectors, allow_null_provider_version = false, qualifier) + most_specific_criteria = selectors.collect(&:most_specific_provider_criterion) + + verification_ids = collect_ids(most_specific_criteria, :verification_ids) + provider_version_ids = collect_ids(most_specific_criteria, :pacticipant_version_id) + provider_ids = collect_ids(most_specific_criteria, :pacticipant_id) + + ors = [ + { verification_id: verification_ids }, + { Sequel[qualifier][:provider_version_id] => provider_version_ids }, + { Sequel[qualifier][:provider_id] => provider_ids } + ] + + if allow_null_provider_version + ors << { + Sequel[qualifier][:provider_id] => selectors.collect{ |s| s[:pacticipant_id] }, + Sequel[qualifier][:provider_version_id] => nil + } + end + + Sequel.|(*ors) + end + + def self.consumer_in_pacticipant_ids(selectors) + { consumer_id: selectors.collect(&:pacticipant_id) } + end + + def self.consumer_or_consumer_version_or_pact_publication_in(selectors, qualifier) + most_specific_criteria = selectors.collect(&:most_specific_consumer_criterion) + pact_publication_ids = collect_ids(most_specific_criteria, :pact_publication_ids) + consumer_version_ids = collect_ids(most_specific_criteria, :pacticipant_version_id) + consumer_ids = collect_ids(most_specific_criteria, :pacticipant_id) + + Sequel.|( + { Sequel[qualifier][:pact_publication_id] => pact_publication_ids }, + { Sequel[qualifier][:consumer_version_id] => consumer_version_ids }, + { Sequel[qualifier][:consumer_id] => consumer_ids } + ) + end + + # Some selecters are specified in the query, others are implied (when only one pacticipant is specified, + # the integrations are automatically worked out, and the selectors for these are of type :implied ) + # When there are 3 pacticipants that each have dependencies on each other (A->B, A->C, B->C), the query + # to deploy C (implied A, implied B, specified C) was returning the A->B row because it matched the + # implied selectors as well. + # This extra filter makes sure that every row that is returned actually matches one of the specified + # selectors. + def self.either_consumer_or_provider_was_specified_in_query(selectors, qualifier = nil) + consumer_id_field = qualifier ? Sequel[qualifier][:consumer_id] : CONSUMER_ID + provider_id_field = qualifier ? Sequel[qualifier][:provider_id] : PROVIDER_ID + specified_pacticipant_ids = selectors.select(&:specified?).collect(&:pacticipant_id) + Sequel.|({ consumer_id_field => specified_pacticipant_ids } , { provider_id_field => specified_pacticipant_ids }) + end + + def self.consumer_or_consumer_version_or_provider_or_provider_or_provider_version_match_selector(s) + consumer_or_consumer_version_match = s[:pacticipant_version_id] ? { Sequel[LP][:consumer_version_id] => s[:pacticipant_version_id] } : { Sequel[LP][:consumer_id] => s[:pacticipant_id] } + provider_or_provider_version_match = s[:pacticipant_version_id] ? { Sequel[:lv][:provider_version_id] => s[:pacticipant_version_id] } : { Sequel[LP][:provider_id] => s[:pacticipant_id] } + Sequel.|(consumer_or_consumer_version_match , provider_or_provider_version_match) + end + + def self.all_pacticipant_ids selectors + selectors.collect(&:pacticipant_id) + end + + def self.collect_ids(hashes, key) + ids = hashes.collect{ |s| s[key] }.flatten.compact + # must avoid an empty IN list, or Sequel makes a (0 = 1) clause for us + ids.empty? ? [-1] : ids + end + + def self.collect_the_ids selectors + most_specific_criteria = selectors.collect(&:most_specific_consumer_criterion) + verification_ids = collect_ids(most_specific_criteria, :verification_ids) + pact_publication_ids = collect_ids(most_specific_criteria, :pact_publication_ids) + pacticipant_version_ids = collect_ids(most_specific_criteria, :pacticipant_version_id) + pacticipant_ids = collect_ids(most_specific_criteria, :pacticipant_id) + all_pacticipant_ids = selectors.collect(&:pacticipant_id) + + specified_pacticipant_ids = selectors.select(&:specified?).collect(&:pacticipant_id) + + { + verification_ids: verification_ids, + pact_publication_ids: pact_publication_ids, + consumer_version_ids: pacticipant_version_ids, + provider_version_ids: pacticipant_version_ids, + consumer_ids: pacticipant_ids, + provider_ids: pacticipant_ids, + all_pacticipant_ids: all_pacticipant_ids, + specified_pacticipant_ids: specified_pacticipant_ids + } + end + end + end +end \ No newline at end of file diff --git a/lib/pact_broker/matrix/quick_row.rb b/lib/pact_broker/matrix/quick_row.rb index fb397687a..7a4b336d3 100644 --- a/lib/pact_broker/matrix/quick_row.rb +++ b/lib/pact_broker/matrix/quick_row.rb @@ -21,19 +21,19 @@ module Matrix LV = :latest_verification_id_for_pact_version_and_provider_version LP = :latest_pact_publication_ids_for_consumer_versions - CONSUMER_COLUMNS = [Sequel[:lp][:consumer_id], Sequel[:consumers][:name].as(:consumer_name), Sequel[:lp][:pact_publication_id], Sequel[:lp][:pact_version_id]] - PROVIDER_COLUMNS = [Sequel[:lp][:provider_id], Sequel[:providers][:name].as(:provider_name), Sequel[:lv][:verification_id]] - CONSUMER_VERSION_COLUMNS = [Sequel[:lp][:consumer_version_id], Sequel[:cv][:number].as(:consumer_version_number), Sequel[:cv][:order].as(:consumer_version_order)] + CONSUMER_COLUMNS = [Sequel[LP][:consumer_id], Sequel[:consumers][:name].as(:consumer_name), Sequel[LP][:pact_publication_id], Sequel[LP][:pact_version_id]] + PROVIDER_COLUMNS = [Sequel[LP][:provider_id], Sequel[:providers][:name].as(:provider_name), Sequel[:lv][:verification_id]] + CONSUMER_VERSION_COLUMNS = [Sequel[LP][:consumer_version_id], Sequel[:cv][:number].as(:consumer_version_number), Sequel[:cv][:order].as(:consumer_version_order)] PROVIDER_VERSION_COLUMNS = [Sequel[:lv][:provider_version_id], Sequel[:pv][:number].as(:provider_version_number), Sequel[:pv][:order].as(:provider_version_order)] ALL_COLUMNS = CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PROVIDER_COLUMNS + PROVIDER_VERSION_COLUMNS - LP_LV_JOIN = { Sequel[:lp][:pact_version_id] => Sequel[:lv][:pact_version_id] } - CONSUMER_JOIN = { Sequel[:lp][:consumer_id] => Sequel[:consumers][:id] } - PROVIDER_JOIN = { Sequel[:lp][:provider_id] => Sequel[:providers][:id] } - CONSUMER_VERSION_JOIN = { Sequel[:lp][:consumer_version_id] => Sequel[:cv][:id] } + LP_LV_JOIN = { Sequel[LP][:pact_version_id] => Sequel[:lv][:pact_version_id] } + CONSUMER_JOIN = { Sequel[LP][:consumer_id] => Sequel[:consumers][:id] } + PROVIDER_JOIN = { Sequel[LP][:provider_id] => Sequel[:providers][:id] } + CONSUMER_VERSION_JOIN = { Sequel[LP][:consumer_version_id] => Sequel[:cv][:id] } PROVIDER_VERSION_JOIN = { Sequel[:lv][:provider_version_id] => Sequel[:pv][:id] } - RAW_QUERY = Sequel::Model.db[Sequel.as(LP, :lp)] + RAW_QUERY = Sequel::Model.db[LP] .select(*ALL_COLUMNS) .left_outer_join(LV, LP_LV_JOIN, { table_alias: :lv } ) .join(:pacticipants, CONSUMER_JOIN, { table_alias: :consumers }) @@ -41,6 +41,7 @@ module Matrix .join(:versions, CONSUMER_VERSION_JOIN, { table_alias: :cv }) .left_outer_join(:versions, PROVIDER_VERSION_JOIN, { table_alias: :pv } ) + ALIASED_QUERY = Sequel.as(RAW_QUERY, :quick_rows) class QuickRow < Sequel::Model(ALIASED_QUERY) @@ -101,36 +102,36 @@ class QueryHelper def self.consumer_and_provider_in selectors Sequel.&( Sequel.|( - *consumer_and_maybe_consumer_version_match_any_selector(selectors) + *consumer_or_consumer_version_match_any_selector(selectors) ), Sequel.|( - *provider_and_maybe_provider_version_match_any_selector_or_verification_is_missing(selectors) + *provider_or_provider_version_match_any_selector_or_verification_is_missing(selectors) ), either_consumer_or_provider_was_specified_in_query(selectors) ) end - def self.consumer_and_maybe_consumer_version_match_any_selector(selectors) - selectors.collect { |s| consumer_and_maybe_consumer_version_match_selector(s) } + def self.consumer_or_consumer_version_match_any_selector(selectors) + selectors.collect { |s| most_specific_consumer_criterion(s) } end - def self.consumer_and_maybe_consumer_version_match_selector(s) + def self.most_specific_consumer_criterion(s, qualifier = :quick_rows) if s[:pact_publication_ids] - { PACT_PUBLICATION_ID => s[:pact_publication_ids] } + { Sequel[qualifier][:pact_publication_id] => s[:pact_publication_ids] } elsif s[:pacticipant_version_id] - { CONSUMER_ID => s[:pacticipant_id], CONSUMER_VERSION_ID => s[:pacticipant_version_id] } + { Sequel[qualifier][:consumer_version_id] => s[:pacticipant_version_id] } else - { CONSUMER_ID => s[:pacticipant_id] } + { Sequel[qualifier][:consumer_id] => s[:pacticipant_id] } end end - def self.provider_and_maybe_provider_version_match_selector(s) - if s[:verification_ids] - { VERIFICATION_ID => s[:verification_ids] } - elsif s[:pacticipant_version_id] - { PROVIDER_ID => s[:pacticipant_id], PROVIDER_VERSION_ID => s[:pacticipant_version_id] } + def self.most_specific_provider_criterion(selector, qualifier = :quick_rows) + if selector[:verification_ids] + { Sequel[qualifier][:verification_id] => selector[:verification_ids] } + elsif selector[:pacticipant_version_id] + { Sequel[qualifier][:provider_version_id] => selector[:pacticipant_version_id] } else - { PROVIDER_ID => s[:pacticipant_id] } + { Sequel[qualifier][:provider_id] => selector[:pacticipant_id] } end end @@ -140,9 +141,9 @@ def self.provider_verification_is_missing_for_matching_selector(s) { PROVIDER_ID => s[:pacticipant_id], PROVIDER_VERSION_ID => nil } end - def self.provider_and_maybe_provider_version_match_any_selector_or_verification_is_missing(selectors) + def self.provider_or_provider_version_match_any_selector_or_verification_is_missing(selectors) selectors.collect { |s| - provider_and_maybe_provider_version_match_selector(s) + most_specific_provider_criterion(s) } + selectors.collect { |s| provider_verification_is_missing_for_matching_selector(s) } @@ -155,16 +156,17 @@ def self.provider_and_maybe_provider_version_match_any_selector_or_verification_ # implied selectors as well. # This extra filter makes sure that every row that is returned actually matches one of the specified # selectors. - def self.either_consumer_or_provider_was_specified_in_query(selectors) - specified_pacticipant_ids = selectors.select{ |s| s[:type] == :specified }.collect{ |s| s[:pacticipant_id] } - Sequel.|({ CONSUMER_ID => specified_pacticipant_ids } , { PROVIDER_ID => specified_pacticipant_ids }) + def self.either_consumer_or_provider_was_specified_in_query(selectors, qualifier = nil) + consumer_id_field = qualifier ? Sequel[qualifier][:consumer_id] : CONSUMER_ID + provider_id_field = qualifier ? Sequel[qualifier][:provider_id] : PROVIDER_ID + specified_pacticipant_ids = selectors.select(&:specified?).collect(&:pacticipant_id) + Sequel.|({ consumer_id_field => specified_pacticipant_ids } , { provider_id_field => specified_pacticipant_ids }) end def self.consumer_or_consumer_version_or_provider_or_provider_or_provider_version_match_selector(s) - Sequel.|( - s[:pacticipant_version_id] ? { CONSUMER_VERSION_ID => s[:pacticipant_version_id] } : { CONSUMER_ID => s[:pacticipant_id] }, - s[:pacticipant_version_id] ? { PROVIDER_VERSION_ID => s[:pacticipant_version_id] } : { PROVIDER_ID => s[:pacticipant_id] } - ) + consumer_or_consumer_version_match = s[:pacticipant_version_id] ? { CONSUMER_VERSION_ID => s[:pacticipant_version_id] } : { CONSUMER_ID => s[:pacticipant_id] } + provider_or_provider_version_match = s[:pacticipant_version_id] ? { PROVIDER_VERSION_ID => s[:pacticipant_version_id] } : { PROVIDER_ID => s[:pacticipant_id] } + Sequel.|(consumer_or_consumer_version_match , provider_or_provider_version_match) end end diff --git a/lib/pact_broker/matrix/quick_row_2.rb b/lib/pact_broker/matrix/quick_row_2.rb new file mode 100644 index 000000000..bd5cf2cdf --- /dev/null +++ b/lib/pact_broker/matrix/quick_row_2.rb @@ -0,0 +1,124 @@ +require 'pact_broker/pacts/all_pact_publications' +require 'pact_broker/repositories/helpers' +require 'pact_broker/matrix/query_builder' + +# The difference between this query and the query for QuickRow2 is that +# the left outer join is done on a pre-filtered dataset so that we +# get a row with null verification fields for a pact that has not been verified +# by the particular providers where're interested in, rather than being excluded +# from the dataset. + +module PactBroker + module Matrix + class QuickRow2 < Sequel::Model(:latest_pact_publication_ids_for_consumer_versions) + LV = :latest_verification_id_for_pact_version_and_provider_version + LP = :latest_pact_publication_ids_for_consumer_versions + + LP_LV_JOIN = { Sequel[LP][:pact_version_id] => Sequel[:lv][:pact_version_id] } + CONSUMER_JOIN = { Sequel[LP][:consumer_id] => Sequel[:consumers][:id] } + PROVIDER_JOIN = { Sequel[LP][:provider_id] => Sequel[:providers][:id] } + CONSUMER_VERSION_JOIN = { Sequel[LP][:consumer_version_id] => Sequel[:cv][:id] } + PROVIDER_VERSION_JOIN = { Sequel[:lv][:provider_version_id] => Sequel[:pv][:id] } + + CONSUMER_COLUMNS = [Sequel[LP][:consumer_id], Sequel[:consumers][:name].as(:consumer_name), Sequel[LP][:pact_publication_id], Sequel[LP][:pact_version_id]] + PROVIDER_COLUMNS = [Sequel[LP][:provider_id], Sequel[:providers][:name].as(:provider_name), Sequel[:lv][:verification_id]] + CONSUMER_VERSION_COLUMNS = [Sequel[LP][:consumer_version_id], Sequel[:cv][:number].as(:consumer_version_number), Sequel[:cv][:order].as(:consumer_version_order)] + PROVIDER_VERSION_COLUMNS = [Sequel[:lv][:provider_version_id], Sequel[:pv][:number].as(:provider_version_number), Sequel[:pv][:order].as(:provider_version_order)] + ALL_COLUMNS = CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PROVIDER_COLUMNS + PROVIDER_VERSION_COLUMNS + + SELECT_ALL_COLUMN_ARGS = [:select_all_columns] + ALL_COLUMNS + + dataset_module do + include PactBroker::Repositories::Helpers + + select *SELECT_ALL_COLUMN_ARGS + + def matching_selectors selectors + if selectors.size == 1 + matching_one_selector(selectors.first) + else + matching_multiple_selectors(selectors) + end + end + + # When we have one selector, we need to join ALL the verifications to find out + # what integrations exist + def matching_one_selector(selector) + select_all_columns + .join_verifications + .join_pacticipants_and_pacticipant_versions + .where { + QueryBuilder.consumer_or_consumer_version_or_provider_or_provider_or_provider_version_match_selector(selector) + } + end + + # When the user has specified multiple selectors, we only want to join the verifications for + # the specified selectors. This is because of the behaviour of the left outer join. + # Imagine a pact has been verified by a provider version that was NOT specified in the selectors. + # If we join all the verifications and THEN filter the rows to only show the versions specified + # in the selectors, we won't get a row for that pact, and hence, we won't + # know that it hasn't been verified by the provider version we're interested in. + # Instead, we need to filter the verifications dataset down to only the ones specified in the selectors first, + # and THEN join them to the pacts, so that we get a row for the pact with null provider version + # and verification fields. + def matching_multiple_selectors(selectors) + select_all_columns + .join_verifications_for(selectors) + .join_pacticipants_and_pacticipant_versions + .where { + Sequel.&( + QueryBuilder.consumer_or_consumer_version_or_pact_publication_in(selectors, LP), + QueryBuilder.either_consumer_or_provider_was_specified_in_query(selectors, LP) + ) + } + .from_self(alias: :t9) + .where { + QueryBuilder.provider_or_provider_version_or_verification_in(selectors, true, :t9) + } + end + + def join_pacticipants_and_pacticipant_versions + join_consumers + .join_providers + .join_consumer_versions + .join_provider_versions + end + + def join_consumers + join(:pacticipants, CONSUMER_JOIN, { table_alias: :consumers }) + end + + def join_providers + join(:pacticipants, PROVIDER_JOIN, { table_alias: :providers }) + end + + def join_consumer_versions + join(:versions, CONSUMER_VERSION_JOIN, { table_alias: :cv }) + end + + def join_provider_versions + left_outer_join(:versions, PROVIDER_VERSION_JOIN, { table_alias: :pv } ) + end + + def join_verifications_for(selectors) + left_outer_join(verifications_for(selectors), LP_LV_JOIN, { table_alias: :lv } ) + end + + def join_verifications + left_outer_join(LV, LP_LV_JOIN, { table_alias: :lv } ) + end + + def verifications_for(selectors) + db[LV] + .select(:verification_id, :provider_version_id, :pact_version_id) + .where { + Sequel.&( + QueryBuilder.consumer_in_pacticipant_ids(selectors), + QueryBuilder.provider_or_provider_version_or_verification_in(selectors, false, LV) + ) + } + end + end + end + end +end diff --git a/lib/pact_broker/matrix/repository.rb b/lib/pact_broker/matrix/repository.rb index 2711b1ecc..2169413f9 100644 --- a/lib/pact_broker/matrix/repository.rb +++ b/lib/pact_broker/matrix/repository.rb @@ -9,6 +9,7 @@ require 'pact_broker/matrix/resolved_selector' require 'pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version' require 'pact_broker/pacts/latest_pact_publications_by_consumer_version' +require 'pact_broker/matrix/quick_row_2' module PactBroker module Matrix @@ -81,8 +82,8 @@ def find_compatible_pacticipant_versions selectors # If two or more are specified, just return the integrations that involve the specified pacticipants def find_integrations_for_specified_selectors(resolved_specified_selectors) specified_pacticipant_names = resolved_specified_selectors.collect(&:pacticipant_name) - QuickRow - .pacticipant_names_and_ids + + QuickRow2 .matching_selectors(resolved_specified_selectors) .distinct .all diff --git a/lib/pact_broker/matrix/resolved_selector.rb b/lib/pact_broker/matrix/resolved_selector.rb index a04b3f6b6..ef8671744 100644 --- a/lib/pact_broker/matrix/resolved_selector.rb +++ b/lib/pact_broker/matrix/resolved_selector.rb @@ -65,6 +65,14 @@ def pacticipant_version_number self[:pacticipant_version_number] end + def verification_ids + self[:verification_ids] + end + + def pact_publication_ids + self[:pact_publication_ids] + end + def latest? self[:latest] end @@ -73,6 +81,26 @@ def tag self[:tag] end + def most_specific_provider_criterion + if verification_ids + { verification_ids: verification_ids } + elsif pacticipant_version_id + { pacticipant_version_id: pacticipant_version_id } + else + { pacticipant_id: pacticipant_id } + end + end + + def most_specific_consumer_criterion + if pact_publication_ids + { pact_publication_ids: pact_publication_ids } + elsif pacticipant_version_id + { pacticipant_version_id: pacticipant_version_id } + else + { pacticipant_id: pacticipant_id } + end + end + def latest_tagged? latest? && tag end diff --git a/lib/pact_broker/test/test_data_builder.rb b/lib/pact_broker/test/test_data_builder.rb index 5f7717156..17dc403dc 100644 --- a/lib/pact_broker/test/test_data_builder.rb +++ b/lib/pact_broker/test/test_data_builder.rb @@ -73,16 +73,23 @@ def create_condor self end - def create_pact_with_hierarchy consumer_name = "Consumer", consumer_version = "1.2.3", provider_name = "Provider", json_content = default_json_content + def create_pact_with_hierarchy consumer_name = "Consumer", consumer_version_number = "1.2.3", provider_name = "Provider", json_content = default_json_content use_consumer(consumer_name) create_consumer(consumer_name) if !consumer use_provider(provider_name) create_provider provider_name if !provider - create_consumer_version consumer_version + use_consumer_version(consumer_version_number) + create_consumer_version(consumer_version_number) if !consumer_version create_pact json_content: json_content self end + def create_pact_with_verification consumer_name = "Consumer", consumer_version = "1.0.#{model_counter}", provider_name = "Provider", provider_version = "1.0.#{model_counter}" + create_pact_with_hierarchy(consumer_name, consumer_version, provider_name) + create_verification(number: model_counter, provider_version: provider_version) + self + end + def create_version_with_hierarchy pacticipant_name, pacticipant_version pacticipant = pacticipant_service.create(:name => pacticipant_name) version = PactBroker::Domain::Version.create(:number => pacticipant_version, :pacticipant => pacticipant) diff --git a/spec/lib/pact_broker/matrix/integration_spec.rb b/spec/lib/pact_broker/matrix/integration_spec.rb index 82712707e..88fb5f719 100644 --- a/spec/lib/pact_broker/matrix/integration_spec.rb +++ b/spec/lib/pact_broker/matrix/integration_spec.rb @@ -4,6 +4,7 @@ module PactBroker module Matrix describe Service do let(:td) { TestDataBuilder.new } + describe "find" do subject { Service.find(selectors, options) } @@ -236,6 +237,47 @@ module Matrix expect(subject.deployment_status_summary.deployable?).to be true end end + + describe "when two applications have pacts with each other (nureva use case)" do + # ServiceA v 1 has been verified by ServiceB v 100 + # but ServiceB v 100 has only been verified by ServiceA v 99. + # It's missing a verification from ServiceA v1. + before do + td.create_pact_with_verification("ServiceB", "100", "ServiceA", "99") + .create_pact_with_verification("ServiceA", "1", "ServiceB", "100") + end + + context "when both application versions are specified explictly" do + let(:selectors) do + [ + { pacticipant_name: "ServiceA", pacticipant_version_number: "1" }, + { pacticipant_name: "ServiceB", pacticipant_version_number: "100" } + ] + end + + let(:options) { { latestby: "cvpv" } } + + it "does not allow the two apps to be deployed together" do + expect(subject.deployment_status_summary.deployable?).to_not be true + end + end + + context "when only one application is specified" do + let(:selectors) do + [ + { pacticipant_name: "ServiceB", pacticipant_version_number: "100" } + ] + end + + let(:options) { { latestby: "cvp", latest: true } } + + it "does not allow the two apps to be deployed together" do + tp subject, :consumer_name, :consumer_version_number, :provider_name, :provider_version_number + puts subject.deployment_status_summary.reasons + expect(subject.deployment_status_summary.deployable?).to_not be true + end + end + end end end end diff --git a/spec/lib/pact_broker/matrix/quick_row_spec.rb b/spec/lib/pact_broker/matrix/quick_row_spec.rb index 2fe7c1641..fdb4d484e 100644 --- a/spec/lib/pact_broker/matrix/quick_row_spec.rb +++ b/spec/lib/pact_broker/matrix/quick_row_spec.rb @@ -1,4 +1,6 @@ require 'pact_broker/matrix/quick_row' +require 'pact_broker/matrix/resolved_selector' +require 'pact_broker/matrix/quick_row_2' module PactBroker module Matrix @@ -18,7 +20,7 @@ module Matrix .create_pact end - it "behaves like a Row, except smarter" do + it "behaves like a Row, except quicker" do a_id = QuickRow.db[:pacticipants].where(name: "A").select(:id).single_record[:id] rows = QuickRow.consumer_id(a_id).eager(:consumer).eager(:verification).all expect(rows.first.consumer).to be rows.last.consumer @@ -26,6 +28,76 @@ module Matrix expect(rows.first.consumer_name).to_not be nil expect(rows.first.provider_name).to_not be nil end + + describe "new_query" do + # ServiceA v 5 has been verified by ServiceB v 100 + # but ServiceB v 100 has only been verified by ServiceA v 99. + # It's missing a verification from ServiceA v5. + before do + td.create_pact_with_verification("ServiceA", "5", "ServiceB", "100") + .create_pact_with_verification("ServiceB", "100", "ServiceA", "99") + .create_pact_with_verification("ServiceA", "5", "ServiceZ", "3") + end + + def shorten_row row + "#{row[:consumer_name]}#{row[:consumer_version_number]} #{row[:provider_name]}#{row[:provider_version_number] || '?'}" + end + + def shorten_rows rows + rows.collect{ |r| shorten_row(r) } + end + + let!(:pact_a) { td.create_pact_with_verification("ServiceA", "5", "ServiceB", "100").and_return(:pact) } + let!(:verification_a) { td.and_return(:verification) } + let!(:pact_b) { td.create_pact_with_verification("ServiceB", "100", "ServiceA", "99").and_return(:pact) } + let!(:verification_b) { td.and_return(:verification) } + + let(:service_a) { PactBroker::Domain::Pacticipant.where(name: "ServiceA").single_record } + let(:service_b) { PactBroker::Domain::Pacticipant.where(name: "ServiceB").single_record } + let(:version_a) { PactBroker::Domain::Version.where(number: "5").single_record } + let(:version_b) { PactBroker::Domain::Version.where(number: "100").single_record } + + let(:selectors) do + [ + PactBroker::Matrix::ResolvedSelector.for_pacticipant_and_version(service_a, version_a, nil, nil, {}, :specified), + PactBroker::Matrix::ResolvedSelector.for_pacticipant_and_version(service_b, version_b, nil, nil, {}, :specified) + ] + end + + subject { QuickRow2.matching_selectors(selectors).all } + let(:subject_hashes) { subject.collect(&:to_hash) } + + it "" do + tp subject_hashes, :consumer_id, :consumer_version_number, :provider_id, :provider_version_id + + expect(subject.size).to be 2 + expect(subject_hashes.collect(&:to_hash)).to include_hash_matching( + consumer_name: "ServiceA", + consumer_version_number: "5", + provider_name: "ServiceB", + provider_version_number: "100" + ) + expect(subject_hashes.collect(&:to_hash)).to include_hash_matching( + consumer_name: "ServiceB", + consumer_version_number: "100", + provider_name: "ServiceA", + provider_version_number: nil + ) + end + + context "with one selector" do + let(:selectors) do + [ + PactBroker::Matrix::ResolvedSelector.for_pacticipant_and_version(service_b, version_b, nil, nil, {}, :specified), + ] + end + + it "" do + tp subject_hashes, :consumer_name, :consumer_version_number, :provider_name, :provider_version_number + expect(subject.size).to be 2 + end + end + end end end end diff --git a/spec/lib/pact_broker/matrix/repository_spec.rb b/spec/lib/pact_broker/matrix/repository_spec.rb index c7fa2df3f..0e8f20152 100644 --- a/spec/lib/pact_broker/matrix/repository_spec.rb +++ b/spec/lib/pact_broker/matrix/repository_spec.rb @@ -633,7 +633,7 @@ def shorten_rows rows end end - context "when the latest version is specified for a provider without a tag but the latest known version for a provider does not have a verification" do + context "when the latest version is specified for a provider ignoring tags but the latest known version for a provider does not have a verification" do before do td.create_pact_with_hierarchy("A", "1.2.3", "B") .create_verification(provider_version: "1.0.0") @@ -647,7 +647,7 @@ def shorten_rows rows ] end - it "returns no data - this may be confusing. Might need to re-think this logic." do + it "returns no data" do expect(subject.size).to eq 0 end end