diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-18 12:45:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-18 12:45:46 +0300 |
commit | a7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch) | |
tree | 7452bd5c3545c2fa67a28aa013835fb4fa071baf /spec/services/projects | |
parent | ee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff) |
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'spec/services/projects')
9 files changed, 316 insertions, 63 deletions
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb index ef7741c2d0f..ed043bacf31 100644 --- a/spec/services/projects/autocomplete_service_spec.rb +++ b/spec/services/projects/autocomplete_service_spec.rb @@ -148,6 +148,32 @@ RSpec.describe Projects::AutocompleteService do end end + describe '#contacts' do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group, :crm_enabled) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:contact_1) { create(:contact, group: group) } + let_it_be(:contact_2) { create(:contact, group: group) } + + subject { described_class.new(project, user).contacts.as_json } + + before do + stub_feature_flags(customer_relations: true) + group.add_developer(user) + end + + it 'returns contact data correctly' do + expected_contacts = [ + { 'id' => contact_1.id, 'email' => contact_1.email, + 'first_name' => contact_1.first_name, 'last_name' => contact_1.last_name }, + { 'id' => contact_2.id, 'email' => contact_2.email, + 'first_name' => contact_2.first_name, 'last_name' => contact_2.last_name } + ] + + expect(subject).to match_array(expected_contacts) + end + end + describe '#labels_as_hash' do def expect_labels_to_equal(labels, expected_labels) expect(labels.size).to eq(expected_labels.size) diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb index 94037d6de1e..246ca301cfa 100644 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do ] end - RSpec.shared_examples 'logging a success response' do + shared_examples 'logging a success response' do it 'logs an info message' do expect(service).to receive(:log_info).with( service_class: 'Projects::ContainerRepository::DeleteTagsService', @@ -28,7 +28,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end end - RSpec.shared_examples 'logging an error response' do |message: 'could not delete tags', extra_log: {}| + shared_examples 'logging an error response' do |message: 'could not delete tags', extra_log: {}| it 'logs an error message' do log_data = { service_class: 'Projects::ContainerRepository::DeleteTagsService', @@ -45,7 +45,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end end - RSpec.shared_examples 'calling the correct delete tags service' do |expected_service_class| + shared_examples 'calling the correct delete tags service' do |expected_service_class| let(:service_response) { { status: :success, deleted: tags } } let(:excluded_service_class) { available_service_classes.excluding(expected_service_class).first } @@ -69,7 +69,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end end - RSpec.shared_examples 'handling invalid params' do + shared_examples 'handling invalid params' do context 'with invalid params' do before do expect(::Projects::ContainerRepository::Gitlab::DeleteTagsService).not_to receive(:new) @@ -91,7 +91,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end end - RSpec.shared_examples 'supporting fast delete' do + shared_examples 'supporting fast delete' do context 'when the registry supports fast delete' do before do allow(repository.client).to receive(:supports_tag_delete?).and_return(true) @@ -155,6 +155,14 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do it_behaves_like 'handling invalid params' end + + context 'when the repository is importing' do + before do + repository.update_columns(migration_state: 'importing', migration_import_started_at: Time.zone.now) + end + + it { is_expected.to include(status: :error, message: 'repository importing') } + end end context 'without user' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index d5fbf96ce74..10f694827e1 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -7,9 +7,10 @@ RSpec.describe Projects::CreateService, '#execute' do include GitHelpers let(:user) { create :user } + let(:project_name) { 'GitLab' } let(:opts) do { - name: 'GitLab', + name: project_name, namespace_id: user.namespace.id } end @@ -144,6 +145,12 @@ RSpec.describe Projects::CreateService, '#execute' do subject { create_project(user, opts) } end + + it 'logs creation' do + expect(Gitlab::AppLogger).to receive(:info).with(/#{user.name} created a new project/) + + create_project(user, opts) + end end context "admin creates project with other user's namespace_id" do @@ -183,9 +190,13 @@ RSpec.describe Projects::CreateService, '#execute' do user.refresh_authorized_projects # Ensure cache is warm end - it do - project = create_project(user, opts.merge!(namespace_id: group.id)) + subject(:project) { create_project(user, opts.merge!(namespace_id: group.id)) } + shared_examples 'has sync-ed traversal_ids' do + specify { expect(project.reload.project_namespace.traversal_ids).to eq([project.namespace.traversal_ids, project.project_namespace.id].flatten.compact) } + end + + it 'creates the project' do expect(project).to be_valid expect(project.owner).to eq(group) expect(project.namespace).to eq(group) @@ -193,6 +204,18 @@ RSpec.describe Projects::CreateService, '#execute' do expect(user.authorized_projects).to include(project) expect(project.project_namespace).to be_in_sync_with_project(project) end + + context 'with before_commit callback' do + it_behaves_like 'has sync-ed traversal_ids' + end + + context 'with after_create callback' do + before do + stub_feature_flags(sync_traversal_ids_before_commit: false) + end + + it_behaves_like 'has sync-ed traversal_ids' + end end context 'group sharing', :sidekiq_inline do @@ -202,7 +225,7 @@ RSpec.describe Projects::CreateService, '#execute' do let(:opts) do { - name: 'GitLab', + name: project_name, namespace_id: shared_group.id } end @@ -237,7 +260,7 @@ RSpec.describe Projects::CreateService, '#execute' do let(:share_max_access_level) { Gitlab::Access::MAINTAINER } let(:opts) do { - name: 'GitLab', + name: project_name, namespace_id: subgroup_for_projects.id } end @@ -583,58 +606,55 @@ RSpec.describe Projects::CreateService, '#execute' do opts[:initialize_with_readme] = '1' end - shared_examples 'creates README.md' do + shared_examples 'a repo with a README.md' do it { expect(project.repository.commit_count).to be(1) } it { expect(project.repository.readme.name).to eql('README.md') } - it { expect(project.repository.readme.data).to include('# GitLab') } + it { expect(project.repository.readme.data).to include(expected_content) } end - it_behaves_like 'creates README.md' + it_behaves_like 'a repo with a README.md' do + let(:expected_content) do + <<~MARKDOWN + cd existing_repo + git remote add origin #{project.http_url_to_repo} + git branch -M master + git push -uf origin master + MARKDOWN + end + end - context 'and a default_branch_name is specified' do + context 'and a readme_template is specified' do before do - allow(Gitlab::CurrentSettings) - .to receive(:default_branch_name) - .and_return('example_branch') + opts[:readme_template] = "# GitLab\nThis is customized readme." end - it_behaves_like 'creates README.md' + it_behaves_like 'a repo with a README.md' do + let(:expected_content) { "# GitLab\nThis is customized readme." } + end + end + + context 'and a default_branch_name is specified' do + before do + allow(Gitlab::CurrentSettings).to receive(:default_branch_name).and_return('example_branch') + end - it 'creates README.md within the specified branch rather than master' do + it 'creates the correct branch' do branches = project.repository.branches expect(branches.size).to eq(1) expect(branches.collect(&:name)).to contain_exactly('example_branch') end - describe 'advanced readme content', experiment: :new_project_readme_content do - before do - stub_experiments(new_project_readme_content: :advanced) - end - - it_behaves_like 'creates README.md' - - it 'includes advanced content in the README.md' do - content = project.repository.readme.data - expect(content).to include <<~MARKDOWN + it_behaves_like 'a repo with a README.md' do + let(:expected_content) do + <<~MARKDOWN + cd existing_repo git remote add origin #{project.http_url_to_repo} git branch -M example_branch git push -uf origin example_branch MARKDOWN end end - - context 'and readme_template is specified' do - before do - opts[:readme_template] = "# GitLab\nThis is customized template." - end - - it_behaves_like 'creates README.md' - - it 'creates README.md with specified template' do - expect(project.repository.readme.data).to include('This is customized template.') - end - end end end @@ -676,7 +696,7 @@ RSpec.describe Projects::CreateService, '#execute' do let(:opts) do { - name: 'GitLab', + name: project_name, namespace_id: group.id } end @@ -697,7 +717,7 @@ RSpec.describe Projects::CreateService, '#execute' do let(:opts) do { - name: 'GitLab', + name: project_name, namespace_id: subgroup.id } end @@ -808,7 +828,7 @@ RSpec.describe Projects::CreateService, '#execute' do let(:opts) do { - name: 'GitLab', + name: project_name, namespace_id: group.id } end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 9475f562d71..d60ec8c2958 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -15,20 +15,39 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do before do stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: []) + allow(Gitlab::EventStore).to receive(:publish) end shared_examples 'deleting the project' do - before do - # Run sidekiq immediately to check that renamed repository will be removed + it 'deletes the project', :sidekiq_inline do destroy_project(project, user, {}) - end - it 'deletes the project', :sidekiq_inline do expect(Project.unscoped.all).not_to include(project) expect(project.gitlab_shell.repository_exists?(project.repository_storage, path + '.git')).to be_falsey expect(project.gitlab_shell.repository_exists?(project.repository_storage, remove_path + '.git')).to be_falsey end + + it 'publishes a ProjectDeleted event with project id and namespace id' do + expected_data = { project_id: project.id, namespace_id: project.namespace_id } + expect(Gitlab::EventStore) + .to receive(:publish) + .with(event_type(Projects::ProjectDeletedEvent).containing(expected_data)) + + destroy_project(project, user, {}) + end + + context 'when feature flag publish_project_deleted_event is disabled' do + before do + stub_feature_flags(publish_project_deleted_event: false) + end + + it 'does not publish an event' do + expect(Gitlab::EventStore).not_to receive(:publish).with(event_type(Projects::ProjectDeletedEvent)) + + destroy_project(project, user, {}) + end + end end shared_examples 'deleting the project with pipeline and build' do @@ -97,6 +116,24 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do end end + context "deleting a project with merge requests" do + let!(:merge_request) { create(:merge_request, source_project: project) } + + before do + allow(project).to receive(:destroy!).and_return(true) + end + + it "deletes merge request and related records" do + merge_request_diffs = merge_request.merge_request_diffs + expect(merge_request_diffs.size).to eq(1) + + mrdc_count = MergeRequestDiffCommit.where(merge_request_diff_id: merge_request_diffs.first.id).count + + expect { destroy_project(project, user, {}) } + .to change { MergeRequestDiffCommit.count }.by(-mrdc_count) + end + end + it_behaves_like 'deleting the project' it 'invalidates personal_project_count cache' do diff --git a/spec/services/projects/import_export/export_service_spec.rb b/spec/services/projects/import_export/export_service_spec.rb index 6002aaf427a..54abbc04084 100644 --- a/spec/services/projects/import_export/export_service_spec.rb +++ b/spec/services/projects/import_export/export_service_spec.rb @@ -93,11 +93,23 @@ RSpec.describe Projects::ImportExport::ExportService do end it 'saves the project in the file system' do - expect(Gitlab::ImportExport::Saver).to receive(:save).with(exportable: project, shared: shared) + expect(Gitlab::ImportExport::Saver).to receive(:save).with(exportable: project, shared: shared).and_return(true) service.execute end + context 'when the upload fails' do + before do + expect(Gitlab::ImportExport::Saver).to receive(:save).with(exportable: project, shared: shared).and_return(false) + end + + it 'notifies the user of an error' do + expect(service).to receive(:notify_error).and_call_original + + expect { service.execute }.to raise_error(Gitlab::ImportExport::Error) + end + end + it 'calls the after export strategy' do expect(after_export_strategy).to receive(:execute) @@ -107,6 +119,7 @@ RSpec.describe Projects::ImportExport::ExportService do context 'when after export strategy fails' do before do allow(after_export_strategy).to receive(:execute).and_return(false) + expect(Gitlab::ImportExport::Saver).to receive(:save).with(exportable: project, shared: shared).and_return(true) end after do diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index 1d63f72ec38..ccfd119b55b 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -298,7 +298,7 @@ RSpec.describe Projects::ImportService do end def stub_github_omniauth_provider - provider = OpenStruct.new( + provider = ActiveSupport::InheritableOptions.new( 'name' => 'github', 'app_id' => 'asd123', 'app_secret' => 'asd123', diff --git a/spec/services/projects/overwrite_project_service_spec.rb b/spec/services/projects/overwrite_project_service_spec.rb index cc6a863a11d..7038910508f 100644 --- a/spec/services/projects/overwrite_project_service_spec.rb +++ b/spec/services/projects/overwrite_project_service_spec.rb @@ -81,16 +81,58 @@ RSpec.describe Projects::OverwriteProjectService do end end - it 'removes the original project' do - subject.execute(project_from) + it 'schedules original project for deletion' do + expect_next_instance_of(Projects::DestroyService) do |service| + expect(service).to receive(:async_execute) + end - expect { Project.find(project_from.id) }.to raise_error(ActiveRecord::RecordNotFound) + subject.execute(project_from) end it 'renames the project' do + original_path = project_from.full_path + subject.execute(project_from) - expect(project_to.full_path).to eq project_from.full_path + expect(project_to.full_path).to eq(original_path) + end + + it 'renames source project to temp name' do + allow(SecureRandom).to receive(:hex).and_return('test') + + subject.execute(project_from) + + expect(project_from.full_path).to include('-old-test') + end + + context 'when project rename fails' do + before do + expect(subject).to receive(:move_relationships_between).with(project_from, project_to) + expect(subject).to receive(:move_relationships_between).with(project_to, project_from) + end + + context 'source rename' do + it 'moves relations back to source project and raises an exception' do + allow(subject).to receive(:rename_project).and_return(status: :error) + + expect { subject.execute(project_from) }.to raise_error(StandardError, 'Source project rename failed during project overwrite') + end + end + + context 'new project rename' do + it 'moves relations back, renames source project back to original name and raises' do + name = project_from.name + path = project_from.path + + allow(subject).to receive(:rename_project).and_call_original + allow(subject).to receive(:rename_project).with(project_to, name, path).and_return(status: :error) + + expect { subject.execute(project_from) }.to raise_error(StandardError, 'New project rename failed during project overwrite') + + expect(project_from.name).to eq(name) + expect(project_from.path).to eq(path) + end + end end end @@ -121,7 +163,7 @@ RSpec.describe Projects::OverwriteProjectService do end end - context 'forks' do + context 'forks', :sidekiq_inline do context 'when moving a root forked project' do it 'moves the descendant forks' do expect(project_from.forks.count).to eq 2 @@ -147,6 +189,7 @@ RSpec.describe Projects::OverwriteProjectService do expect(project_to.fork_network.fork_network_members.map(&:project)).not_to include project_from end end + context 'when moving a intermediate forked project' do let(:project_to) { create(:project, namespace: lvl1_forked_project_1.namespace) } @@ -180,22 +223,26 @@ RSpec.describe Projects::OverwriteProjectService do end context 'if an exception is raised' do + before do + allow(subject).to receive(:rename_project).and_raise(StandardError) + end + it 'rollbacks changes' do updated_at = project_from.updated_at - allow(subject).to receive(:rename_project).and_raise(StandardError) - expect { subject.execute(project_from) }.to raise_error(StandardError) expect(Project.find(project_from.id)).not_to be_nil expect(project_from.reload.updated_at.change(usec: 0)).to eq updated_at.change(usec: 0) end - it 'tries to restore the original project repositories' do - allow(subject).to receive(:rename_project).and_raise(StandardError) - - expect(subject).to receive(:attempt_restore_repositories).with(project_from) + it 'removes fork network member' do + expect(ForkNetworkMember).to receive(:create!) + expect(ForkNetworkMember).to receive(:find_by) + expect(subject).to receive(:remove_source_project_from_fork_network).and_call_original expect { subject.execute(project_from) }.to raise_error(StandardError) + + expect(project_from.fork_network_member).to be_nil end end end diff --git a/spec/services/projects/readme_renderer_service_spec.rb b/spec/services/projects/readme_renderer_service_spec.rb new file mode 100644 index 00000000000..14cdcf67640 --- /dev/null +++ b/spec/services/projects/readme_renderer_service_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::ReadmeRendererService, '#execute' do + using RSpec::Parameterized::TableSyntax + + subject(:service) { described_class.new(project, nil, opts) } + + let_it_be(:project) { create(:project, title: 'My Project', description: '_custom_description_') } + + let(:opts) { {} } + + it 'renders the an ERB readme template' do + expect(service.execute).to start_with(<<~MARKDOWN) + # My Project + + _custom_description_ + + ## Getting started + + To make it easy for you to get started with GitLab, here's a list of recommended next steps. + + Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + + ## Add your files + + - [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files + - [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + + ``` + cd existing_repo + git remote add origin #{project.http_url_to_repo} + git branch -M master + git push -uf origin master + ``` + MARKDOWN + end + + context 'with a custom template' do + before do + allow(File).to receive(:read).and_call_original + end + + it 'renders that template file' do + opts[:template_name] = :custom_readme + + expect(service).to receive(:sanitized_filename).with(:custom_readme).and_return('custom_readme.md.tt') + expect(File).to receive(:read).with('custom_readme.md.tt').and_return('_custom_readme_file_content_') + expect(service.execute).to eq('_custom_readme_file_content_') + end + + context 'with path traversal in mind' do + where(:template_name, :exception, :expected_path) do + '../path/traversal/bad' | [Gitlab::Utils::PathTraversalAttackError, 'Invalid path'] | nil + '/bad/template' | [StandardError, 'path /bad/template.md.tt is not allowed'] | nil + 'good/template' | nil | 'good/template.md.tt' + end + + with_them do + it 'raises the expected exception on bad paths' do + opts[:template_name] = template_name + + if exception + expect { subject.execute }.to raise_error(*exception) + else + expect(File).to receive(:read).with(described_class::TEMPLATE_PATH.join(expected_path).to_s).and_return('') + + expect { subject.execute }.not_to raise_error + end + end + end + end + end +end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index ddd16100b40..fb94e94fd18 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -5,13 +5,14 @@ require 'spec_helper' RSpec.describe Projects::TransferService do include GitHelpers - let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } let_it_be(:group_integration) { create(:integrations_slack, :group, group: group, webhook: 'http://group.slack.com') } let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) } + let(:target) { group } - subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } } + subject(:execute_transfer) { described_class.new(project, user).execute(target).tap { project.reload } } context 'with npm packages' do before do @@ -690,6 +691,32 @@ RSpec.describe Projects::TransferService do end end + context 'handling issue contacts' do + let_it_be(:root_group) { create(:group) } + + let(:project) { create(:project, group: root_group) } + + before do + root_group.add_owner(user) + target.add_owner(user) + create_list(:issue_customer_relations_contact, 2, :for_issue, issue: create(:issue, project: project)) + end + + context 'with the same root_ancestor' do + let(:target) { create(:group, parent: root_group) } + + it 'retains issue contacts' do + expect { execute_transfer }.not_to change { CustomerRelations::IssueContact.count } + end + end + + context 'with a different root_ancestor' do + it 'deletes issue contacts' do + expect { execute_transfer }.to change { CustomerRelations::IssueContact.count }.by(-2) + end + end + end + def rugged_config rugged_repo(project.repository).config end |