From 6f79cf2bd654a018387313de70b9dd45a373ce72 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 17 Jun 2021 18:10:22 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- ...Geo Replicate a new Git repository type.md | 102 ++++-- .../Geo Replicate a new blob type.md | 102 ++++-- .rubocop_manual_todo.yml | 1 - .../components/graph/graph_component.vue | 1 + .../graph/stage_column_component.vue | 9 +- .../pipelines_list/pipeline_url.vue | 23 +- .../components/blob_content_viewer.vue | 9 +- .../{blob_header_edit.vue => blob_edit.vue} | 0 .../paginated_table_with_search_and_tabs.vue | 6 + app/assets/stylesheets/framework/header.scss | 5 +- .../framework/system_messages.scss | 3 +- .../stylesheets/startup/startup-signin.scss | 2 +- app/assets/stylesheets/utilities.scss | 4 + .../experience_levels_controller.rb | 2 +- .../registrations/welcome_controller.rb | 2 +- .../get_pipeline_details.query.graphql | 3 + .../push_options_handler_service.rb | 2 + app/views/layouts/minimal.html.haml | 17 + app/views/layouts/welcome.html.haml | 8 - .../registrations/welcome/show.html.haml | 5 +- app/workers/pipeline_hooks_worker.rb | 2 +- ...ad_balancing_for_pipeline_hooks_worker.yml | 8 - .../background_verification.md | 12 +- .../img/replication-status.png | Bin 7716 -> 0 bytes .../img/verification-status-primary.png | Bin 13329 -> 0 bytes .../img/verification-status-secondary.png | Bin 12186 -> 0 bytes .../img/verification_status_primary_v14_0.png | Bin 0 -> 28197 bytes .../verification_status_secondary_v14_0.png | Bin 0 -> 35270 bytes .../geo/disaster_recovery/planned_failover.md | 2 +- .../runbooks/planned_failover_multi_node.md | 2 +- .../runbooks/planned_failover_single_node.md | 2 +- .../geo/replication/configuration.md | 2 +- .../geo/replication/img/geo_architecture.png | Bin 53225 -> 49547 bytes .../replication/img/geo_node_dashboard.png | Bin 41734 -> 0 bytes .../img/geo_node_dashboard_v14_0.png | Bin 0 -> 48805 bytes .../replication/img/geo_node_health_v14_0.png | Bin 0 -> 57973 bytes .../geo/replication/troubleshooting.md | 2 +- doc/api/graphql/reference/index.md | 6 +- doc/ci/caching/index.md | 165 ++++++--- doc/ci/yaml/README.md | 346 ++++++++---------- .../documentation/styleguide/index.md | 8 +- .../documentation/styleguide/word_list.md | 2 +- doc/development/pipelines.md | 2 +- doc/user/project/push_options.md | 1 + geo_architecture.png | Bin 226694 -> 0 bytes lib/api/api.rb | 1 + lib/api/geo.rb | 29 ++ lib/api/internal/base.rb | 11 - .../background_migration/batched_migration.rb | 7 +- .../batched_migration_runner.rb | 45 ++- lib/gitlab/database/migration_helpers.rb | 6 +- .../processor/grpc_error_processor.rb | 7 +- lib/gitlab/push_options.rb | 1 + lib/tasks/gitlab/background_migrations.rake | 23 ++ package.json | 2 +- .../experience_levels_controller_spec.rb | 2 +- .../add_context_commits_modal_spec.js.snap | 1 + .../__snapshots__/popover_spec.js.snap | 2 + .../incidents_settings_tabs_spec.js.snap | 2 + spec/frontend/pipelines/graph/mock_data.js | 8 + .../graph/stage_column_component_spec.js | 63 ++-- .../components/blob_content_viewer_spec.js | 8 +- ..._header_edit_spec.js => blob_edit_spec.js} | 6 +- .../batched_migration_runner_spec.rb | 148 ++++++++ .../batched_migration_spec.rb | 18 + .../gitlab/database/migration_helpers_spec.rb | 8 +- .../processor/grpc_error_processor_spec.rb | 72 +++- spec/requests/api/geo_spec.rb | 30 ++ spec/requests/api/internal/base_spec.rb | 23 -- .../push_options_handler_service_spec.rb | 75 ++++ spec/workers/pipeline_hooks_worker_spec.rb | 1 - yarn.lock | 8 +- 72 files changed, 1013 insertions(+), 462 deletions(-) rename app/assets/javascripts/repository/components/{blob_header_edit.vue => blob_edit.vue} (100%) create mode 100644 app/views/layouts/minimal.html.haml delete mode 100644 app/views/layouts/welcome.html.haml delete mode 100644 config/feature_flags/development/load_balancing_for_pipeline_hooks_worker.yml delete mode 100644 doc/administration/geo/disaster_recovery/img/replication-status.png delete mode 100644 doc/administration/geo/disaster_recovery/img/verification-status-primary.png delete mode 100644 doc/administration/geo/disaster_recovery/img/verification-status-secondary.png create mode 100644 doc/administration/geo/disaster_recovery/img/verification_status_primary_v14_0.png create mode 100644 doc/administration/geo/disaster_recovery/img/verification_status_secondary_v14_0.png delete mode 100644 doc/administration/geo/replication/img/geo_node_dashboard.png create mode 100644 doc/administration/geo/replication/img/geo_node_dashboard_v14_0.png create mode 100644 doc/administration/geo/replication/img/geo_node_health_v14_0.png delete mode 100644 geo_architecture.png create mode 100644 lib/api/geo.rb create mode 100644 lib/tasks/gitlab/background_migrations.rake rename spec/frontend/repository/components/{blob_header_edit_spec.js => blob_edit_spec.js} (92%) create mode 100644 spec/requests/api/geo_spec.rb diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md index be6fef40f3a..3b4e6231882 100644 --- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md +++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md @@ -1,6 +1,6 @@ diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js index 28fe3b67e7b..3812483766d 100644 --- a/spec/frontend/pipelines/graph/mock_data.js +++ b/spec/frontend/pipelines/graph/mock_data.js @@ -12,6 +12,10 @@ export const mockPipelineResponse = { usesNeeds: true, downstream: null, upstream: null, + userPermissions: { + __typename: 'PipelinePermissions', + updatePipeline: true, + }, stages: { __typename: 'CiStageConnection', nodes: [ @@ -573,6 +577,10 @@ export const wrappedPipelineReturn = { iid: '38', complete: true, usesNeeds: true, + userPermissions: { + __typename: 'PipelinePermissions', + updatePipeline: true, + }, downstream: { __typename: 'PipelineConnection', nodes: [], diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js index f9f6c96a1a6..2a89dbd3fa5 100644 --- a/spec/frontend/pipelines/graph/stage_column_component_spec.js +++ b/spec/frontend/pipelines/graph/stage_column_component_spec.js @@ -31,6 +31,9 @@ const defaultProps = { name: 'Fish', groups: mockGroups, pipelineId: 159, + userPermissions: { + updatePipeline: true, + }, }; describe('stage column component', () => { @@ -152,35 +155,51 @@ describe('stage column component', () => { }); describe('with action', () => { - beforeEach(() => { + const defaults = { + groups: [ + { + id: 4259, + name: '', + status: { + icon: 'status_success', + label: 'success', + tooltip: '', + }, + jobs: [mockJob], + }, + ], + title: 'test', + hasTriggeredBy: false, + action: { + icon: 'play', + title: 'Play all', + path: 'action', + }, + }; + + it('renders action button if permissions are permitted', () => { createComponent({ method: mount, props: { - groups: [ - { - id: 4259, - name: '', - status: { - icon: 'status_success', - label: 'success', - tooltip: '', - }, - jobs: [mockJob], - }, - ], - title: 'test', - hasTriggeredBy: false, - action: { - icon: 'play', - title: 'Play all', - path: 'action', + ...defaults, + }, + }); + + expect(findActionComponent().exists()).toBe(true); + }); + + it('does not render action button if permissions are not permitted', () => { + createComponent({ + method: mount, + props: { + ...defaults, + userPermissions: { + updatePipeline: false, }, }, }); - }); - it('renders action button', () => { - expect(findActionComponent().exists()).toBe(true); + expect(findActionComponent().exists()).toBe(false); }); }); diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 495039b4ccb..5492fdd3065 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -4,7 +4,7 @@ import { nextTick } from 'vue'; import BlobContent from '~/blob/components/blob_content.vue'; import BlobHeader from '~/blob/components/blob_header.vue'; import BlobContentViewer from '~/repository/components/blob_content_viewer.vue'; -import BlobHeaderEdit from '~/repository/components/blob_header_edit.vue'; +import BlobEdit from '~/repository/components/blob_edit.vue'; import BlobReplace from '~/repository/components/blob_replace.vue'; let wrapper; @@ -78,7 +78,7 @@ const fullFactory = createFactory(mount); describe('Blob content viewer component', () => { const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findBlobHeader = () => wrapper.findComponent(BlobHeader); - const findBlobHeaderEdit = () => wrapper.findComponent(BlobHeaderEdit); + const findBlobEdit = () => wrapper.findComponent(BlobEdit); const findBlobContent = () => wrapper.findComponent(BlobContent); const findBlobReplace = () => wrapper.findComponent(BlobReplace); @@ -177,7 +177,7 @@ describe('Blob content viewer component', () => { await nextTick(); - expect(findBlobHeaderEdit().props()).toMatchObject({ + expect(findBlobEdit().props()).toMatchObject({ editPath: editBlobPath, webIdePath: ideEditPath, }); @@ -194,7 +194,7 @@ describe('Blob content viewer component', () => { await nextTick(); - expect(findBlobHeaderEdit().props()).toMatchObject({ + expect(findBlobEdit().props()).toMatchObject({ editPath: editBlobPath, webIdePath: ideEditPath, }); diff --git a/spec/frontend/repository/components/blob_header_edit_spec.js b/spec/frontend/repository/components/blob_edit_spec.js similarity index 92% rename from spec/frontend/repository/components/blob_header_edit_spec.js rename to spec/frontend/repository/components/blob_edit_spec.js index c0eb7c523c4..e6e69cd8549 100644 --- a/spec/frontend/repository/components/blob_header_edit_spec.js +++ b/spec/frontend/repository/components/blob_edit_spec.js @@ -1,6 +1,6 @@ import { GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import BlobHeaderEdit from '~/repository/components/blob_header_edit.vue'; +import BlobEdit from '~/repository/components/blob_edit.vue'; import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; const DEFAULT_PROPS = { @@ -8,11 +8,11 @@ const DEFAULT_PROPS = { webIdePath: 'some_file.js/ide/edit', }; -describe('BlobHeaderEdit component', () => { +describe('BlobEdit component', () => { let wrapper; const createComponent = (consolidatedEditButton = false, props = {}) => { - wrapper = shallowMount(BlobHeaderEdit, { + wrapper = shallowMount(BlobEdit, { propsData: { ...DEFAULT_PROPS, ...props, diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb index 9f0493ab0d7..779e8e40c97 100644 --- a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb +++ b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb @@ -281,4 +281,152 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do end end end + + describe '#finalize' do + let(:migration_wrapper) { Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper.new } + + let(:migration_helpers) { ActiveRecord::Migration.new } + let(:table_name) { :_batched_migrations_test_table } + let(:column_name) { :some_id } + let(:job_arguments) { [:some_id, :some_id_convert_to_bigint] } + + let(:migration_status) { :active } + + let!(:batched_migration) do + create( + :batched_background_migration, + status: migration_status, + max_value: 8, + batch_size: 2, + sub_batch_size: 1, + interval: 0, + table_name: table_name, + column_name: column_name, + job_arguments: job_arguments, + pause_ms: 0 + ) + end + + before do + migration_helpers.drop_table table_name, if_exists: true + migration_helpers.create_table table_name, id: false do |t| + t.integer :some_id, primary_key: true + t.integer :some_id_convert_to_bigint + end + + migration_helpers.execute("INSERT INTO #{table_name} VALUES (1, 1), (2, 2), (3, NULL), (4, NULL), (5, NULL), (6, NULL), (7, NULL), (8, NULL)") + end + + after do + migration_helpers.drop_table table_name, if_exists: true + end + + context 'when the migration is not yet completed' do + before do + common_attributes = { + batched_migration: batched_migration, + batch_size: 2, + sub_batch_size: 1, + pause_ms: 0 + } + + create(:batched_background_migration_job, common_attributes.merge(status: :succeeded, min_value: 1, max_value: 2)) + create(:batched_background_migration_job, common_attributes.merge(status: :pending, min_value: 3, max_value: 4)) + create(:batched_background_migration_job, common_attributes.merge(status: :failed, min_value: 5, max_value: 6, attempts: 1)) + end + + it 'completes the migration' do + expect(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_for_configuration) + .with('CopyColumnUsingBackgroundMigrationJob', table_name, column_name, job_arguments) + .and_return(batched_migration) + + expect(batched_migration).to receive(:finalizing!).and_call_original + + expect do + runner.finalize( + batched_migration.job_class_name, + table_name, + column_name, + job_arguments + ) + end.to change { batched_migration.reload.status }.from('active').to('finished') + + expect(batched_migration.batched_jobs).to all(be_succeeded) + + not_converted = migration_helpers.execute("SELECT * FROM #{table_name} WHERE some_id_convert_to_bigint IS NULL") + expect(not_converted.to_a).to be_empty + end + + context 'when migration fails to complete' do + it 'raises an error' do + batched_migration.batched_jobs.failed.update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS) + + expect do + runner.finalize( + batched_migration.job_class_name, + table_name, + column_name, + job_arguments + ) + end.to raise_error described_class::FailedToFinalize + end + end + end + + context 'when the migration is already finished' do + let(:migration_status) { :finished } + + it 'is a no-op' do + expect(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_for_configuration) + .with('CopyColumnUsingBackgroundMigrationJob', table_name, column_name, job_arguments) + .and_return(batched_migration) + + configuration = { + job_class_name: batched_migration.job_class_name, + table_name: table_name.to_sym, + column_name: column_name.to_sym, + job_arguments: job_arguments + } + + expect(Gitlab::AppLogger).to receive(:warn) + .with("Batched background migration for the given configuration is already finished: #{configuration}") + + expect(batched_migration).not_to receive(:finalizing!) + + runner.finalize( + batched_migration.job_class_name, + table_name, + column_name, + job_arguments + ) + end + end + + context 'when the migration does not exist' do + it 'is a no-op' do + expect(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_for_configuration) + .with('CopyColumnUsingBackgroundMigrationJob', table_name, column_name, [:some, :other, :arguments]) + .and_return(nil) + + configuration = { + job_class_name: batched_migration.job_class_name, + table_name: table_name.to_sym, + column_name: column_name.to_sym, + job_arguments: [:some, :other, :arguments] + } + + expect(Gitlab::AppLogger).to receive(:warn) + .with("Could not find batched background migration for the given configuration: #{configuration}") + + expect(batched_migration).not_to receive(:finalizing!) + + runner.finalize( + batched_migration.job_class_name, + table_name, + column_name, + [:some, :other, :arguments] + ) + end + end + end end diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb index d881390cd52..194d0243476 100644 --- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb +++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb @@ -387,4 +387,22 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m expect(actual).to contain_exactly(migration) end end + + describe '.find_for_configuration' do + it 'returns nill if such migration does not exists' do + expect(described_class.find_for_configuration('MyJobClass', :projects, :id, [[:id], [:id_convert_to_bigint]])).to be_nil + end + + it 'returns the migration when it exists' do + migration = create( + :batched_background_migration, + job_class_name: 'MyJobClass', + table_name: :projects, + column_name: :id, + job_arguments: [[:id], [:id_convert_to_bigint]] + ) + + expect(described_class.find_for_configuration('MyJobClass', :projects, :id, [[:id], [:id_convert_to_bigint]])).to eq(migration) + end + end end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index f0ea07646fb..d5e9de84538 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -2007,7 +2007,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do job_class_name: 'CopyColumnUsingBackgroundMigrationJob', table_name: :events, column_name: :id, - job_arguments: [[:id], [:id_convert_to_bigint]] + job_arguments: [["id"], ["id_convert_to_bigint"]] } end @@ -2017,7 +2017,11 @@ RSpec.describe Gitlab::Database::MigrationHelpers do create(:batched_background_migration, configuration.merge(status: :active)) expect { ensure_batched_background_migration_is_finished } - .to raise_error "Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active': #{configuration}" + .to raise_error "Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active': #{configuration}" \ + "\n\n" \ + "Finalize it manualy by running" \ + "\n\n" \ + "\tgitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,events,id,'[[\"id\"]\\, [\"id_convert_to_bigint\"]]']" end it 'does not raise error when migration exists and is marked as finished' do diff --git a/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb index 6076e525f06..9acc7fd04be 100644 --- a/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb +++ b/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb @@ -15,6 +15,18 @@ RSpec.describe Gitlab::ErrorTracking::Processor::GrpcErrorProcessor do let(:event) { Raven::Event.from_exception(exception, required_options.merge(data)) } let(:result_hash) { described_class.call(event).to_hash } + let(:data) do + { + extra: { + caller: 'test' + }, + fingerprint: [ + 'GRPC::DeadlineExceeded', + '4:Deadline Exceeded. debug_error_string:{"created":"@1598938192.005782000","description":"Error received from peer unix:/home/git/gitalypraefect.socket","file":"src/core/lib/surface/call.cc","file_line":1055,"grpc_message":"Deadline Exceeded","grpc_status":4}' + ] + } + end + context 'when there is no GRPC exception' do let(:exception) { RuntimeError.new } let(:data) { { fingerprint: ['ArgumentError', 'Missing arguments'] } } @@ -24,21 +36,9 @@ RSpec.describe Gitlab::ErrorTracking::Processor::GrpcErrorProcessor do end end - context 'when there is a GPRC exception with a debug string' do + context 'when there is a GRPC exception with a debug string' do let(:exception) { GRPC::DeadlineExceeded.new('Deadline Exceeded', {}, '{"hello":1}') } - let(:data) do - { - extra: { - caller: 'test' - }, - fingerprint: [ - 'GRPC::DeadlineExceeded', - '4:Deadline Exceeded. debug_error_string:{"created":"@1598938192.005782000","description":"Error received from peer unix:/home/git/gitalypraefect.socket","file":"src/core/lib/surface/call.cc","file_line":1055,"grpc_message":"Deadline Exceeded","grpc_status":4}' - ] - } - end - it 'removes the debug error string and stores it as an extra field' do expect(result_hash[:fingerprint]) .to eq(['GRPC::DeadlineExceeded', '4:Deadline Exceeded.']) @@ -66,5 +66,51 @@ RSpec.describe Gitlab::ErrorTracking::Processor::GrpcErrorProcessor do end end end + + context 'when there is a wrapped GRPC exception with a debug string' do + let(:inner_exception) { GRPC::DeadlineExceeded.new('Deadline Exceeded', {}, '{"hello":1}') } + let(:exception) do + begin + raise inner_exception + rescue GRPC::DeadlineExceeded + raise StandardError.new, inner_exception.message + end + rescue StandardError => e + e + end + + it 'removes the debug error string and stores it as an extra field' do + expect(result_hash[:fingerprint]) + .to eq(['GRPC::DeadlineExceeded', '4:Deadline Exceeded.']) + + expect(result_hash[:exception][:values].first) + .to include(type: 'GRPC::DeadlineExceeded', value: '4:Deadline Exceeded.') + + expect(result_hash[:exception][:values].second) + .to include(type: 'StandardError', value: '4:Deadline Exceeded.') + + expect(result_hash[:extra]) + .to include(caller: 'test', grpc_debug_error_string: '{"hello":1}') + end + + context 'with no custom fingerprint' do + let(:data) do + { extra: { caller: 'test' } } + end + + it 'removes the debug error string and stores it as an extra field' do + expect(result_hash).not_to include(:fingerprint) + + expect(result_hash[:exception][:values].first) + .to include(type: 'GRPC::DeadlineExceeded', value: '4:Deadline Exceeded.') + + expect(result_hash[:exception][:values].second) + .to include(type: 'StandardError', value: '4:Deadline Exceeded.') + + expect(result_hash[:extra]) + .to include(caller: 'test', grpc_debug_error_string: '{"hello":1}') + end + end + end end end diff --git a/spec/requests/api/geo_spec.rb b/spec/requests/api/geo_spec.rb new file mode 100644 index 00000000000..edbca5eb1c6 --- /dev/null +++ b/spec/requests/api/geo_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Geo do + include WorkhorseHelpers + + describe 'GET /geo/proxy' do + subject { get api('/geo/proxy'), headers: workhorse_headers } + + include_context 'workhorse headers' + + context 'with valid auth' do + it 'returns empty data' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to be_empty + end + end + + it 'rejects requests that bypassed gitlab-workhorse' do + workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) + + subject + + expect(response).to have_gitlab_http_status(:forbidden) + end + end +end diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index 631698554f9..3405d66f216 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1378,29 +1378,6 @@ RSpec.describe API::Internal::Base do end end - describe 'GET /internal/geo_proxy' do - subject { get api('/internal/geo_proxy'), params: { secret_token: secret_token } } - - context 'with valid auth' do - it 'returns empty data' do - subject - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_empty - end - end - - context 'with invalid auth' do - let(:secret_token) { 'invalid_token' } - - it 'returns unauthorized' do - subject - - expect(response).to have_gitlab_http_status(:unauthorized) - end - end - end - def lfs_auth_project(project) post( api("/internal/lfs_authenticate"), diff --git a/spec/services/merge_requests/push_options_handler_service_spec.rb b/spec/services/merge_requests/push_options_handler_service_spec.rb index 87c3fc6a2d8..c09435a70e2 100644 --- a/spec/services/merge_requests/push_options_handler_service_spec.rb +++ b/spec/services/merge_requests/push_options_handler_service_spec.rb @@ -10,6 +10,7 @@ RSpec.describe MergeRequests::PushOptionsHandlerService do let_it_be(:user2) { create(:user, developer_projects: [project]) } let_it_be(:user3) { create(:user, developer_projects: [project]) } let_it_be(:forked_project) { fork_project(project, user1, repository: true) } + let_it_be(:milestone) { create(:milestone, project: project, title: '1.0') } let(:service) { described_class.new(project: project, current_user: user1, changes: changes, push_options: push_options) } let(:source_branch) { 'fix' } @@ -59,6 +60,16 @@ RSpec.describe MergeRequests::PushOptionsHandlerService do end end + shared_examples_for 'a service that can set the milestone of a merge request' do + subject(:last_mr) { MergeRequest.last } + + it 'sets the milestone' do + service.execute + + expect(last_mr.milestone&.title).to eq(expected_milestone) + end + end + shared_examples_for 'a service that can set the merge request to merge when pipeline succeeds' do subject(:last_mr) { MergeRequest.last } @@ -514,6 +525,70 @@ RSpec.describe MergeRequests::PushOptionsHandlerService do it_behaves_like 'with the project default branch' end + describe '`milestone` push option' do + context 'with a valid milestone' do + let(:expected_milestone) { milestone.title } + let(:push_options) { { milestone: milestone.title } } + + context 'with a new branch' do + let(:changes) { new_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + service.execute + + expect(service.errors).to include(error_mr_required) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, milestone: milestone.title } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the milestone of a merge request' + end + end + + context 'with an existing branch but no open MR' do + let(:changes) { existing_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + service.execute + + expect(service.errors).to include(error_mr_required) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, milestone: milestone.title } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the milestone of a merge request' + end + end + + context 'with an existing branch that has a merge request open' do + let(:changes) { existing_branch_changes } + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)} + + it_behaves_like 'a service that does not create a merge request' + it_behaves_like 'a service that can set the milestone of a merge request' + end + + it_behaves_like 'with a deleted branch' + it_behaves_like 'with the project default branch' + end + + context 'with invalid milestone' do + let(:expected_milestone) { nil } + let(:changes) { new_branch_changes } + let(:push_options) { { create: true, milestone: 'invalid_milestone' } } + + it_behaves_like 'a service that can set the milestone of a merge request' + end + end + shared_examples 'with an existing branch that has a merge request open in foss' do let(:changes) { existing_branch_changes } let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)} diff --git a/spec/workers/pipeline_hooks_worker_spec.rb b/spec/workers/pipeline_hooks_worker_spec.rb index 5957b355c8e..0ed00c0c66a 100644 --- a/spec/workers/pipeline_hooks_worker_spec.rb +++ b/spec/workers/pipeline_hooks_worker_spec.rb @@ -25,6 +25,5 @@ RSpec.describe PipelineHooksWorker do it_behaves_like 'worker with data consistency', described_class, - feature_flag: :load_balancing_for_pipeline_hooks_worker, data_consistency: :delayed end diff --git a/yarn.lock b/yarn.lock index f86c8d75736..46c6932fa68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -908,10 +908,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8" integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw== -"@gitlab/ui@29.36.0": - version "29.36.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.36.0.tgz#a418c34c7ef768552b551807fa2a65deeaeba0bf" - integrity sha512-ZsaYpbp5cFN9hxVCf19E7avS9AmMaAyS4/Zwkwu2reHJUOkwyOY24eLr44u/Kbaq6SkFarQ2y+zU8vuhzXwQjQ== +"@gitlab/ui@29.37.0": + version "29.37.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.37.0.tgz#ddfd4760562387f7c164756301f73e29c1a5cd13" + integrity sha512-DK+MRhCeAXs7RhbIq7k7z+jTvSoQFfziMgFidmFiyyLYsZRj0+ya2pF9SubxEzH9HKwhs2TNZFd28onO8i5upg== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0"