diff options
Diffstat (limited to 'spec/services/projects')
8 files changed, 252 insertions, 35 deletions
diff --git a/spec/services/projects/apple_target_platform_detector_service_spec.rb b/spec/services/projects/apple_target_platform_detector_service_spec.rb new file mode 100644 index 00000000000..6391161824c --- /dev/null +++ b/spec/services/projects/apple_target_platform_detector_service_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::AppleTargetPlatformDetectorService do + let_it_be(:project) { build(:project) } + + subject { described_class.new(project).execute } + + context 'when project is not an xcode project' do + before do + allow(Gitlab::FileFinder).to receive(:new) { instance_double(Gitlab::FileFinder, find: []) } + end + + it 'returns an empty array' do + is_expected.to match_array [] + end + end + + context 'when project is an xcode project' do + using RSpec::Parameterized::TableSyntax + + let(:finder) { instance_double(Gitlab::FileFinder) } + + before do + allow(Gitlab::FileFinder).to receive(:new) { finder } + end + + def search_query(sdk, filename) + "SDKROOT = #{sdk} filename:#{filename}" + end + + context 'when setting string is found' do + where(:sdk, :filename, :result) do + 'iphoneos' | 'project.pbxproj' | [:ios] + 'iphoneos' | '*.xcconfig' | [:ios] + end + + with_them do + before do + allow(finder).to receive(:find).with(anything) { [] } + allow(finder).to receive(:find).with(search_query(sdk, filename)) { [instance_double(Gitlab::Search::FoundBlob)] } + end + + it 'returns an array of unique detected targets' do + is_expected.to match_array result + end + end + end + + context 'when setting string is not found' do + before do + allow(finder).to receive(:find).with(anything) { [] } + end + + it 'returns an empty array' do + is_expected.to match_array [] + end + end + end +end diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb index 38a3e00c8e7..86c0ba4222c 100644 --- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb +++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_ let(:tags) { %w[latest A Ba Bb C D E] } before do - project.add_maintainer(user) + project.add_maintainer(user) if user stub_container_registry_config(enabled: true) diff --git a/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb b/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb index 7fc963949eb..22cada7816b 100644 --- a/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb @@ -58,7 +58,19 @@ RSpec.describe Projects::ContainerRepository::ThirdParty::DeleteTagsService do stub_put_manifest_request('Ba', 500, {}) end - it { is_expected.to eq(status: :error, message: 'could not delete tags') } + it { is_expected.to eq(status: :error, message: "could not delete tags: #{tags.join(', ')}")} + + context 'when a large list of tag updates fails' do + let(:tags) { Array.new(1000) { |i| "tag_#{i}" } } + + before do + expect(service).to receive(:replace_tag_manifests).and_return({}) + end + + it 'truncates the log message' do + expect(subject).to eq(status: :error, message: "could not delete tags: #{tags.join(', ')}".truncate(1000)) + end + end end context 'a single tag update fails' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 96a50b26871..c5c5af3cb01 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -135,10 +135,22 @@ RSpec.describe Projects::CreateService, '#execute' do create_project(user, opts) end - it 'builds associated project settings' do + it 'creates associated project settings' do project = create_project(user, opts) - expect(project.project_setting).to be_new_record + expect(project.project_setting).to be_persisted + end + + context 'create_project_settings feature flag is disabled' do + before do + stub_feature_flags(create_project_settings: false) + end + + it 'builds associated project settings' do + project = create_project(user, opts) + + expect(project.project_setting).to be_new_record + end end it_behaves_like 'storing arguments in the application context' do @@ -376,6 +388,18 @@ RSpec.describe Projects::CreateService, '#execute' do imported_project end + + describe 'import scheduling' do + context 'when project import type is gitlab project migration' do + it 'does not schedule project import' do + opts[:import_type] = 'gitlab_project_migration' + + project = create_project(user, opts) + + expect(project.import_state.status).to eq('none') + end + end + end end context 'builds_enabled global setting' do diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb index b64f2d1e7d6..3ee867ba6f2 100644 --- a/spec/services/projects/operations/update_service_spec.rb +++ b/spec/services/projects/operations/update_service_spec.rb @@ -407,10 +407,11 @@ RSpec.describe Projects::Operations::UpdateService do context 'prometheus integration' do context 'prometheus params were passed into service' do - let(:prometheus_integration) do - build_stubbed(:prometheus_integration, project: project, properties: { + let!(:prometheus_integration) do + create(:prometheus_integration, :instance, properties: { api_url: "http://example.prometheus.com", - manual_configuration: "0" + manual_configuration: "0", + google_iap_audience_client_id: 123 }) end @@ -424,21 +425,23 @@ RSpec.describe Projects::Operations::UpdateService do end it 'uses Project#find_or_initialize_integration to include instance defined defaults and pass them to Projects::UpdateService', :aggregate_failures do - project_update_service = double(Projects::UpdateService) - - expect(project) - .to receive(:find_or_initialize_integration) - .with('prometheus') - .and_return(prometheus_integration) expect(Projects::UpdateService).to receive(:new) do |project_arg, user_arg, update_params_hash| + prometheus_attrs = update_params_hash[:prometheus_integration_attributes] + expect(project_arg).to eq project expect(user_arg).to eq user - expect(update_params_hash[:prometheus_integration_attributes]).to include('properties' => { 'api_url' => 'http://new.prometheus.com', 'manual_configuration' => '1' }) - expect(update_params_hash[:prometheus_integration_attributes]).not_to include(*%w(id project_id created_at updated_at)) - end.and_return(project_update_service) - expect(project_update_service).to receive(:execute) + expect(prometheus_attrs).to have_key('encrypted_properties') + expect(prometheus_attrs.keys).not_to include(*%w(id project_id created_at updated_at properties)) + expect(prometheus_attrs['encrypted_properties']).not_to eq(prometheus_integration.encrypted_properties) + end.and_call_original - subject.execute + expect { subject.execute }.to change(Integrations::Prometheus, :count).by(1) + + expect(Integrations::Prometheus.last).to have_attributes( + api_url: 'http://new.prometheus.com', + manual_configuration: true, + google_iap_audience_client_id: 123 + ) end end diff --git a/spec/services/projects/record_target_platforms_service_spec.rb b/spec/services/projects/record_target_platforms_service_spec.rb new file mode 100644 index 00000000000..85311f36428 --- /dev/null +++ b/spec/services/projects/record_target_platforms_service_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do + let_it_be(:project) { create(:project) } + + subject(:execute) { described_class.new(project).execute } + + context 'when project is an XCode project' do + before do + double = instance_double(Projects::AppleTargetPlatformDetectorService, execute: [:ios, :osx]) + allow(Projects::AppleTargetPlatformDetectorService).to receive(:new) { double } + end + + it 'creates a new setting record for the project', :aggregate_failures do + expect { execute }.to change { ProjectSetting.count }.from(0).to(1) + expect(ProjectSetting.last.target_platforms).to match_array(%w(ios osx)) + end + + it 'returns array of detected target platforms' do + expect(execute).to match_array %w(ios osx) + end + + context 'when a project has an existing setting record' do + before do + create(:project_setting, project: project, target_platforms: saved_target_platforms) + end + + def project_setting + ProjectSetting.find_by_project_id(project.id) + end + + context 'when target platforms changed' do + let(:saved_target_platforms) { %w(tvos) } + + it 'updates' do + expect { execute }.to change { project_setting.target_platforms }.from(%w(tvos)).to(%w(ios osx)) + end + + it { is_expected.to match_array %w(ios osx) } + end + + context 'when target platforms are the same' do + let(:saved_target_platforms) { %w(osx ios) } + + it 'does not update' do + expect { execute }.not_to change { project_setting.updated_at } + end + end + end + end + + context 'when project is not an XCode project' do + before do + double = instance_double(Projects::AppleTargetPlatformDetectorService, execute: []) + allow(Projects::AppleTargetPlatformDetectorService).to receive(:new).with(project) { double } + end + + it 'does nothing' do + expect { execute }.not_to change { ProjectSetting.count } + end + + it { is_expected.to be_nil } + end +end diff --git a/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb b/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb index 41de8c6bdbb..41487e9ea48 100644 --- a/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb +++ b/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb @@ -10,7 +10,8 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl let_it_be(:artifact_1) { create(:ci_job_artifact, project: project, size: 1, created_at: 14.days.ago) } let_it_be(:artifact_2) { create(:ci_job_artifact, project: project, size: 2, created_at: 13.days.ago) } - let_it_be(:artifact_3) { create(:ci_job_artifact, project: project, size: 5, created_at: 12.days.ago) } + let_it_be(:artifact_3) { create(:ci_job_artifact, project: project, size: nil, created_at: 13.days.ago) } + let_it_be(:artifact_4) { create(:ci_job_artifact, project: project, size: 5, created_at: 12.days.ago) } # This should not be included in the recalculation as it is created later than the refresh start time let_it_be(:future_artifact) { create(:ci_job_artifact, project: project, size: 8, created_at: 2.days.from_now) } @@ -33,7 +34,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl end before do - stub_const("#{described_class}::BATCH_SIZE", 2) + stub_const("#{described_class}::BATCH_SIZE", 3) stats = create(:project_statistics, project: project, build_artifacts_size: 120) stats.increment_counter(:build_artifacts_size, 30) @@ -48,7 +49,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl end it 'updates the last_job_artifact_id to the ID of the last artifact from the batch' do - expect { service.execute }.to change { refresh.reload.last_job_artifact_id.to_i }.to(artifact_2.id) + expect { service.execute }.to change { refresh.reload.last_job_artifact_id.to_i }.to(artifact_3.id) end it 'requeues the refresh job' do @@ -62,7 +63,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl :project_build_artifacts_size_refresh, :pending, project: project, - last_job_artifact_id: artifact_2.id + last_job_artifact_id: artifact_3.id ) end @@ -73,7 +74,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl end it 'keeps the last_job_artifact_id unchanged' do - expect(refresh.reload.last_job_artifact_id).to eq(artifact_2.id) + expect(refresh.reload.last_job_artifact_id).to eq(artifact_3.id) end it 'keeps the state of the refresh record at running' do @@ -89,7 +90,7 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitl project: project, updated_at: 2.days.ago, refresh_started_at: now, - last_job_artifact_id: artifact_3.id + last_job_artifact_id: artifact_4.id ) end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index fb94e94fd18..e547ace1d9f 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -11,8 +11,9 @@ RSpec.describe Projects::TransferService do let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) } let(:target) { group } + let(:executor) { user } - subject(:execute_transfer) { described_class.new(project, user).execute(target).tap { project.reload } } + subject(:execute_transfer) { described_class.new(project, executor).execute(target).tap { project.reload } } context 'with npm packages' do before do @@ -92,6 +93,55 @@ RSpec.describe Projects::TransferService do end end + context 'project in a group -> a personal namespace', :enable_admin_mode do + let(:project) { create(:project, :repository, :legacy_storage, group: group) } + let(:target) { user.namespace } + # We need to use an admin user as the executor because + # only an admin user has required permissions to transfer projects + # under _all_ the different circumstances specified below. + let(:executor) { create(:user, :admin) } + + it 'executes the transfer to personal namespace successfully' do + execute_transfer + + expect(project.namespace).to eq(user.namespace) + end + + context 'the owner of the namespace does not have a direct membership in the project residing in the group' do + it 'creates a project membership record for the owner of the namespace, with OWNER access level, after the transfer' do + execute_transfer + + expect(project.members.owners.find_by(user_id: user.id)).to be_present + end + end + + context 'the owner of the namespace has a direct membership in the project residing in the group' do + context 'that membership has an access level of OWNER' do + before do + project.add_owner(user) + end + + it 'retains the project membership record for the owner of the namespace, with OWNER access level, after the transfer' do + execute_transfer + + expect(project.members.owners.find_by(user_id: user.id)).to be_present + end + end + + context 'that membership has an access level that is not OWNER' do + before do + project.add_developer(user) + end + + it 'updates the project membership record for the owner of the namespace, to OWNER access level, after the transfer' do + execute_transfer + + expect(project.members.owners.find_by(user_id: user.id)).to be_present + end + end + end + end + context 'when transfer succeeds' do before do group.add_owner(user) @@ -148,23 +198,23 @@ RSpec.describe Projects::TransferService do context 'with a project integration' do let_it_be_with_reload(:project) { create(:project, namespace: user.namespace) } - let_it_be(:instance_integration) { create(:integrations_slack, :instance, webhook: 'http://project.slack.com') } + let_it_be(:instance_integration) { create(:integrations_slack, :instance) } + let_it_be(:project_integration) { create(:integrations_slack, project: project) } - context 'with an inherited integration' do - let_it_be(:project_integration) { create(:integrations_slack, project: project, webhook: 'http://project.slack.com', inherit_from_id: instance_integration.id) } + context 'when it inherits from instance_integration' do + before do + project_integration.update!(inherit_from_id: instance_integration.id, webhook: instance_integration.webhook) + end it 'replaces inherited integrations', :aggregate_failures do - execute_transfer - - expect(project.slack_integration.webhook).to eq(group_integration.webhook) - expect(Integration.count).to eq(3) + expect { execute_transfer } + .to change(Integration, :count).by(0) + .and change { project.slack_integration.webhook }.to eq(group_integration.webhook) end end context 'with a custom integration' do - let_it_be(:project_integration) { create(:integrations_slack, project: project, webhook: 'http://project.slack.com') } - - it 'does not updates the integrations' do + it 'does not update the integrations' do expect { execute_transfer }.not_to change { project.slack_integration.webhook } end end |