diff --git a/lib/pact_broker/domain/pact.rb b/lib/pact_broker/domain/pact.rb index 79d27e4f0..4b5af7f0b 100644 --- a/lib/pact_broker/domain/pact.rb +++ b/lib/pact_broker/domain/pact.rb @@ -90,7 +90,9 @@ def pact_publication_id end def select_pending_provider_version_tags(provider_version_tags) - provider_version_tags - db_model.pact_version.select_provider_tags_with_successful_verifications(provider_version_tags) + tags_with_successful_verifications_from_that_branch = db_model.pact_version.select_provider_tags_with_successful_verifications(provider_version_tags) + tags_with_previous_successful_verifications_from_other_branches = db_model.pact_version.select_provider_tags_with_successful_verifications_from_another_branch_from_before_this_branch_created(provider_version_tags) + provider_version_tags - tags_with_successful_verifications_from_that_branch - tags_with_previous_successful_verifications_from_other_branches end def pending? diff --git a/lib/pact_broker/domain/version.rb b/lib/pact_broker/domain/version.rb index 43a14313b..390d06baf 100644 --- a/lib/pact_broker/domain/version.rb +++ b/lib/pact_broker/domain/version.rb @@ -59,6 +59,10 @@ def for(pacticipant_name, version_number) where_pacticipant_name(pacticipant_name).where_number(version_number).single_record end + def first_for_pacticipant_id_and_branch(pacticipant_id, branch) + where(pacticipant_id: pacticipant_id, branch: branch).order(:created_at).first + end + def latest_versions_for_pacticipant_branches(pacticipant_id, branches) query = Version.where(Sequel[:versions][:pacticipant_id] => pacticipant_id, Sequel[:versions][:branch] => branches) diff --git a/lib/pact_broker/pacts/pact_publication.rb b/lib/pact_broker/pacts/pact_publication.rb index 552c89bd2..88fd57cf0 100644 --- a/lib/pact_broker/pacts/pact_publication.rb +++ b/lib/pact_broker/pacts/pact_publication.rb @@ -5,6 +5,7 @@ require 'pact_broker/integrations/integration' require 'pact_broker/tags/head_pact_tags' require 'pact_broker/pacts/pact_publication_dataset_module' +require 'pact_broker/pacts/pact_publication_wip_dataset_module' require 'pact_broker/pacts/eager_loaders' require 'pact_broker/pacts/lazy_loaders' @@ -37,6 +38,7 @@ class PactPublication < Sequel::Model(:pact_publications) dataset_module do include PactBroker::Repositories::Helpers include PactPublicationDatasetModule + include PactPublicationWipDatasetModule end def self.subtract(a, b) diff --git a/lib/pact_broker/pacts/pact_publication_dataset_module.rb b/lib/pact_broker/pacts/pact_publication_dataset_module.rb index 33b36c0c8..d8957f207 100644 --- a/lib/pact_broker/pacts/pact_publication_dataset_module.rb +++ b/lib/pact_broker/pacts/pact_publication_dataset_module.rb @@ -168,43 +168,8 @@ def for_currently_deployed_versions(environment_name) .join(:environments, environments_join) end - def successfully_verified_by_provider_branch(provider_id, provider_version_branch) - verifications_join = { - pact_version_id: :pact_version_id, - Sequel[:verifications][:success] => true, - Sequel[:verifications][:wip] => false, - Sequel[:verifications][:provider_id] => provider_id - } - versions_join = { - Sequel[:verifications][:provider_version_id] => Sequel[:provider_versions][:id], - Sequel[:provider_versions][:branch] => provider_version_branch, - Sequel[:provider_versions][:pacticipant_id] => provider_id - } - - from_self(alias: :pp).select(Sequel[:pp].*) - .join(:verifications, verifications_join) - .join(:versions, versions_join, { table_alias: :provider_versions } ) - .where(Sequel[:pp][:provider_id] => provider_id) - .distinct - end - - def successfully_verified_by_provider_tag(provider_id, provider_tag) - verifications_join = { - pact_version_id: :pact_version_id, - Sequel[:verifications][:success] => true, - Sequel[:verifications][:wip] => false, - Sequel[:verifications][:provider_id] => provider_id - } - tags_join = { - Sequel[:verifications][:provider_version_id] => Sequel[:provider_tags][:version_id], - Sequel[:provider_tags][:name] => provider_tag - } - - from_self(alias: :pp).select(Sequel[:pp].*) - .join(:verifications, verifications_join) - .join(:tags, tags_join, { table_alias: :provider_tags } ) - .where(Sequel[:pp][:provider_id] => provider_id) - .distinct + def verified_before_date(date) + where { Sequel[:verifications][:execution_date] < date } end def created_after date diff --git a/lib/pact_broker/pacts/pact_publication_wip_dataset_module.rb b/lib/pact_broker/pacts/pact_publication_wip_dataset_module.rb new file mode 100644 index 000000000..ba59679ec --- /dev/null +++ b/lib/pact_broker/pacts/pact_publication_wip_dataset_module.rb @@ -0,0 +1,106 @@ +module PactBroker + module Pacts + module PactPublicationWipDatasetModule + def successfully_verified_by_provider_branch_when_not_wip(provider_id, provider_version_branch) + from_self(alias: :pp) + .select(Sequel[:pp].*) + .where(Sequel[:pp][:provider_id] => provider_id) + .join_successful_non_wip_verifications_for_provider_id(provider_id) + .join_provider_versions_for_provider_id_and_branch(provider_id, provider_version_branch) + .distinct + end + + def successfully_verified_by_provider_another_branch_before_this_branch_first_created(provider_id, provider_version_branch) + first_version_for_branch = PactBroker::Domain::Version.first_for_pacticipant_id_and_branch(provider_id, provider_version_branch) + + from_self(alias: :pp) + .select(Sequel[:pp].*) + .join_successful_non_wip_verifications_for_provider_id(provider_id) + .join_provider_versions_for_provider_id(provider_id) do + Sequel.lit('provider_versions.branch != ?', provider_version_branch) + end + .where(Sequel[:pp][:provider_id] => provider_id) + .verified_before_creation_date_of(first_version_for_branch) + .distinct + end + + def successfully_verified_by_provider_tag_when_not_wip(provider_id, provider_tag) + from_self(alias: :pp) + .select(Sequel[:pp].*) + .where(Sequel[:pp][:provider_id] => provider_id) + .join_successful_non_wip_verifications_for_provider_id(provider_id) + .join_provider_version_tags_for_tag(provider_tag) + .distinct + end + + def successfully_verified_by_provider_another_tag_before_this_tag_first_created(provider_id, provider_tag) + first_tag_with_name = PactBroker::Domain::Tag.where(pacticipant_id: provider_id, name: provider_tag).order(:created_at).first + from_self(alias: :pp) + .select(Sequel[:pp].*) + .where(Sequel[:pp][:provider_id] => provider_id) + .join_successful_non_wip_verifications_for_provider_id(provider_id) + .join_provider_version_tags do + Sequel.lit('provider_tags.name != ?', provider_tag) + end + .verified_before_creation_date_of(first_tag_with_name) + .distinct + end + + protected + + def verified_before_date(date) + where { Sequel[:verifications][:execution_date] < date } + end + + def join_successful_non_wip_verifications_for_provider_id(provider_id, &block) + verifications_join = { + pact_version_id: :pact_version_id, + Sequel[:verifications][:success] => true, + Sequel[:verifications][:wip] => false, + Sequel[:verifications][:provider_id] => provider_id + } + join(:verifications, verifications_join, {}, &block) + end + + def join_provider_version_tags &block + tags_join = { + Sequel[:verifications][:provider_version_id] => Sequel[:provider_tags][:version_id], + } + join(:tags, tags_join, { table_alias: :provider_tags }, &block) + end + + def join_provider_version_tags_for_tag(tag) + tags_join = { + Sequel[:verifications][:provider_version_id] => Sequel[:provider_tags][:version_id], + Sequel[:provider_tags][:name] => tag + } + join(:tags, tags_join, { table_alias: :provider_tags } ) + end + + def join_provider_versions_for_provider_id_and_branch(provider_id, provider_version_branch) + versions_join = { + Sequel[:verifications][:provider_version_id] => Sequel[:provider_versions][:id], + Sequel[:provider_versions][:branch] => provider_version_branch, + Sequel[:provider_versions][:pacticipant_id] => provider_id + } + join(:versions, versions_join, { table_alias: :provider_versions } ) + end + + def join_provider_versions_for_provider_id(provider_id, &block) + versions_join = { + Sequel[:verifications][:provider_version_id] => Sequel[:provider_versions][:id], + Sequel[:provider_versions][:pacticipant_id] => provider_id + } + join(:versions, versions_join, { table_alias: :provider_versions }, &block) + end + + def verified_before_creation_date_of(record) + if record + verified_before_date(record.created_at) + else + self + end + end + end + end +end diff --git a/lib/pact_broker/pacts/pact_version.rb b/lib/pact_broker/pacts/pact_version.rb index 8671fa6f2..a4fb00e80 100644 --- a/lib/pact_broker/pacts/pact_version.rb +++ b/lib/pact_broker/pacts/pact_version.rb @@ -16,6 +16,26 @@ class PactVersion < Sequel::Model(:pact_versions) dataset_module do include PactBroker::Repositories::Helpers + + def join_successful_verifications + verifications_join = { + Sequel[:verifications][:pact_version_id] => Sequel[:pact_versions][:id], + Sequel[:verifications][:success] => true + } + join(:verifications, verifications_join) + end + + def join_provider_versions + join(:versions, { Sequel[:provider_versions][:id] => Sequel[:verifications][:provider_version_id] }, { table_alias: :provider_versions }) + end + + def join_provider_version_tags_for_tag(tag) + tags_join = { + Sequel[:tags][:version_id] => Sequel[:provider_versions][:id], + Sequel[:tags][:name] => tag + } + join(:tags, tags_join) + end end def name @@ -52,33 +72,45 @@ def latest_consumer_version_number latest_consumer_version.number end - def select_provider_tags_with_successful_verifications(tags) + def select_provider_tags_with_successful_verifications_from_another_branch_from_before_this_branch_created(tags) tags.select do | tag | + first_tag_with_name = PactBroker::Domain::Tag.where(pacticipant_id: provider_id, name: tag).order(:created_at).first + verifications_join = { Sequel[:verifications][:pact_version_id] => Sequel[:pact_versions][:id], Sequel[:verifications][:success] => true } tags_join = { Sequel[:tags][:version_id] => Sequel[:versions][:id], - Sequel[:tags][:name] => tag } - PactVersion.where(Sequel[:pact_versions][:id] => id) + query = PactVersion.where(Sequel[:pact_versions][:id] => id) .join(:verifications, verifications_join) .join(:versions, Sequel[:versions][:id] => Sequel[:verifications][:provider_version_id]) - .join(:tags, tags_join) + .join(:tags, tags_join) do + Sequel.lit('tags.name != ?', tag) + end + + if first_tag_with_name + query = query.where { Sequel[:verifications][:created_at] < first_tag_with_name.created_at } + end + + query.any? + end + end + + def select_provider_tags_with_successful_verifications(tags) + tags.select do | tag | + PactVersion.where(Sequel[:pact_versions][:id] => id) + .join_successful_verifications + .join_provider_versions + .join_provider_version_tags_for_tag(tag) .any? end end def verified_successfully_by_any_provider_version? - verifications_join = { - Sequel[:verifications][:pact_version_id] => Sequel[:pact_versions][:id], - Sequel[:verifications][:pact_version_id] => id, - Sequel[:verifications][:success] => true - } PactVersion.where(Sequel[:pact_versions][:id] => id) - .join(:verifications, verifications_join) - .join(:versions, Sequel[:versions][:id] => Sequel[:verifications][:provider_version_id]) + .join_successful_verifications .any? end end diff --git a/lib/pact_broker/pacts/pacts_for_verification_repository.rb b/lib/pact_broker/pacts/pacts_for_verification_repository.rb index a7c23c48c..46a8a8c83 100644 --- a/lib/pact_broker/pacts/pacts_for_verification_repository.rb +++ b/lib/pact_broker/pacts/pacts_for_verification_repository.rb @@ -175,6 +175,7 @@ def merge_selected_pacts(selected_pacts) end end + # Tag object with created_at date for the first time that tag was created def provider_tag_objects_for(provider, provider_tags_names) PactBroker::Domain::Tag .select_group(Sequel[:tags][:name], Sequel[:pacticipant_id]) @@ -185,12 +186,13 @@ def provider_tag_objects_for(provider, provider_tags_names) .all end + # TODO ? find the WIP pacts by consumer branch def find_wip_pact_versions_for_provider_by_provider_tags(provider, provider_tags_names, provider_tags, wip_start_date, pact_publication_scope) potential_wip_pacts_by_consumer_tag_query = PactPublication.for_provider(provider).created_after(wip_start_date).send(pact_publication_scope) potential_wip_pacts_by_consumer_tag = potential_wip_pacts_by_consumer_tag_query.all tag_to_pact_publications = provider_tags_names.each_with_object({}) do | provider_tag_name, tag_to_pact_publications | - tag_to_pact_publications[provider_tag_name] = remove_already_verified_by_tag( + tag_to_pact_publications[provider_tag_name] = remove_non_wip_for_tag( potential_wip_pacts_by_consumer_tag, potential_wip_pacts_by_consumer_tag_query, provider, @@ -198,23 +200,10 @@ def find_wip_pact_versions_for_provider_by_provider_tags(provider, provider_tags ) end - provider_has_no_versions = !provider.any_versions? - tag_to_pact_publications.flat_map do | provider_tag_name, pact_publications | pact_publications.collect do | pact_publication | - force_include = PactBroker.feature_enabled?(:experimental_no_provider_versions_makes_all_head_pacts_wip) && provider_has_no_versions - - pending_tag_names_to_use = if force_include - [provider_tag_name] - else - pre_existing_tag_names = find_provider_tag_names_that_were_first_used_before_pact_published(pact_publication, provider_tags) - [provider_tag_name] & pre_existing_tag_names - end - - if pending_tag_names_to_use.any? - selectors = create_selectors_for_wip_pact(pact_publication) - VerifiablePact.create_for_wip_for_provider_tags(pact_publication.to_domain, selectors, pending_tag_names_to_use) - end + selectors = create_selectors_for_wip_pact(pact_publication) + VerifiablePact.create_for_wip_for_provider_tags(pact_publication.to_domain, selectors, [provider_tag_name]) end end.compact end @@ -231,13 +220,13 @@ def find_wip_pact_versions_for_provider_by_provider_branch(provider_name, provid provider = pacticipant_repository.find_by_name(provider_name) wip_start_date = options.fetch(:include_wip_pacts_since) - wip_pact_publications_by_branch = remove_already_verified_by_branch( + wip_pact_publications_by_branch = remove_non_wip_for_branch( PactPublication.for_provider(provider).created_after(wip_start_date).latest_by_consumer_branch, provider, provider_version_branch ) - wip_pact_publications_by_tag = remove_already_verified_by_branch( + wip_pact_publications_by_tag = remove_non_wip_for_branch( PactPublication.for_provider(provider).created_after(wip_start_date).latest_by_consumer_tag, provider, provider_version_branch @@ -277,12 +266,14 @@ def find_all_pact_versions_for_provider_with_consumer_version_tags provider_name end end - def remove_already_verified_by_branch(pact_publications, provider, provider_version_branch) - PactPublication.subtract(pact_publications.all, pact_publications.successfully_verified_by_provider_branch(provider.id, provider_version_branch).all) + def remove_non_wip_for_branch(pact_publications, provider, provider_version_branch) + remaining_pact_publications = PactPublication.subtract(pact_publications.all, pact_publications.successfully_verified_by_provider_branch_when_not_wip(provider.id, provider_version_branch).all) + PactPublication.subtract(remaining_pact_publications, pact_publications.successfully_verified_by_provider_another_branch_before_this_branch_first_created(provider.id, provider_version_branch).all) end - def remove_already_verified_by_tag(pact_publications, query, provider, tag) - PactPublication.subtract(pact_publications, query.successfully_verified_by_provider_tag(provider.id, tag).all) + def remove_non_wip_for_tag(pact_publications, query, provider, tag) + pact_publications = PactPublication.subtract(pact_publications, query.successfully_verified_by_provider_tag_when_not_wip(provider.id, tag).all) + PactPublication.subtract(pact_publications, query.successfully_verified_by_provider_another_tag_before_this_tag_first_created(provider.id, tag).all) end def scope_for(scope) diff --git a/lib/pact_broker/test/http_test_data_builder.rb b/lib/pact_broker/test/http_test_data_builder.rb index 3387f0310..bcf3f83c1 100644 --- a/lib/pact_broker/test/http_test_data_builder.rb +++ b/lib/pact_broker/test/http_test_data_builder.rb @@ -33,6 +33,13 @@ def separate puts "\n=============================================================\n\n" end + def comment string + puts "**********************************************************" + puts string + puts "**********************************************************\n\n" + self + end + def create_tagged_pacticipant_version(pacticipant:, version:, tag:) [*tag].each do | tag | create_tag(pacticipant: pacticipant, version: version, tag: tag) @@ -51,6 +58,7 @@ def create_version(pacticipant:, version:, branch:) branch: branch } client.put("pacticipants/#{encode(pacticipant)}/versions/#{encode(version)}", request_body).tap { |response| check_for_error(response) } + self end def deploy_to_prod(pacticipant:, version:) diff --git a/script/reproduce-issue-starting-up.rb b/script/reproduce-issue-starting-up.rb index bbdc81194..f4e66f798 100755 --- a/script/reproduce-issue-starting-up.rb +++ b/script/reproduce-issue-starting-up.rb @@ -1,7 +1,8 @@ #!/usr/bin/env ruby -begin +# To show issue on master, need to set RACK_ENV=production to turn off feature toggle +begin $LOAD_PATH << "#{Dir.pwd}/lib" require 'pact_broker/test/http_test_data_builder' base_url = ENV['PACT_BROKER_BASE_URL'] || 'http://localhost:9292' @@ -11,13 +12,17 @@ .delete_integration(consumer: "foo-consumer", provider: "bar-provider") .create_pacticipant("foo-consumer") .create_pacticipant("foo-provider") - .create_global_webhook_for_verification_published(uuid: "ba8feb17-558a-4b3f-a078-f52c6fafd014", url: "http://webhook-server:9393") + .create_version(pacticipant: "foo-provider", version: "0", branch: nil) .publish_pact(consumer: "foo-consumer", consumer_version: "1", provider: "bar-provider", content_id: "111", tag: "main") - .publish_pact(consumer: "foo-consumer", consumer_version: "2", provider: "bar-provider", content_id: "111", tag: ["feat/x", "feat/y"]) - .sleep(10) + .publish_pact(consumer: "foo-consumer", consumer_version: "2", provider: "bar-provider", content_id: "222", tag: ["feat/x", "feat/y"]) + .sleep(1) .get_pacts_for_verification( provider_version_tag: "main", - consumer_version_selectors: [{ tag: "main", latest: true }, { tag: "feat/x", latest: true }, { tag: "feat/y", latest: true }]) + consumer_version_selectors: [{ tag: "main", latest: true }], + include_wip_pacts_since: "2020-01-01", + enable_pending: true + ) + .comment("There should have been 2 pacts to verify here") .verify_pact( index: 0, provider_version_tag: "main", diff --git a/spec/features/pending_pacts_with_tags_spec.rb b/spec/features/pending_pacts_with_tags_spec.rb new file mode 100644 index 000000000..8747ea8b5 --- /dev/null +++ b/spec/features/pending_pacts_with_tags_spec.rb @@ -0,0 +1,138 @@ +RSpec.describe "the pending lifecycle of a pact (with tags)" do + let(:pact_content_1) { { some: "interactions" }.to_json } + let(:pact_content_2) { { some: "other interactions" }.to_json } + let(:request_headers) { { "CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/hal+json"} } + let(:failed_verification_results) do + { + providerApplicationVersion: "2", + success: false + }.to_json + end + let(:successful_verification_results) do + { + providerApplicationVersion: "2", + success: true + }.to_json + end + + def publish_pact + put("/pacts/provider/Bar/consumer/Foo/version/1", pact_content_1, request_headers) + end + + def get_pacts_for_verification(provider_version_tag = "main") + post("/pacts/provider/Bar/for-verification", { includePendingStatus: true, providerVersionTags: [*provider_version_tag] }.to_json, request_headers) + end + + def pact_url_from(pacts_for_verification_response) + JSON.parse(pacts_for_verification_response.body)["_embedded"]["pacts"][0]["_links"]["self"]["href"] + end + + def get_pact(pact_url) + get pact_url, nil, request_headers + end + + def verification_results_url_from(pact_response) + JSON.parse(pact_response.body)["_links"]["pb:publish-verification-results"]["href"] + end + + def publish_verification_results(verification_results_url, results) + put("/pacticipants/Bar/versions/#{JSON.parse(results)["providerApplicationVersion"]}/tags/main", nil, { "CONTENT_TYPE" => "application/json" }) + post(verification_results_url, results, request_headers) + end + + def pending_status_from(pacts_for_verification_response) + JSON.parse(pacts_for_verification_response.body)["_embedded"]["pacts"][0]["verificationProperties"]["pending"] + end + + + context "a pact" do + describe "when it is first published" do + it "is pending" do + publish_pact + pacts_for_verification_response = get_pacts_for_verification + expect(pending_status_from(pacts_for_verification_response)).to be true + end + end + + describe "when it is verified unsuccessfully" do + it "is still pending" do + # CONSUMER BUILD + # publish pact + publish_pact + + # PROVIDER BUILD + # fetch pacts to verify + pacts_for_verification_response = get_pacts_for_verification + pact_url = pact_url_from(pacts_for_verification_response) + pact_response = get_pact(pact_url) + + # verify pact... failure... + + # publish failure verification results + verification_results_url = verification_results_url_from(pact_response) + publish_verification_results(verification_results_url, failed_verification_results) + + # ANOTHER PROVIDER BUILD + # get pacts for verification + pacts_for_verification_response = get_pacts_for_verification + # still pending + expect(pending_status_from(pacts_for_verification_response)).to be true + end + end + + describe "when it is verified successfully" do + it "is no longer pending" do + # CONSUMER BUILD + publish_pact + + # PROVIDER BUILD + pacts_for_verification_response = get_pacts_for_verification + + # fetch pact + pact_url = pact_url_from(pacts_for_verification_response) + pact_response = get_pact(pact_url) + + # verify pact... success! + + # publish failure verification results + verification_results_url = verification_results_url_from(pact_response) + publish_verification_results(verification_results_url, successful_verification_results) + + # ANOTHER PROVIDER BUILD 2 + # get pacts for verification + # publish successful verification results + pacts_for_verification_response = get_pacts_for_verification + # not pending any more + expect(pending_status_from(pacts_for_verification_response)).to be false + end + end + + + describe "when it is verified successfully by one branch, and then another branch of the provider is created" do + it "is not pending for the new branch" do + # CONSUMER BUILD + publish_pact + + # PROVIDER BUILD + pacts_for_verification_response = get_pacts_for_verification + + # fetch pact + pact_url = pact_url_from(pacts_for_verification_response) + pact_response = get_pact(pact_url) + + # verify pact... success! + + # publish failure verification results + verification_results_url = verification_results_url_from(pact_response) + publish_verification_results(verification_results_url, successful_verification_results) + + # ANOTHER PROVIDER BUILD 2 from another branch + # get pacts for verification + # publish successful verification results + pacts_for_verification_response = get_pacts_for_verification("feat/foo") + # not pending + expect(pending_status_from(pacts_for_verification_response)).to be false + end + end + end +end diff --git a/spec/features/wip_pacts_spec.rb b/spec/features/wip_pacts_spec.rb index e9f861854..1129a9f8c 100644 --- a/spec/features/wip_pacts_spec.rb +++ b/spec/features/wip_pacts_spec.rb @@ -8,7 +8,8 @@ { consumerVersionSelectors: [ { tag: "master", latest: true } ], providerVersionTags: ["master"], - includeWipPactsSince: start_date + includeWipPactsSince: start_date, + includePendingStatus: true }.to_json end @@ -91,7 +92,8 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver { consumerVersionSelectors: [ { tag: consumer_version_tag || provider_version_tag, latest: true, fallbackTag: "master" } ], providerVersionTags: [provider_version_tag], - includeWipPactsSince: start_date + includeWipPactsSince: start_date, + includePendingStatus: true }.to_json end @@ -99,8 +101,6 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver context "a pact published afer the specified date, with a tag that is not specified explictly in the 'pacts for verification' request" do describe "when it is first published" do it "is included in the list of pacts to verify as a WIP pact" do - create_initial_provider_version_on_master - publish_pact_with_master_tag publish_pact_with_feature_tag @@ -111,8 +111,6 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver describe "when it is verified unsuccessfully" do it "is still included as a WIP pact" do - create_initial_provider_version_on_master - # CONSUMER BUILD # publish pact publish_pact_with_master_tag @@ -140,17 +138,16 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver describe "when it is verified successfully while included as a WIP pact" do it "is still included as a WIP pact" do - create_initial_provider_version_on_master - # CONSUMER BUILD publish_pact_with_master_tag publish_pact_with_feature_tag - # PROVIDER BUILD + # PROVIDER BUILD - master # fetch pacts to verify pacts_for_verification_response = get_pacts_for_verification pact_url = wip_pact_url_from(pacts_for_verification_response) pact_response = get_pact(pact_url) + expect(wip_pacts_from(pacts_for_verification_response).first["verificationProperties"]["pending"]).to be true # verify pact... success! @@ -158,19 +155,19 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver verification_results_url = verification_results_url_from(pact_response) publish_verification_results_with_tag_master(verification_results_url, true) - # ANOTHER PROVIDER BUILD 2 + # ANOTHER PROVIDER BUILD 2 - master # get pacts for verification # publish successful verification results pacts_for_verification_response = get_pacts_for_verification # still wip expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1 + # still pending - but maybe it should not be? + expect(wip_pacts_from(pacts_for_verification_response).first["verificationProperties"]["pending"]).to be true end end describe "when it is verified successfully when included explicitly" do it "is no longer included as a WIP pact" do - create_initial_provider_version_on_master - # CONSUMER BUILD publish_pact_with_master_tag publish_pact_with_feature_tag @@ -178,6 +175,7 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver # PROVIDER BUILD # fetch pacts to verify pacts_for_verification_response = get_pacts_for_verification + expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1 pact_url = wip_pact_url_from(pacts_for_verification_response) pact_response = get_pact(pact_url) @@ -202,8 +200,6 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver describe "a feature branching scenario" do it "keeps being WIP until the branch is merged" do - create_initial_provider_version_on_master - # CONSUMER BUILD - master publish_pact_with_master_tag @@ -236,7 +232,7 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver # however feat-x pact is no longer pending because it has a successful verification from master!!! # Question: do we want this behaviour? Or should pending use the same logic? expect(wip_pacts_from(pacts_for_verification_response).first['verificationProperties']['wip']).to be true - expect(wip_pacts_from(pacts_for_verification_response).first['verificationProperties']['pending']).to be nil + expect(wip_pacts_from(pacts_for_verification_response).first['verificationProperties']['pending']).to be true # verify pact... success! @@ -259,13 +255,31 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver end end - describe "a feature branching scenario with matching feature branches" do - it "stays wip on master even after it has been successfully verified on the provider's feature branch" do - create_initial_provider_version_on_master + describe "a feature branching scenario with multiple provider feature branches" do + it "is WIP on a second feature branch even if the first feature branch successfully verified it" do + # CONSUMER BUILD - feature branch + publish_pact_with_feature_tag - # CONSUMER BUILD - master - publish_pact_with_master_tag + # PROVIDER BUILD for feature branch 1 + # fetch pacts to verify + pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-1", "master")) + pact_response = get_pact(wip_pact_url_from(pacts_for_verification_response)) + + # verify pact... success! + + # publish success verification results + publish_verification_results("1", "feat-1", verification_results_url_from(pact_response), true) + + # ANOTHER PROVIDER BUILD on a different new feature branch + # fetch pacts to verify + sleep 1 if ::DB.mysql? + pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-2", "master")) + expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1 + end + end + describe "a feature branching scenario with matching feature branches" do + it "stays wip on master even after it has been successfully verified on the provider's feature branch" do # CONSUMER BUILD - feature branch publish_pact_with_feature_tag @@ -283,7 +297,7 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver # PROVIDER BUILD - on a matching feature branch # fetch pacts to verify - pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-x")) + pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-x", "feat-x")) # pact is not WIP because it has been explicitly included expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0 pact_url = pact_urls_from(pacts_for_verification_response).first @@ -303,36 +317,42 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver end describe "when a brand new provider branch is created" do - it "does not include any previously created WIP pacts because every single pact is pending for this new branch, and we don't want to verify the world" do - create_initial_provider_version_on_master - + it "does not include any pacts already successfully verified by another branch before this branch was created" do # CONSUMER BUILD - master publish_pact_with_master_tag + # PROVIDER BUILD - master + pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("master", "master")) + + # verify master pact successfully from provider master branch + pact_url = pact_urls_from(pacts_for_verification_response).first + pact_response = get_pact(pact_url) + verification_results_url = verification_results_url_from(pact_response) + publish_verification_results("1", "master", verification_results_url, true) + sleep 1 if ::DB.mysql? + # CONSUMER BUILD - feature branch publish_pact_with_feature_tag # PROVIDER BUILD - brand new feature branch pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master")) - expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0 + expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1 - # verify master pact successfully - pact_url = pact_urls_from(pacts_for_verification_response).first - pact_response = get_pact(pact_url) + # verify feature pact successfully + # pact_url = pact_urls_from(pacts_for_verification_response).first + # pact_response = get_pact(pact_url) - # publish successful results from feature branch - verification_results_url = verification_results_url_from(pact_response) - publish_verification_results("2", "feat-y", verification_results_url, true) + # # publish successful results from feature branch + # verification_results_url = verification_results_url_from(pact_response) + # publish_verification_results("2", "feat-y", verification_results_url, true) - # PROVIDER BUILD 2 - feature branch - pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master")) - # still no wip pacts - expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0 + # # PROVIDER BUILD 2 - feature branch + # pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master")) + # # still no wip pacts + # expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0 end it "does include any subsequently created new pacts" do - create_initial_provider_version_on_master - # CONSUMER BUILD - master publish_pact_with_master_tag @@ -341,7 +361,8 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver # PROVIDER BUILD - brand new feature branch pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master")) - expect(wip_pacts_from(pacts_for_verification_response).size).to eq 0 + expect(wip_pacts_from(pacts_for_verification_response).size).to eq 1 + # verify master pact successfully (creates a new provider version with tag feat-y) pact_url = pact_urls_from(pacts_for_verification_response).first pact_response = get_pact(pact_url) @@ -356,7 +377,7 @@ def build_pacts_for_verification_request_body(provider_version_tag, consumer_ver # CONSUMER BUILD - another feature branch publish_pact_with_feature_tag("4", "feat-z", pact_content_3) - # PROVIDER BUILD - brand new feature branch again + # PROVIDER BUILD - new feature branch again pacts_for_verification_response = get_pacts_for_verification(build_pacts_for_verification_request_body("feat-y", "master")) expect(wip_pacts_from(pacts_for_verification_response).size).to eq 2 end diff --git a/spec/lib/pact_broker/pacts/pact_publication_dataset_module_spec.rb b/spec/lib/pact_broker/pacts/pact_publication_dataset_module_spec.rb index fecddcffe..3586a7ce8 100644 --- a/spec/lib/pact_broker/pacts/pact_publication_dataset_module_spec.rb +++ b/spec/lib/pact_broker/pacts/pact_publication_dataset_module_spec.rb @@ -268,10 +268,10 @@ module Pacts end end - describe "#successfully_verified_by_provider_branch" do + describe "#successfully_verified_by_provider_branch_when_not_wip" do let(:bar) { td.find_pacticipant("Bar") } - subject { PactPublication.successfully_verified_by_provider_branch(bar.id, "main").all } + subject { PactPublication.successfully_verified_by_provider_branch_when_not_wip(bar.id, "main").all } context "PactPublication" do before do @@ -295,7 +295,7 @@ module Pacts context "with chained scopes" do - subject { PactPublication.latest_by_consumer_branch.successfully_verified_by_provider_branch(bar.id, "provider-main").all } + subject { PactPublication.latest_by_consumer_branch.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main").all } context "when there are no latest branch pacts that have been successfully verified by the specified provider branch" do before do @@ -348,7 +348,7 @@ module Pacts .create_verification(provider_version: "2", success: true, branch: "provider-main", number: "2") end - subject { PactPublication.latest_by_consumer_tag.successfully_verified_by_provider_branch(bar.id, "provider-main").all } + subject { PactPublication.latest_by_consumer_tag.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main").all } its(:size) { is_expected.to eq 1 } @@ -376,7 +376,7 @@ module Pacts it "with branches" do potential = PactPublication.for_provider(bar).latest_by_consumer_branch - already_verified = potential.successfully_verified_by_provider_branch(bar.id, "provider-main") + already_verified = potential.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main") not_verified = PactPublication.subtract(potential.all, already_verified.all) expect(not_verified.size).to eq 1 @@ -385,7 +385,7 @@ module Pacts it "with tags" do potential = PactPublication.for_provider(bar).latest_by_consumer_tag - already_verified = potential.successfully_verified_by_provider_branch(bar.id, "provider-main") + already_verified = potential.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main") not_verified = PactPublication.subtract(potential.all, already_verified.all) expect(not_verified.size).to eq 1 diff --git a/spec/lib/pact_broker/pacts/pact_publication_spec.rb b/spec/lib/pact_broker/pacts/pact_publication_spec.rb index 08c7e9cd9..bb7e2c079 100644 --- a/spec/lib/pact_broker/pacts/pact_publication_spec.rb +++ b/spec/lib/pact_broker/pacts/pact_publication_spec.rb @@ -432,10 +432,10 @@ module Pacts end end - describe "#successfully_verified_by_provider_branch" do + describe "#successfully_verified_by_provider_branch_when_not_wip" do let(:bar) { td.find_pacticipant("Bar") } - subject { PactPublication.successfully_verified_by_provider_branch(bar.id, "main").all } + subject { PactPublication.successfully_verified_by_provider_branch_when_not_wip(bar.id, "main").all } context "PactPublication" do before do @@ -455,7 +455,7 @@ module Pacts context "with chained scopes" do - subject { PactPublication.latest_by_consumer_branch.successfully_verified_by_provider_branch(bar.id, "provider-main").all } + subject { PactPublication.latest_by_consumer_branch.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main").all } context "when there are no latest branch pacts that have been successfully verified by the specified provider branch" do before do @@ -508,7 +508,7 @@ module Pacts .create_verification(provider_version: "2", success: true, branch: "provider-main", number: "2") end - subject { PactPublication.latest_by_consumer_tag.successfully_verified_by_provider_branch(bar.id, "provider-main").all } + subject { PactPublication.latest_by_consumer_tag.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main").all } its(:size) { is_expected.to eq 1 } @@ -536,7 +536,7 @@ module Pacts it "with branches" do potential = PactPublication.for_provider(bar).latest_by_consumer_branch - already_verified = potential.successfully_verified_by_provider_branch(bar.id, "provider-main") + already_verified = potential.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main") not_verified = potential.all - already_verified.all expect(not_verified.size).to eq 1 @@ -545,7 +545,7 @@ module Pacts it "with tags" do potential = PactPublication.for_provider(bar).latest_by_consumer_tag - already_verified = potential.successfully_verified_by_provider_branch(bar.id, "provider-main") + already_verified = potential.successfully_verified_by_provider_branch_when_not_wip(bar.id, "provider-main") not_verified = potential.all - already_verified.all expect(not_verified.size).to eq 1 diff --git a/spec/lib/pact_broker/pacts/pact_version_spec.rb b/spec/lib/pact_broker/pacts/pact_version_spec.rb index 3476ce16d..4413deefb 100644 --- a/spec/lib/pact_broker/pacts/pact_version_spec.rb +++ b/spec/lib/pact_broker/pacts/pact_version_spec.rb @@ -144,6 +144,50 @@ module Pacts end end + describe "select_provider_tags_with_successful_verifications_from_another_branch_from_before_this_branch_created" do + let(:pact_version) { PactVersion.last } + + subject { pact_version.select_provider_tags_with_successful_verifications_from_another_branch_from_before_this_branch_created(tags) } + + context "when the provider version tag specified does not exist yet but there are previous successful verifications from another branch" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + .create_verification(provider_version: "20", tag_names: ['dev'], success: true) + .create_verification(provider_version: "21", number: 2) + end + + let(:tags) { %w[feat-new-branch] } + + it { is_expected.to eq ["feat-new-branch"] } + end + + context "when there is a successful verification from before the first provider version with the specified tag was created" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + .create_verification(provider_version: "20", tag_names: ['dev'], success: true) + .add_day + .create_verification(provider_version: "21", tag_names: ['feat-new-branch'], number: 2, success: false) + end + + let(:tags) { %w[feat-new-branch] } + + it { is_expected.to eq ["feat-new-branch"] } + end + + context "when there is a successful verification from after the first provider version with the specified tag was created" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + .create_verification(provider_version: "21", tag_names: ['feat-new-branch'], number: 2, success: false) + .add_day + .create_verification(provider_version: "20", tag_names: ['dev'], success: true) + end + + let(:tags) { %w[feat-new-branch] } + + it { is_expected.to eq [] } + end + end + describe "#verified_successfully_by_any_provider_version?" do let(:pact_version) { PactVersion.last } diff --git a/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_branch_spec.rb b/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_branch_spec.rb index 0f54e4c17..fcb719027 100644 --- a/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_branch_spec.rb +++ b/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_branch_spec.rb @@ -218,6 +218,64 @@ module Pacts expect(subject.size).to be 0 end end + + context "when the provider version tag specified does not exist yet and there are previous successful verifications from another branch" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "20", branch: 'dev', success: true) + .create_verification(provider_version: "21", number: 2) + end + + let(:provider_version_branch) { "feat-new-branch" } + + it { is_expected.to be_empty } + end + + context "when the provider version tag specified does not exist yet and there are previous failed verifications from another branch" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "20", branch: 'dev', success: false) + .create_verification(provider_version: "21", number: 2) + end + + let(:provider_version_branch) { "feat-new-branch" } + + it "is included" do + expect(subject.first.provider_branch).to eq provider_version_branch + end + end + + context "when there is a successful verification from before the first provider version with the specified tag was created" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "20", branch: 'dev', success: true) + .add_day + .create_verification(provider_version: "21", branch: 'feat-new-branch', number: 2, success: false) + end + + let(:provider_version_branch) { "feat-new-branch" } + + it { is_expected.to be_empty } + end + + context "when there is a successful verification from after the first provider version with the specified tag was created" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "21", branch: 'feat-new-branch', number: 2, success: false) + .add_day + .create_verification(provider_version: "20", branch: 'dev', success: true) + end + + let(:provider_version_branch) { "feat-new-branch" } + + it "is included" do + expect(subject.first.provider_branch).to eq provider_version_branch + end + end end end end diff --git a/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb b/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb index 1b343b518..203f10eaf 100644 --- a/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +++ b/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb @@ -253,24 +253,22 @@ module Pacts end end - context "when the first provider tag with a given name was created after the head pact was created" do + context "when the provider tag does not exist yet and there are no provider versions" do before do td.create_pact_with_hierarchy("foo", "1", "bar") .create_consumer_version_tag("feat-x") - .add_day - .create_provider_version("5") - .create_provider_version_tag(provider_tags.first) end - it "doesn't return any pacts" do - expect(subject.size).to be 0 + it "is included" do + expect(subject.size).to be 1 end end - context "when the provider tag does not exist yet and there are no provider versions" do + context "when the provider tag does not exist yet but there are other provider versions" do before do td.create_pact_with_hierarchy("foo", "1", "bar") .create_consumer_version_tag("feat-x") + .create_provider_version("1") end it "is included" do @@ -278,35 +276,96 @@ module Pacts end end - context "when the provider tag does not exist yet but there are other provider versions" do + context "when a pact was already successfully verified by another branch before the first creation of one tag but not the other" do + let(:provider_tags) { %w[dev feat-1] } + before do td.create_pact_with_hierarchy("foo", "1", "bar") .create_consumer_version_tag("feat-x") - .create_provider_version("1") + .add_day + .create_verification(provider_version: "1", success: false, number: 1, tag_names: %w[dev]) + .add_day + .create_verification(provider_version: "2", success: true, number: 2, tag_names: %w[blah]) + .add_day + .create_verification(provider_version: "3", success: false, number: 3, tag_names: %w[feat-1]) end - it "doesn't return any pacts" do - expect(subject.size).to be 0 + it "is wip for the first tag but not the second" do + expect(subject.first.pending_provider_tags).to eq %w[dev] end end - - context "when a pact was published between the first creation date of two provider tags" do + context "when a pact was already successfully verified by another branch before the first creation of one tag but not the other" do let(:provider_tags) { %w[dev feat-1] } before do - td.create_provider("bar") - .create_provider_version("4") - .create_provider_version_tag(provider_tags.first) - .add_day - .create_pact_with_hierarchy("foo", "1", "bar") + td.create_pact_with_hierarchy("foo", "1", "bar") .create_consumer_version_tag("feat-x") .add_day - .create_provider_version("5") - .create_provider_version_tag(provider_tags.last) + .create_verification(provider_version: "1", success: true, number: 1, tag_names: %w[dev]) + .add_day + .create_verification(provider_version: "3", success: false, number: 3, tag_names: %w[feat-1]) end - it "is wip for the first tag but not the second" do + it "this should be WIP, as it hasn't been successfully verified by both dev AND feat-1 - need to update logic to exclude previous verifications from other specified tags. But two tags doesn't make sense anyway. Will leave it for now.", pending: true do + expect(subject).to_not be_empty + end + end + + context "when the provider version tag specified does not exist yet and there are previous successful verifications from another branch" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "20", tag_names: ['dev'], success: true) + .create_verification(provider_version: "21", number: 2) + end + + let(:provider_tags) { %w[feat-new-branch] } + + it { is_expected.to be_empty } + end + + context "when the provider version tag specified does not exist yet and there are previous failed verifications from another branch" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "20", tag_names: ['dev'], success: false) + .create_verification(provider_version: "21", number: 2) + end + + let(:provider_tags) { %w[feat-new-branch] } + + it "is included" do + expect(subject.first.pending_provider_tags).to eq [provider_tags.first] + end + end + + context "when there is a successful verification from before the first provider version with the specified tag was created" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "20", tag_names: ['dev'], success: true) + .add_day + .create_verification(provider_version: "21", tag_names: ['feat-new-branch'], number: 2, success: false) + end + + let(:provider_tags) { %w[feat-new-branch] } + + it { is_expected.to be_empty } + end + + context "when there is a successful verification from after the first provider version with the specified tag was created" do + before do + td.create_pact_with_hierarchy("foo", "1", "bar") + .create_consumer_version_tag("main") + .create_verification(provider_version: "21", tag_names: ['feat-new-branch'], number: 2, success: false) + .add_day + .create_verification(provider_version: "20", tag_names: ['dev'], success: true) + end + + let(:provider_tags) { %w[feat-new-branch] } + + it "is included" do expect(subject.first.pending_provider_tags).to eq [provider_tags.first] end end