diff options
Diffstat (limited to 'spec/requests/api')
120 files changed, 1594 insertions, 556 deletions
diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb index 6bc133f67c0..f497227789a 100644 --- a/spec/requests/api/admin/plan_limits_spec.rb +++ b/spec/requests/api/admin/plan_limits_spec.rb @@ -29,6 +29,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do expect(json_response['npm_max_file_size']).to eq(Plan.default.actual_limits.npm_max_file_size) expect(json_response['nuget_max_file_size']).to eq(Plan.default.actual_limits.nuget_max_file_size) expect(json_response['pypi_max_file_size']).to eq(Plan.default.actual_limits.pypi_max_file_size) + expect(json_response['terraform_module_max_file_size']).to eq(Plan.default.actual_limits.terraform_module_max_file_size) end end @@ -48,6 +49,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do expect(json_response['npm_max_file_size']).to eq(Plan.default.actual_limits.npm_max_file_size) expect(json_response['nuget_max_file_size']).to eq(Plan.default.actual_limits.nuget_max_file_size) expect(json_response['pypi_max_file_size']).to eq(Plan.default.actual_limits.pypi_max_file_size) + expect(json_response['terraform_module_max_file_size']).to eq(Plan.default.actual_limits.terraform_module_max_file_size) end end @@ -85,7 +87,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do 'maven_max_file_size': 30, 'npm_max_file_size': 40, 'nuget_max_file_size': 50, - 'pypi_max_file_size': 60 + 'pypi_max_file_size': 60, + 'terraform_module_max_file_size': 70 } expect(response).to have_gitlab_http_status(:ok) @@ -96,6 +99,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do expect(json_response['npm_max_file_size']).to eq(40) expect(json_response['nuget_max_file_size']).to eq(50) expect(json_response['pypi_max_file_size']).to eq(60) + expect(json_response['terraform_module_max_file_size']).to eq(70) end it 'updates single plan limits' do @@ -128,7 +132,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do 'maven_max_file_size': 'c', 'npm_max_file_size': 'd', 'nuget_max_file_size': 'e', - 'pypi_max_file_size': 'f' + 'pypi_max_file_size': 'f', + 'terraform_module_max_file_size': 'g' } expect(response).to have_gitlab_http_status(:bad_request) @@ -139,7 +144,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do 'generic_packages_max_file_size is invalid', 'npm_max_file_size is invalid', 'nuget_max_file_size is invalid', - 'pypi_max_file_size is invalid' + 'pypi_max_file_size is invalid', + 'terraform_module_max_file_size is invalid' ) end end diff --git a/spec/requests/api/api_spec.rb b/spec/requests/api/api_spec.rb index 46430e55ff2..81620fce448 100644 --- a/spec/requests/api/api_spec.rb +++ b/spec/requests/api/api_spec.rb @@ -81,6 +81,7 @@ RSpec.describe API::API do let_it_be(:maven_metadatum) { package.maven_metadatum } let_it_be(:package_file) { package.package_files.first } let_it_be(:deploy_token) { create(:deploy_token) } + let(:headers_with_deploy_token) do { Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => deploy_token.token @@ -138,6 +139,7 @@ RSpec.describe API::API do describe 'Marginalia comments' do context 'GET /user/:id' do let_it_be(:user) { create(:user) } + let(:component_map) do { "application" => "test", diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb index 1c825949ae8..07a9f7dfd74 100644 --- a/spec/requests/api/award_emoji_spec.rb +++ b/spec/requests/api/award_emoji_spec.rb @@ -8,6 +8,7 @@ RSpec.describe API::AwardEmoji do let_it_be(:issue) { create(:issue, project: project) } let_it_be(:award_emoji) { create(:award_emoji, awardable: issue, user: user) } let_it_be(:note) { create(:note, project: project, noteable: issue) } + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) } diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 36fbe86ac76..ad517a05533 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe API::Branches do let_it_be(:user) { create(:user) } + let(:project) { create(:project, :repository, creator: user, path: 'my.project') } let(:guest) { create(:user).tap { |u| project.add_guest(u) } } let(:branch_name) { 'feature' } @@ -20,7 +21,7 @@ RSpec.describe API::Branches do stub_feature_flags(branch_list_keyset_pagination: false) end - describe "GET /projects/:id/repository/branches", :use_clean_rails_redis_caching do + describe "GET /projects/:id/repository/branches", :use_clean_rails_redis_caching, :clean_gitlab_redis_shared_state do let(:route) { "/projects/#{project_id}/repository/branches" } shared_examples_for 'repository branches' do @@ -74,6 +75,14 @@ RSpec.describe API::Branches do check_merge_status(json_response) end + + it 'recovers pagination headers from cache between consecutive requests' do + 2.times do + get api(route, current_user), params: base_params + + expect(response.headers).to include('X-Page') + end + end end context 'with gitaly pagination params' do @@ -718,10 +727,11 @@ RSpec.describe API::Branches do end it 'returns 400 if ref name is invalid' do + error_message = 'Failed to create branch \'new_design3\': invalid reference name \'foo\'' post api(route, user), params: { branch: 'new_design3', ref: 'foo' } expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['message']).to eq('Invalid reference name: foo') + expect(json_response['message']).to eq(error_message) end end diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb new file mode 100644 index 00000000000..f0edfa6f227 --- /dev/null +++ b/spec/requests/api/bulk_imports_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::BulkImports do + let_it_be(:user) { create(:user) } + let_it_be(:import_1) { create(:bulk_import, user: user) } + let_it_be(:import_2) { create(:bulk_import, user: user) } + let_it_be(:entity_1) { create(:bulk_import_entity, bulk_import: import_1) } + let_it_be(:entity_2) { create(:bulk_import_entity, bulk_import: import_1) } + let_it_be(:entity_3) { create(:bulk_import_entity, bulk_import: import_2) } + let_it_be(:failure_3) { create(:bulk_import_failure, entity: entity_3) } + + describe 'GET /bulk_imports' do + it 'returns a list of bulk imports authored by the user' do + get api('/bulk_imports', user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.pluck('id')).to contain_exactly(import_1.id, import_2.id) + end + end + + describe 'GET /bulk_imports/entities' do + it 'returns a list of all import entities authored by the user' do + get api('/bulk_imports/entities', user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.pluck('id')).to contain_exactly(entity_1.id, entity_2.id, entity_3.id) + end + end + + describe 'GET /bulk_imports/:id' do + it 'returns specified bulk import' do + get api("/bulk_imports/#{import_1.id}", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['id']).to eq(import_1.id) + end + end + + describe 'GET /bulk_imports/:id/entities' do + it 'returns specified bulk import entities with failures' do + get api("/bulk_imports/#{import_2.id}/entities", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.pluck('id')).to contain_exactly(entity_3.id) + expect(json_response.first['failures'].first['exception_class']).to eq(failure_3.exception_class) + end + end + + describe 'GET /bulk_imports/:id/entities/:entity_id' do + it 'returns specified bulk import entity' do + get api("/bulk_imports/#{import_1.id}/entities/#{entity_2.id}", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['id']).to eq(entity_2.id) + end + end + + context 'when user is unauthenticated' do + it 'returns 401' do + get api('/bulk_imports', nil) + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end +end diff --git a/spec/requests/api/ci/pipelines_spec.rb b/spec/requests/api/ci/pipelines_spec.rb index 092cd00630e..eb6c0861844 100644 --- a/spec/requests/api/ci/pipelines_spec.rb +++ b/spec/requests/api/ci/pipelines_spec.rb @@ -491,6 +491,7 @@ RSpec.describe API::Ci::Pipelines do describe 'GET /projects/:id/pipelines/:pipeline_id/bridges' do let_it_be(:bridge) { create(:ci_bridge, pipeline: pipeline) } + let(:downstream_pipeline) { create(:ci_pipeline) } let!(:pipeline_source) do diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb index 8896bd44077..00c3a0a31af 100644 --- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb +++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb @@ -297,7 +297,13 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do end context 'when job filtered by job_age' do - let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, queued_at: 60.seconds.ago) } + let!(:job) do + create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, queued_at: 60.seconds.ago) + end + + before do + job.queuing_entry&.update!(created_at: 60.seconds.ago) + end context 'job is queued less than job_age parameter' do let(:job_age) { 120 } @@ -797,29 +803,16 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do end context 'when a runner supports this feature' do - it 'exposes excluded paths when the feature is enabled' do - stub_feature_flags(ci_artifacts_exclude: true) - + it 'exposes excluded paths' do request_job info: { features: { artifacts_exclude: true } } expect(response).to have_gitlab_http_status(:created) expect(json_response.dig('artifacts').first).to include('exclude' => ['cde']) end - - it 'does not expose excluded paths when the feature is disabled' do - stub_feature_flags(ci_artifacts_exclude: false) - - request_job info: { features: { artifacts_exclude: true } } - - expect(response).to have_gitlab_http_status(:created) - expect(json_response.dig('artifacts').first).not_to have_key('exclude') - end end context 'when a runner does not support this feature' do it 'does not expose the build at all' do - stub_feature_flags(ci_artifacts_exclude: true) - request_job expect(response).to have_gitlab_http_status(:no_content) diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb index e077a174b08..e20c7e36096 100644 --- a/spec/requests/api/ci/runner/jobs_trace_spec.rb +++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do +RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do include StubGitlabCalls include RedisHelpers include WorkhorseHelpers @@ -142,7 +142,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do context 'when redis data are flushed' do before do - redis_shared_state_cleanup! + redis_trace_chunks_cleanup! end it 'has empty trace' do @@ -272,6 +272,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do it { expect(response).to have_gitlab_http_status(:forbidden) } end + context 'when the job trace is too big' do + before do + project.actual_limits.update!(ci_jobs_trace_size_limit: 1) + end + + it 'returns 403 Forbidden' do + patch_the_trace(' appended', headers.merge({ 'Content-Range' => "#{1.megabyte}-#{1.megabyte + 9}" })) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + def patch_the_trace(content = ' appended', request_headers = nil, job_id: job.id) unless request_headers job.trace.read do |stream| diff --git a/spec/requests/api/ci/runner/runners_post_spec.rb b/spec/requests/api/ci/runner/runners_post_spec.rb index 1696fe63d5d..6d222046998 100644 --- a/spec/requests/api/ci/runner/runners_post_spec.rb +++ b/spec/requests/api/ci/runner/runners_post_spec.rb @@ -11,8 +11,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do before do stub_feature_flags(ci_enable_live_trace: true) + stub_feature_flags(runner_registration_control: false) stub_gitlab_calls stub_application_setting(runners_registration_token: registration_token) + stub_application_setting(valid_runner_registrars: ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES) allow_any_instance_of(::Ci::Runner).to receive(:cache_attributes) end @@ -122,6 +124,33 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do expect(project.runners.recent.size).to eq(1) end end + + context 'when valid runner registrars do not include project' do + before do + stub_application_setting(valid_runner_registrars: ['group']) + end + + context 'when feature flag is enabled' do + before do + stub_feature_flags(runner_registration_control: true) + end + + it 'returns 403 error' do + request + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when feature flag is disabled' do + it 'registers the runner' do + request + + expect(response).to have_gitlab_http_status(:created) + expect(::Ci::Runner.first.active).to be true + end + end + end end context 'when group token is used' do @@ -180,6 +209,33 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do expect(group.runners.recent.size).to eq(1) end end + + context 'when valid runner registrars do not include group' do + before do + stub_application_setting(valid_runner_registrars: ['project']) + end + + context 'when feature flag is enabled' do + before do + stub_feature_flags(runner_registration_control: true) + end + + it 'returns 403 error' do + request + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when feature flag is disabled' do + it 'registers the runner' do + request + + expect(response).to have_gitlab_http_status(:created) + expect(::Ci::Runner.first.active).to be true + end + end + end end end diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb index 4120edabea3..e75725cacba 100644 --- a/spec/requests/api/composer_packages_spec.rb +++ b/spec/requests/api/composer_packages_spec.rb @@ -9,6 +9,7 @@ RSpec.describe API::ComposerPackages do let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let_it_be(:package_name) { 'package-name' } let_it_be(:project, reload: true) { create(:project, :custom_repo, files: { 'composer.json' => { name: package_name }.to_json }, group: group) } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } let(:headers) { {} } diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb index c3abb06c5c1..931eaf41891 100644 --- a/spec/requests/api/debian_group_packages_spec.rb +++ b/spec/requests/api/debian_group_packages_spec.rb @@ -15,23 +15,23 @@ RSpec.describe API::DebianGroupPackages do describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Release$/ + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^Codename: fixture-distribution\n$/ end describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^Codename: fixture-distribution\n$/ end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do - let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" } + let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/ + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /Description: This is an incomplete Packages file/ end describe 'GET groups/:id/-/packages/debian/pool/:component/:letter/:source_package/:file_name' do - let(:url) { "/groups/#{container.id}/-/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } + let(:url) { "/groups/#{container.id}/-/packages/debian/pool/#{component.name}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture.name}.deb" } it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/ end diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb index c11c4ecc12a..fb7da467322 100644 --- a/spec/requests/api/debian_project_packages_spec.rb +++ b/spec/requests/api/debian_project_packages_spec.rb @@ -15,23 +15,23 @@ RSpec.describe API::DebianProjectPackages do describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Release$/ + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^Codename: fixture-distribution\n$/ end describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^Codename: fixture-distribution\n$/ end describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do - let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" } + let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages" } - it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/ + it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /Description: This is an incomplete Packages file/ end describe 'GET projects/:id/packages/debian/pool/:component/:letter/:source_package/:file_name' do - let(:url) { "/projects/#{container.id}/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } + let(:url) { "/projects/#{container.id}/packages/debian/pool/#{component.name}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture.name}.deb" } it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/ end diff --git a/spec/requests/api/deploy_tokens_spec.rb b/spec/requests/api/deploy_tokens_spec.rb index e8426270622..38380fa4460 100644 --- a/spec/requests/api/deploy_tokens_spec.rb +++ b/spec/requests/api/deploy_tokens_spec.rb @@ -7,6 +7,7 @@ RSpec.describe API::DeployTokens do let_it_be(:creator) { create(:user) } let_it_be(:project) { create(:project, creator_id: creator.id) } let_it_be(:group) { create(:group) } + let!(:deploy_token) { create(:deploy_token, projects: [project]) } let!(:revoked_deploy_token) { create(:deploy_token, projects: [project], revoked: true) } let!(:expired_deploy_token) { create(:deploy_token, projects: [project], expires_at: '1988-01-11T04:33:04-0600') } diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb index bbfe37cb70b..38c96cd37af 100644 --- a/spec/requests/api/deployments_spec.rb +++ b/spec/requests/api/deployments_spec.rb @@ -456,6 +456,7 @@ RSpec.describe API::Deployments do context 'when a user member of the deployment project' do let_it_be(:project2) { create(:project) } + let!(:merge_request1) { create(:merge_request, source_project: project, target_project: project) } let!(:merge_request2) { create(:merge_request, source_project: project, target_project: project, state: 'closed') } let!(:merge_request3) { create(:merge_request, source_project: project2, target_project: project2) } diff --git a/spec/requests/api/error_tracking_collector_spec.rb b/spec/requests/api/error_tracking_collector_spec.rb new file mode 100644 index 00000000000..52d63410e7a --- /dev/null +++ b/spec/requests/api/error_tracking_collector_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::ErrorTrackingCollector do + let_it_be(:project) { create(:project, :private) } + let_it_be(:setting) { create(:project_error_tracking_setting, project: project) } + + describe "POST /error_tracking/collector/api/:id/envelope" do + let_it_be(:raw_event) { fixture_file('error_tracking/event.txt') } + let_it_be(:url) { "/error_tracking/collector/api/#{project.id}/envelope" } + + let(:params) { raw_event } + + subject { post api(url), params: params } + + RSpec.shared_examples 'not found' do + it 'reponds with 404' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + RSpec.shared_examples 'bad request' do + it 'responds with 400' do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'error tracking feature is disabled' do + before do + setting.update!(enabled: false) + end + + it_behaves_like 'not found' + end + + context 'feature flag is disabled' do + before do + stub_feature_flags(integrated_error_tracking: false) + end + + it_behaves_like 'not found' + end + + context 'empty body' do + let(:params) { '' } + + it_behaves_like 'bad request' + end + + context 'unknown request type' do + let(:params) { fixture_file('error_tracking/unknown.txt') } + + it_behaves_like 'bad request' + end + + context 'transaction request type' do + let(:params) { fixture_file('error_tracking/transaction.txt') } + + it 'does nothing and returns no content' do + expect { subject }.not_to change { ErrorTracking::ErrorEvent.count } + + expect(response).to have_gitlab_http_status(:no_content) + end + end + + it 'writes to the database and returns no content' do + expect { subject }.to change { ErrorTracking::ErrorEvent.count }.by(1) + + expect(response).to have_gitlab_http_status(:no_content) + end + end +end diff --git a/spec/requests/api/error_tracking_spec.rb b/spec/requests/api/error_tracking_spec.rb index 8c9ca1b6a9d..39121af7bc3 100644 --- a/spec/requests/api/error_tracking_spec.rb +++ b/spec/requests/api/error_tracking_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe API::ErrorTracking do let_it_be(:user) { create(:user) } + let(:setting) { create(:project_error_tracking_setting) } let(:project) { setting.project } diff --git a/spec/requests/api/feature_flags_spec.rb b/spec/requests/api/feature_flags_spec.rb index 2cd52c0a5e5..8edf8825fb2 100644 --- a/spec/requests/api/feature_flags_spec.rb +++ b/spec/requests/api/feature_flags_spec.rb @@ -8,6 +8,7 @@ RSpec.describe API::FeatureFlags do let_it_be(:developer) { create(:user) } let_it_be(:reporter) { create(:user) } let_it_be(:non_project_member) { create(:user) } + let(:user) { developer } before_all do diff --git a/spec/requests/api/freeze_periods_spec.rb b/spec/requests/api/freeze_periods_spec.rb index 5589d4d543d..3da992301d5 100644 --- a/spec/requests/api/freeze_periods_spec.rb +++ b/spec/requests/api/freeze_periods_spec.rb @@ -6,6 +6,7 @@ RSpec.describe API::FreezePeriods do let_it_be(:project) { create(:project, :repository, :private) } let_it_be(:user) { create(:user) } let_it_be(:admin) { create(:admin) } + let(:api_user) { user } let(:invalid_cron) { '0 0 0 * *' } let(:last_freeze_period) { project.freeze_periods.last } 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/graphql/boards/board_lists_query_spec.rb b/spec/requests/api/graphql/boards/board_lists_query_spec.rb index cd94ce91071..2d52cddcacc 100644 --- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb +++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb @@ -66,6 +66,7 @@ RSpec.describe 'get board lists' do describe 'sorting and pagination' do let_it_be(:current_user) { user } + let(:data_path) { [board_parent_type, :boards, :nodes, 0, :lists] } def pagination_query(params) diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb index 3fb89d6e815..10f05efa1b8 100644 --- a/spec/requests/api/graphql/ci/jobs_spec.rb +++ b/spec/requests/api/graphql/ci/jobs_spec.rb @@ -38,9 +38,15 @@ RSpec.describe 'Query.project.pipeline' do name groups { nodes { + detailedStatus { + id + } name jobs { nodes { + detailedStatus { + id + } name needs { nodes { #{all_graphql_fields_for('CiBuildNeed')} } diff --git a/spec/requests/api/graphql/ci/pipelines_spec.rb b/spec/requests/api/graphql/ci/pipelines_spec.rb index f207636283f..6587061094d 100644 --- a/spec/requests/api/graphql/ci/pipelines_spec.rb +++ b/spec/requests/api/graphql/ci/pipelines_spec.rb @@ -229,6 +229,7 @@ RSpec.describe 'Query.project(fullPath).pipelines' do let_it_be(:pipeline) { create(:ci_pipeline, project: project, user: user) } let_it_be(:upstream_project) { create(:project, :repository, :public) } let_it_be(:upstream_pipeline) { create(:ci_pipeline, project: upstream_project, user: user) } + let(:upstream_pipelines_graphql_data) { graphql_data.dig(*%w[project pipelines nodes]).first['upstream'] } let(:query) do diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index e1f84d23209..cdd46ca4ecc 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -5,25 +5,25 @@ require 'spec_helper' RSpec.describe 'Query.runner(id)' do include GraphqlHelpers - let_it_be(:user) { create_default(:user, :admin) } + let_it_be(:user) { create(:user, :admin) } - let_it_be(:active_runner) do + let_it_be(:active_instance_runner) do create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago, active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600, access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true) end - let_it_be(:inactive_runner) do + let_it_be(:inactive_instance_runner) do create(:ci_runner, :instance, description: 'Runner 2', contacted_at: 1.day.ago, active: false, version: 'adfe157', revision: 'b', ip_address: '10.10.10.10', access_level: 1, run_untagged: true) end def get_runner(id) case id - when :active_runner - active_runner - when :inactive_runner - inactive_runner + when :active_instance_runner + active_instance_runner + when :inactive_instance_runner + inactive_instance_runner end end @@ -59,7 +59,9 @@ RSpec.describe 'Query.runner(id)' do 'accessLevel' => runner.access_level.to_s.upcase, 'runUntagged' => runner.run_untagged, 'ipAddress' => runner.ip_address, - 'runnerType' => 'INSTANCE_TYPE' + 'runnerType' => 'INSTANCE_TYPE', + 'jobCount' => 0, + 'projectCount' => nil ) expect(runner_data['tagList']).to match_array runner.tag_list end @@ -84,38 +86,113 @@ RSpec.describe 'Query.runner(id)' do end describe 'for active runner' do - it_behaves_like 'runner details fetch', :active_runner + it_behaves_like 'runner details fetch', :active_instance_runner + + context 'when tagList is not requested' do + let(:query) do + wrap_fields(query_graphql_path(query_path, 'id')) + end + + let(:query_path) do + [ + [:runner, { id: active_instance_runner.to_global_id.to_s }] + ] + end + + it 'does not retrieve tagList' do + post_graphql(query, current_user: user) + + runner_data = graphql_data_at(:runner) + expect(runner_data).not_to be_nil + expect(runner_data).not_to include('tagList') + end + end end describe 'for inactive runner' do - it_behaves_like 'runner details fetch', :inactive_runner + it_behaves_like 'runner details fetch', :inactive_instance_runner + end + + describe 'for multiple runners' do + let_it_be(:project1) { create(:project, :test_repo) } + let_it_be(:project2) { create(:project, :test_repo) } + let_it_be(:project_runner1) { create(:ci_runner, :project, projects: [project1, project2], description: 'Runner 1') } + let_it_be(:project_runner2) { create(:ci_runner, :project, projects: [], description: 'Runner 2') } + + let!(:job) { create(:ci_build, runner: project_runner1) } + + context 'requesting project and job counts' do + let(:query) do + %( + query { + projectRunner1: runner(id: "#{project_runner1.to_global_id}") { + projectCount + jobCount + } + projectRunner2: runner(id: "#{project_runner2.to_global_id}") { + projectCount + jobCount + } + activeInstanceRunner: runner(id: "#{active_instance_runner.to_global_id}") { + projectCount + jobCount + } + } + ) + end + + before do + project_runner2.projects.clear + + post_graphql(query, current_user: user) + end + + it 'retrieves expected fields' do + runner1_data = graphql_data_at(:project_runner1) + runner2_data = graphql_data_at(:project_runner2) + runner3_data = graphql_data_at(:active_instance_runner) + + expect(runner1_data).to match a_hash_including( + 'jobCount' => 1, + 'projectCount' => 2) + expect(runner2_data).to match a_hash_including( + 'jobCount' => 0, + 'projectCount' => 0) + expect(runner3_data).to match a_hash_including( + 'jobCount' => 0, + 'projectCount' => nil) + end + end end describe 'by regular user' do - let(:user) { create_default(:user) } + let(:user) { create(:user) } - it_behaves_like 'retrieval by unauthorized user', :active_runner + it_behaves_like 'retrieval by unauthorized user', :active_instance_runner end describe 'by unauthenticated user' do let(:user) { nil } - it_behaves_like 'retrieval by unauthorized user', :active_runner + it_behaves_like 'retrieval by unauthorized user', :active_instance_runner end describe 'Query limits' do def runner_query(runner) <<~SINGLE runner(id: "#{runner.to_global_id}") { - #{all_graphql_fields_for('CiRunner')} + #{all_graphql_fields_for('CiRunner', excluded: excluded_fields)} } SINGLE end + # Currently excluding a known N+1 issue, see https://gitlab.com/gitlab-org/gitlab/-/issues/334759 + let(:excluded_fields) { %w[jobCount] } + let(:single_query) do <<~QUERY { - active: #{runner_query(active_runner)} + active: #{runner_query(active_instance_runner)} } QUERY end @@ -123,8 +200,8 @@ RSpec.describe 'Query.runner(id)' do let(:double_query) do <<~QUERY { - active: #{runner_query(active_runner)} - inactive: #{runner_query(inactive_runner)} + active: #{runner_query(active_instance_runner)} + inactive: #{runner_query(inactive_instance_runner)} } QUERY end diff --git a/spec/requests/api/graphql/current_user_todos_spec.rb b/spec/requests/api/graphql/current_user_todos_spec.rb index b657f15d0e9..7f37abba74a 100644 --- a/spec/requests/api/graphql/current_user_todos_spec.rb +++ b/spec/requests/api/graphql/current_user_todos_spec.rb @@ -10,6 +10,7 @@ RSpec.describe 'A Todoable that implements the CurrentUserTodos interface' do let_it_be(:todoable) { create(:issue, project: project) } let_it_be(:done_todo) { create(:todo, state: :done, target: todoable, user: current_user) } let_it_be(:pending_todo) { create(:todo, state: :pending, target: todoable, user: current_user) } + let(:state) { 'null' } let(:todoable_response) do diff --git a/spec/requests/api/graphql/issue_status_counts_spec.rb b/spec/requests/api/graphql/issue_status_counts_spec.rb index 3d8817c3bc5..89ecbf44b10 100644 --- a/spec/requests/api/graphql/issue_status_counts_spec.rb +++ b/spec/requests/api/graphql/issue_status_counts_spec.rb @@ -9,6 +9,7 @@ RSpec.describe 'getting Issue counts by status' do let_it_be(:issue_opened) { create(:issue, project: project) } let_it_be(:issue_closed) { create(:issue, :closed, project: project) } let_it_be(:other_project_issue) { create(:issue) } + let(:params) { {} } let(:fields) do diff --git a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb index e01f59ee6a0..1b84acff0e2 100644 --- a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb +++ b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Getting Metrics Dashboard' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } + let(:project) { create(:project) } let(:environment) { create(:environment, project: project) } diff --git a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb index b8cde32877b..1692cfbcf84 100644 --- a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb +++ b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do include GraphqlHelpers let_it_be(:admin) { create(:admin) } + let(:queue) { 'authorized_projects' } let(:variables) { { user: admin.username, queue_name: queue } } diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb index cd5cefa0a9a..fcef7b4e3ec 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Setting assignees of an alert' do let_it_be(:project) { create(:project) } let_it_be(:current_user) { create(:user) } let_it_be(:alert) { create(:alert_management_alert, project: project) } + let(:input) { { assignee_usernames: [current_user.username] } } let(:mutation) do diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb index cd423d7764a..48307964345 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Creating a todo for the alert' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } + let(:alert) { create(:alert_management_alert, project: project) } let(:mutation) do diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb index ff55656a2ae..802d8d6c5a1 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Setting the status of an alert' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } + let(:alert) { create(:alert_management_alert, project: project) } let(:input) { { status: 'ACKNOWLEDGED' } } diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb index e594d67aab4..ff93da2153f 100644 --- a/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Creating a new HTTP Integration' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } + let(:variables) do { project_path: project.full_path, diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb index 0ef61ae0d5b..4c359d9b357 100644 --- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Creating a new Prometheus Integration' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } + let(:variables) do { project_path: project.full_path, @@ -42,7 +43,7 @@ RSpec.describe 'Creating a new Prometheus Integration' do it 'creates a new integration' do post_graphql_mutation(mutation, current_user: current_user) - new_integration = ::PrometheusService.last! + new_integration = ::Integrations::Prometheus.last! integration_response = mutation_response['integration'] expect(response).to have_gitlab_http_status(:success) diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb index d8d0ace5981..31053c50cac 100644 --- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Resetting a token on an existing Prometheus Integration' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:integration) { create(:prometheus_service, project: project) } + let_it_be(:integration) { create(:prometheus_integration, project: project) } let(:mutation) do variables = { diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb index 6c4a647a353..ad26ec118d7 100644 --- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Updating an existing Prometheus Integration' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:integration) { create(:prometheus_service, project: project) } + let_it_be(:integration) { create(:prometheus_integration, project: project) } let(:mutation) do variables = { diff --git a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb index b39062f2e71..fdf5503a3a2 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Adding an AwardEmoji' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:awardable) { create(:note, project: project) } + let(:emoji_name) { 'thumbsup' } let(:mutation) do variables = { diff --git a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb index 170e7ff3b44..6b26e37e30c 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Toggling an AwardEmoji' do let_it_be(:current_user) { create(:user) } let_it_be(:project, reload: true) { create(:project) } let_it_be(:awardable) { create(:note, project: project) } + let(:emoji_name) { 'thumbsup' } let(:mutation) do variables = { diff --git a/spec/requests/api/graphql/mutations/boards/create_spec.rb b/spec/requests/api/graphql/mutations/boards/create_spec.rb index c5f981262ea..22d05f36f0f 100644 --- a/spec/requests/api/graphql/mutations/boards/create_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/create_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Mutations::Boards::Create do let_it_be(:parent) { create(:project) } + let(:project_path) { parent.full_path } let(:params) do { diff --git a/spec/requests/api/graphql/mutations/branches/create_spec.rb b/spec/requests/api/graphql/mutations/branches/create_spec.rb index fc09f57a389..6a098002963 100644 --- a/spec/requests/api/graphql/mutations/branches/create_spec.rb +++ b/spec/requests/api/graphql/mutations/branches/create_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Creation of a new branch' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project, :public, :empty_repo) } + let(:input) { { project_path: project.full_path, name: new_branch, ref: ref } } let(:new_branch) { 'new_branch' } let(:ref) { 'master' } @@ -34,11 +35,12 @@ RSpec.describe 'Creation of a new branch' do end context 'when ref is not correct' do + err_msg = 'Failed to create branch \'another_branch\': invalid reference name \'unknown\'' let(:new_branch) { 'another_branch' } let(:ref) { 'unknown' } it_behaves_like 'a mutation that returns errors in the response', - errors: ['Invalid reference name: unknown'] + errors: [err_msg] end end end diff --git a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb index 0d7571d91ca..05f6804a208 100644 --- a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb @@ -5,7 +5,10 @@ require 'spec_helper' RSpec.describe 'CiCdSettingsUpdate' do include GraphqlHelpers - let_it_be(:project) { create(:project, keep_latest_artifact: true, ci_job_token_scope_enabled: true) } + let_it_be(:project) do + create(:project, keep_latest_artifact: true, ci_job_token_scope_enabled: true) + .tap(&:save!) + end let(:variables) do { diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb new file mode 100644 index 00000000000..b53a7ddde32 --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'CiJobTokenScopeAddProject' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:target_project) { create(:project) } + + let(:variables) do + { + project_path: project.full_path, + target_project_path: target_project.full_path + } + end + + let(:mutation) do + graphql_mutation(:ci_job_token_scope_add_project, variables) do + <<~QL + errors + ciJobTokenScope { + projects { + nodes { + path + } + } + } + QL + end + end + + let(:mutation_response) { graphql_mutation_response(:ci_job_token_scope_add_project) } + + context 'when unauthorized' do + let(:current_user) { create(:user) } + + context 'when not a maintainer' do + before do + project.add_developer(current_user) + end + + it 'has graphql errors' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(graphql_errors).not_to be_empty + end + end + end + + context 'when authorized' do + let_it_be(:current_user) { project.owner } + + before do + target_project.add_developer(current_user) + end + + it 'adds the target project to the job token scope' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty + end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(false).to(true) + end + + context 'when invalid target project is provided' do + before do + variables[:target_project_path] = 'unknown/project' + end + + it 'has mutation errors' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['errors']).to contain_exactly(Ci::JobTokenScope::EditScopeValidations::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND) + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb new file mode 100644 index 00000000000..f1f42b00ada --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'CiJobTokenScopeRemoveProject' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:target_project) { create(:project) } + + let_it_be(:link) do + create(:ci_job_token_project_scope_link, + source_project: project, + target_project: target_project) + end + + let(:variables) do + { + project_path: project.full_path, + target_project_path: target_project.full_path + } + end + + let(:mutation) do + graphql_mutation(:ci_job_token_scope_remove_project, variables) do + <<~QL + errors + ciJobTokenScope { + projects { + nodes { + path + } + } + } + QL + end + end + + let(:mutation_response) { graphql_mutation_response(:ci_job_token_scope_remove_project) } + + context 'when unauthorized' do + let(:current_user) { create(:user) } + + context 'when not a maintainer' do + before do + project.add_developer(current_user) + end + + it 'has graphql errors' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(graphql_errors).not_to be_empty + end + end + end + + context 'when authorized' do + let_it_be(:current_user) { project.owner } + + before do + target_project.add_guest(current_user) + end + + it 'removes the target project from the job token scope' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty + end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(true).to(false) + end + + context 'when invalid target project is provided' do + before do + variables[:target_project_path] = 'unknown/project' + end + + it 'has mutation errors' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['errors']).to contain_exactly(Ci::JobTokenScope::EditScopeValidations::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND) + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/commits/create_spec.rb b/spec/requests/api/graphql/mutations/commits/create_spec.rb index 375d4f10b40..619cba99c4e 100644 --- a/spec/requests/api/graphql/mutations/commits/create_spec.rb +++ b/spec/requests/api/graphql/mutations/commits/create_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Creation of a new commit' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project, :public, :repository) } + let(:input) { { project_path: project.full_path, branch: branch, message: message, actions: actions } } let(:branch) { 'master' } let(:message) { 'Commit message' } diff --git a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb index 23e8e366483..0156142dc6f 100644 --- a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb +++ b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb @@ -53,6 +53,7 @@ RSpec.describe 'Updating the container expiration policy' do RSpec.shared_examples 'rejecting invalid regex for' do |field_name| context "for field #{field_name}" do let_it_be(:invalid_regex) { '*production' } + let(:params) do { :project_path => project.full_path, diff --git a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb index 450996bf76b..632a934cd95 100644 --- a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb +++ b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Toggling the resolve status of a discussion' do let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:noteable) { create(:merge_request, source_project: project) } + let(:discussion) do create(:diff_note_on_merge_request, noteable: noteable, project: project).to_discussion end diff --git a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb index f25a49291a6..3771ae0746e 100644 --- a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb +++ b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb @@ -13,6 +13,7 @@ RSpec.describe 'Update Environment Canary Ingress', :clean_gitlab_redis_cache do let_it_be(:deployment) { create(:deployment, :success, environment: environment, project: project) } let_it_be(:maintainer) { create(:user) } let_it_be(:developer) { create(:user) } + let(:environment_id) { environment.to_global_id.to_s } let(:weight) { 25 } let(:actor) { developer } diff --git a/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb b/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb index 4989d096925..435ed0f9eb2 100644 --- a/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Setting an issue as locked' do let_it_be(:current_user) { create(:user) } let_it_be(:issue) { create(:issue) } let_it_be(:project) { issue.project } + let(:input) { { locked: true } } let(:mutation) do diff --git a/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb b/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb index 96fd2368765..41997f151a2 100644 --- a/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Setting severity level of an incident' do include GraphqlHelpers let_it_be(:user) { create(:user) } + let(:incident) { create(:incident) } let(:project) { incident.project } let(:input) { { severity: 'CRITICAL' } } diff --git a/spec/requests/api/graphql/mutations/issues/update_spec.rb b/spec/requests/api/graphql/mutations/issues/update_spec.rb index adfa2a2bc08..b3e1ab62e54 100644 --- a/spec/requests/api/graphql/mutations/issues/update_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/update_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Update of an existing issue' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project, :public) } let_it_be(:issue) { create(:issue, project: project) } + let(:input) do { 'iid' => issue.iid.to_s, diff --git a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb index 00b93984f98..45cc70f09fd 100644 --- a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb +++ b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Importing Jira Users' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } + let(:importer) { instance_double(JiraImport::UsersImporter) } let(:project_path) { project.full_path } let(:start_at) { 7 } diff --git a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb index e7124512ef1..b14305281af 100644 --- a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb +++ b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Starting a Jira Import' do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project) } + let(:jira_project_key) { 'AA' } let(:project_path) { project.full_path } @@ -80,17 +81,17 @@ RSpec.describe 'Starting a Jira Import' do end end - context 'when project has no Jira service' do + context 'when project has no Jira integration' do it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira integration not configured.'] end - context 'when when project has Jira service' do - let!(:service) { create(:jira_service, project: project) } + context 'when when project has Jira integration' do + let!(:service) { create(:jira_integration, project: project) } before do project.reload - stub_jira_service_test + stub_jira_integration_test end context 'when issues feature are disabled' do diff --git a/spec/requests/api/graphql/mutations/labels/create_spec.rb b/spec/requests/api/graphql/mutations/labels/create_spec.rb index 28284408306..d19411f6c1d 100644 --- a/spec/requests/api/graphql/mutations/labels/create_spec.rb +++ b/spec/requests/api/graphql/mutations/labels/create_spec.rb @@ -61,6 +61,7 @@ RSpec.describe Mutations::Labels::Create do context 'when creating a project label' do let_it_be(:parent) { create(:project) } + let(:extra_params) { { project_path: parent.full_path } } it_behaves_like 'labels create mutation' @@ -68,6 +69,7 @@ RSpec.describe Mutations::Labels::Create do context 'when creating a group label' do let_it_be(:parent) { create(:group) } + let(:extra_params) { { group_path: parent.full_path } } it_behaves_like 'labels create mutation' diff --git a/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb index 2725b33d528..19a7c72ba80 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'accepting a merge request', :request_store do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project, :public, :repository) } + let!(:merge_request) { create(:merge_request, source_project: project) } let(:input) do { diff --git a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb index bf759521dc0..3a4508489a1 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Creation of a new merge request' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } + let(:project) { create(:project, :public, :repository) } let(:input) do { diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb index a63116e2b94..dec9afd1310 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb @@ -68,7 +68,7 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled do context 'when the current user does not have permission to add assignees' do let(:current_user) { create(:user) } - let(:db_query_limit) { 28 } + let(:db_query_limit) { 27 } it 'does not change the assignees' do project.add_guest(current_user) @@ -80,7 +80,7 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled do end context 'with assignees already assigned' do - let(:db_query_limit) { 46 } + let(:db_query_limit) { 39 } before do merge_request.assignees = [assignee2] @@ -96,7 +96,7 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled do end context 'when passing an empty list of assignees' do - let(:db_query_limit) { 32 } + let(:db_query_limit) { 31 } let(:input) { { assignee_usernames: [] } } before do @@ -115,7 +115,7 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled do context 'when passing append as true' do let(:mode) { Types::MutationOperationModeEnum.enum[:append] } let(:input) { { assignee_usernames: [assignee2.username], operation_mode: mode } } - let(:db_query_limit) { 22 } + let(:db_query_limit) { 21 } before do # In CE, APPEND is a NOOP as you can't have multiple assignees @@ -135,7 +135,7 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled do end context 'when passing remove as true' do - let(:db_query_limit) { 32 } + let(:db_query_limit) { 31 } let(:mode) { Types::MutationOperationModeEnum.enum[:remove] } let(:input) { { assignee_usernames: [assignee.username], operation_mode: mode } } let(:expected_result) { [] } diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb index 2a39757e103..5bc3c68cf26 100644 --- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb +++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do let_it_be(:project) { create(:project, :private, :repository) } let_it_be(:environment) { create(:environment, project: project) } let_it_be(:cluster) { create(:cluster, projects: [project]) } + let(:dashboard_path) { 'config/prometheus/common_metrics.yml' } let(:starting_at) { Time.current.iso8601 } let(:ending_at) { 1.hour.from_now.iso8601 } diff --git a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb index 202e7e7c333..d335642d321 100644 --- a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb +++ b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb @@ -126,6 +126,7 @@ RSpec.describe 'Updating the package settings' do context 'without existing package settings' do let_it_be(:namespace, reload: true) { create(:group) } + let(:package_settings) { namespace.package_settings } where(:user_role, :shared_examples_name) do diff --git a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb index b5aaf304812..8f3ae9f26f6 100644 --- a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Adding a DiffNote' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } let(:project) { create(:project, :repository) } let(:diff_refs) { noteable.diff_refs } diff --git a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb index 0e5744fb64f..8f2438cb741 100644 --- a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Adding an image DiffNote' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } let(:project) { create(:project, :repository) } let(:diff_refs) { noteable.diff_refs } diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb index 8dd8ed361ba..87c752393ea 100644 --- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Adding a Note' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } let(:project) { create(:project) } let(:discussion) { nil } diff --git a/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb index 4efa7f9d509..89e3a71280f 100644 --- a/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Repositioning an ImageDiffNote' do let_it_be(:noteable) { create(:merge_request) } let_it_be(:project) { noteable.project } + let(:note) { create(:image_diff_note_on_merge_request, noteable: noteable, project: project) } let(:new_position) { { x: 10 } } let(:current_user) { project.creator } diff --git a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb index 1ce09881fde..cfd0b34b815 100644 --- a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb @@ -26,6 +26,7 @@ RSpec.describe 'Updating an image DiffNote' do let_it_be(:updated_height) { 100 } let_it_be(:updated_x) { 5 } let_it_be(:updated_y) { 10 } + let(:updated_position) do { width: updated_width, diff --git a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb new file mode 100644 index 00000000000..e5ced419ecf --- /dev/null +++ b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Destroying a package' do + using RSpec::Parameterized::TableSyntax + + include GraphqlHelpers + + let_it_be_with_reload(:package) { create(:package) } + let_it_be(:user) { create(:user) } + + let(:project) { package.project } + let(:id) { package.to_global_id.to_s } + + let(:query) do + <<~GQL + errors + GQL + end + + let(:params) { { id: id } } + let(:mutation) { graphql_mutation(:destroy_package, params, query) } + let(:mutation_response) { graphql_mutation_response(:destroyPackage) } + + shared_examples 'destroying the package' do + it 'destroy the package' do + expect(::Packages::DestroyPackageService) + .to receive(:new).with(container: package, current_user: user).and_call_original + + expect { mutation_request }.to change { ::Packages::Package.count }.by(-1) + end + + it_behaves_like 'returning response status', :success + end + + shared_examples 'denying the mutation request' do + it 'does not destroy the package' do + expect(::Packages::DestroyPackageService) + .not_to receive(:new).with(container: package, current_user: user) + + expect { mutation_request }.not_to change { ::Packages::Package.count } + + expect(mutation_response).to be_nil + end + + it_behaves_like 'returning response status', :success + end + + describe 'post graphql mutation' do + subject(:mutation_request) { post_graphql_mutation(mutation, current_user: user) } + + context 'with valid id' do + where(:user_role, :shared_examples_name) do + :maintainer | 'destroying the package' + :developer | 'denying the mutation request' + :reporter | 'denying the mutation request' + :guest | 'denying the mutation request' + :anonymous | 'denying the mutation request' + end + + with_them do + before do + project.send("add_#{user_role}", user) unless user_role == :anonymous + end + + it_behaves_like params[:shared_examples_name] + end + end + + context 'with invalid id' do + let(:params) { { id: 'gid://gitlab/Packages::Package/5555' } } + + it_behaves_like 'denying the mutation request' + end + + context 'when an error occures' do + before do + project.add_maintainer(user) + end + + it 'returns the errors in the response' do + allow_next_found_instance_of(::Packages::Package) do |package| + allow(package).to receive(:destroy!).and_raise(StandardError) + end + + mutation_request + + expect(mutation_response['errors']).to eq(['Failed to remove the package']) + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/releases/delete_spec.rb b/spec/requests/api/graphql/mutations/releases/delete_spec.rb index 3710f118bf4..40063156609 100644 --- a/spec/requests/api/graphql/mutations/releases/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/releases/delete_spec.rb @@ -55,7 +55,7 @@ RSpec.describe 'Deleting a release' do end context 'when the current user has access to update releases' do - let(:current_user) { maintainer } + let(:current_user) { developer } it 'deletes the release' do expect { delete_release }.to change { Release.count }.by(-1) @@ -105,12 +105,6 @@ RSpec.describe 'Deleting a release' do end context "when the current user doesn't have access to update releases" do - context 'when the current user is a Developer' do - let(:current_user) { developer } - - it_behaves_like 'unauthorized or not found error' - end - context 'when the current user is a Reporter' do let(:current_user) { reporter } diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb index 214c804c519..9a3cea3ca14 100644 --- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb @@ -17,7 +17,6 @@ RSpec.describe 'Creating a Snippet' do let(:actions) { [{ action: action }.merge(file_1), { action: action }.merge(file_2)] } let(:project_path) { nil } let(:uploaded_files) { nil } - let(:spam_mutation_vars) { {} } let(:mutation_vars) do { description: description, @@ -26,7 +25,7 @@ RSpec.describe 'Creating a Snippet' do project_path: project_path, uploaded_files: uploaded_files, blob_actions: actions - }.merge(spam_mutation_vars) + } end let(:mutation) do @@ -77,21 +76,6 @@ RSpec.describe 'Creating a Snippet' do expect(mutation_response['snippet']).to be_nil end - - context 'when snippet_spam flag is disabled' do - before do - stub_feature_flags(snippet_spam: false) - end - - it 'passes disable_spam_action_service param to service' do - expect(::Snippets::CreateService) - .to receive(:new) - .with(project: anything, current_user: anything, params: hash_including(disable_spam_action_service: true)) - .and_call_original - - subject - end - end end shared_examples 'creates snippet' do @@ -101,8 +85,8 @@ RSpec.describe 'Creating a Snippet' do end.to change { Snippet.count }.by(1) snippet = Snippet.last - created_file_1 = snippet.repository.blob_at('HEAD', file_1[:filePath]) - created_file_2 = snippet.repository.blob_at('HEAD', file_2[:filePath]) + created_file_1 = snippet.repository.blob_at(snippet.default_branch, file_1[:filePath]) + created_file_2 = snippet.repository.blob_at(snippet.default_branch, file_2[:filePath]) expect(created_file_1.data).to match(file_1[:content]) expect(created_file_2.data).to match(file_2[:content]) @@ -121,15 +105,6 @@ RSpec.describe 'Creating a Snippet' do it_behaves_like 'snippet edit usage data counters' it_behaves_like 'a mutation which can mutate a spammable' do - let(:captcha_response) { 'abc123' } - let(:spam_log_id) { 1234 } - let(:spam_mutation_vars) do - { - captcha_response: captcha_response, - spam_log_id: spam_log_id - } - end - let(:service) { Snippets::CreateService } end end @@ -190,7 +165,7 @@ RSpec.describe 'Creating a Snippet' do it do expect(::Snippets::CreateService).to receive(:new) - .with(project: nil, current_user: user, params: hash_including(files: expected_value)) + .with(project: nil, current_user: user, params: hash_including(files: expected_value), spam_params: instance_of(::Spam::SpamParams)) .and_return(double(execute: creation_response)) subject diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb index 4d499310591..43d846cb297 100644 --- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb @@ -10,6 +10,7 @@ RSpec.describe 'Mark snippet as spam' do let_it_be(:other_user) { create(:user) } let_it_be(:snippet) { create(:personal_snippet) } let_it_be(:user_agent_detail) { create(:user_agent_detail, subject: snippet) } + let(:current_user) { snippet.author } let(:snippet_gid) { snippet.to_global_id.to_s } diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb index 77efb786dcb..eb7e6f840fe 100644 --- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb @@ -9,6 +9,7 @@ RSpec.describe 'Updating a Snippet' do let_it_be(:original_description) { 'Initial description' } let_it_be(:original_title) { 'Initial title' } let_it_be(:original_file_name) { 'Initial file_name' } + let(:updated_content) { 'Updated content' } let(:updated_description) { 'Updated description' } let(:updated_title) { 'Updated_title' } @@ -16,7 +17,6 @@ RSpec.describe 'Updating a Snippet' do let(:updated_file) { 'CHANGELOG' } let(:deleted_file) { 'README' } let(:snippet_gid) { GitlabSchema.id_from_object(snippet).to_s } - let(:spam_mutation_vars) { {} } let(:mutation_vars) do { id: snippet_gid, @@ -27,7 +27,7 @@ RSpec.describe 'Updating a Snippet' do { action: :update, filePath: updated_file, content: updated_content }, { action: :delete, filePath: deleted_file } ] - }.merge(spam_mutation_vars) + } end let(:mutation) do @@ -82,21 +82,6 @@ RSpec.describe 'Updating a Snippet' do end end - context 'when snippet_spam flag is disabled' do - before do - stub_feature_flags(snippet_spam: false) - end - - it 'passes disable_spam_action_service param to service' do - expect(::Snippets::UpdateService) - .to receive(:new) - .with(project: anything, current_user: anything, params: hash_including(disable_spam_action_service: true)) - .and_call_original - - subject - end - end - context 'when there are ActiveRecord validation errors' do let(:updated_title) { '' } @@ -125,15 +110,6 @@ RSpec.describe 'Updating a Snippet' do end it_behaves_like 'a mutation which can mutate a spammable' do - let(:captcha_response) { 'abc123' } - let(:spam_log_id) { 1234 } - let(:spam_mutation_vars) do - { - captcha_response: captcha_response, - spam_log_id: spam_log_id - } - end - let(:service) { Snippets::UpdateService } end @@ -164,6 +140,7 @@ RSpec.describe 'Updating a Snippet' do describe 'ProjectSnippet' do let_it_be(:project) { create(:project, :private) } + let(:snippet) do create(:project_snippet, :private, diff --git a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb index cb67a60ebe4..716983f01d2 100644 --- a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb +++ b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Create a user callout' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } + let(:feature_name) { ::UserCallout.feature_names.each_key.first } let(:input) do diff --git a/spec/requests/api/graphql/namespace/package_settings_spec.rb b/spec/requests/api/graphql/namespace/package_settings_spec.rb index 6af098e902f..42fd07dbdc7 100644 --- a/spec/requests/api/graphql/namespace/package_settings_spec.rb +++ b/spec/requests/api/graphql/namespace/package_settings_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'getting namespace package settings in a namespace' do let_it_be(:package_settings) { create(:namespace_package_setting) } let_it_be(:namespace) { package_settings.namespace } let_it_be(:current_user) { namespace.owner } + let(:package_settings_response) { graphql_data.dig('namespace', 'packageSettings') } let(:fields) { all_graphql_fields_for('PackageSettings') } diff --git a/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb index 9724de4fedb..05a98a9dd9c 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'getting Alert Management Alert Issue' do let_it_be(:project) { create(:project) } let_it_be(:current_user) { create(:user) } + let(:payload) { {} } let(:query) { 'avg(metric) > 1.0' } diff --git a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb index 9fbf5aaa41f..ecd93d169d3 100644 --- a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb @@ -9,6 +9,7 @@ RSpec.describe 'getting Alert Management Alert counts by status' do let_it_be(:alert_resolved) { create(:alert_management_alert, :resolved, project: project) } let_it_be(:alert_triggered) { create(:alert_management_alert, project: project) } let_it_be(:other_project_alert) { create(:alert_management_alert) } + let(:params) { {} } let(:fields) do diff --git a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb index 0e029aee9e8..1793d4961eb 100644 --- a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'getting Alert Management Integrations' do let_it_be(:project) { create(:project, :repository) } let_it_be(:current_user) { create(:user) } - let_it_be(:prometheus_service) { create(:prometheus_service, project: project) } + let_it_be(:prometheus_integration) { create(:prometheus_integration, project: project) } let_it_be(:project_alerting_setting) { create(:project_alerting_setting, project: project) } let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) } let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) } @@ -53,15 +53,15 @@ RSpec.describe 'getting Alert Management Integrations' do end context 'when no extra params given' do - let(:http_integration) { integrations.first } - let(:prometheus_integration) { integrations.second } + let(:http_integration_response) { integrations.first } + let(:prometheus_integration_response) { integrations.second } it_behaves_like 'a working graphql query' it { expect(integrations.size).to eq(2) } it 'returns the correct properties of the integrations' do - expect(http_integration).to include( + expect(http_integration_response).to include( 'id' => global_id_of(active_http_integration), 'type' => 'HTTP', 'name' => active_http_integration.name, @@ -71,14 +71,14 @@ RSpec.describe 'getting Alert Management Integrations' do 'apiUrl' => nil ) - expect(prometheus_integration).to include( - 'id' => global_id_of(prometheus_service), + expect(prometheus_integration_response).to include( + 'id' => global_id_of(prometheus_integration), 'type' => 'PROMETHEUS', 'name' => 'Prometheus', - 'active' => prometheus_service.manual_configuration?, + 'active' => prometheus_integration.manual_configuration?, 'token' => project_alerting_setting.token, 'url' => "http://localhost/#{project.full_path}/prometheus/alerts/notify.json", - 'apiUrl' => prometheus_service.api_url + 'apiUrl' => prometheus_integration.api_url ) end end @@ -104,7 +104,7 @@ RSpec.describe 'getting Alert Management Integrations' do end context 'when Prometheus Integration ID is given' do - let(:params) { { id: global_id_of(prometheus_service) } } + let(:params) { { id: global_id_of(prometheus_integration) } } it_behaves_like 'a working graphql query' @@ -112,13 +112,13 @@ RSpec.describe 'getting Alert Management Integrations' do it 'returns the correct properties of the Prometheus Integration' do expect(integrations.first).to include( - 'id' => global_id_of(prometheus_service), + 'id' => global_id_of(prometheus_integration), 'type' => 'PROMETHEUS', 'name' => 'Prometheus', - 'active' => prometheus_service.manual_configuration?, + 'active' => prometheus_integration.manual_configuration?, 'token' => project_alerting_setting.token, 'url' => "http://localhost/#{project.full_path}/prometheus/alerts/notify.json", - 'apiUrl' => prometheus_service.api_url + 'apiUrl' => prometheus_integration.api_url ) end end diff --git a/spec/requests/api/graphql/project/base_service_spec.rb b/spec/requests/api/graphql/project/base_service_spec.rb index af462c4a639..5dc0f55db88 100644 --- a/spec/requests/api/graphql/project/base_service_spec.rb +++ b/spec/requests/api/graphql/project/base_service_spec.rb @@ -7,9 +7,9 @@ RSpec.describe 'query Jira service' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:jira_service) { create(:jira_service, project: project) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } let_it_be(:bugzilla_integration) { create(:bugzilla_integration, project: project) } - let_it_be(:redmine_service) { create(:redmine_service, project: project) } + let_it_be(:redmine_integration) { create(:redmine_integration, project: project) } let(:query) do %( diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb index b2b42137acf..14fabaaf032 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'getting a detailed sentry error' do let_it_be(:project_setting) { create(:project_error_tracking_setting, project: project) } let_it_be(:current_user) { project.owner } let_it_be(:sentry_detailed_error) { build(:detailed_error_tracking_error) } + let(:sentry_gid) { sentry_detailed_error.to_global_id.to_s } let(:fields) do <<~QUERY diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb index c7d327a62af..e71e5a48ddc 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb @@ -17,6 +17,7 @@ RSpec.describe 'sentry errors requests' do describe 'getting a detailed sentry error' do let_it_be(:sentry_detailed_error) { build(:detailed_error_tracking_error) } + let(:sentry_gid) { sentry_detailed_error.to_global_id.to_s } let(:detailed_fields) do @@ -193,6 +194,7 @@ RSpec.describe 'sentry errors requests' do describe 'getting a stack trace' do let_it_be(:sentry_stack_trace) { build(:error_tracking_error_event) } + let(:sentry_gid) { global_id_of(Gitlab::ErrorTracking::DetailedError.new(id: 1)) } let(:stack_trace_fields) do diff --git a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb index decab900a43..def41efddde 100644 --- a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Getting designs related to an issue' do let_it_be(:design) { create(:design, :with_smaller_image_versions, versions_count: 1) } let_it_be(:current_user) { design.project.owner } + let(:design_query) do <<~NODE designs { @@ -124,6 +125,7 @@ RSpec.describe 'Getting designs related to an issue' do context 'with versions' do let_it_be(:version) { design.versions.take } + let(:design_query) do <<~NODE designs { @@ -165,6 +167,7 @@ RSpec.describe 'Getting designs related to an issue' do let_it_be(:issue) { design.issue } let_it_be(:second_design, reload: true) { create(:design, :with_smaller_image_versions, issue: issue, versions_count: 1) } let_it_be(:deleted_design) { create(:design, :with_versions, issue: issue, deleted: true, versions_count: 1) } + let(:all_versions) { issue.design_versions.ordered.reverse } let(:design_query) do <<~NODE diff --git a/spec/requests/api/graphql/project/jira_service_spec.rb b/spec/requests/api/graphql/project/jira_service_spec.rb index 905a669bf0d..64e9e04ae44 100644 --- a/spec/requests/api/graphql/project/jira_service_spec.rb +++ b/spec/requests/api/graphql/project/jira_service_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'query Jira service' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:jira_service) { create(:jira_service, project: project) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } let(:query) do %( diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb index 0a5bcc7a965..cb6755640a9 100644 --- a/spec/requests/api/graphql/project/pipeline_spec.rb +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -8,9 +8,9 @@ RSpec.describe 'getting pipeline information nested in a project' do let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } let_it_be(:current_user) { create(:user) } - let_it_be(:build_job) { create(:ci_build, :trace_with_sections, name: 'build-a', pipeline: pipeline) } - let_it_be(:failed_build) { create(:ci_build, :failed, name: 'failed-build', pipeline: pipeline) } - let_it_be(:bridge) { create(:ci_bridge, name: 'ci-bridge-example', pipeline: pipeline) } + let_it_be(:build_job) { create(:ci_build, :trace_with_sections, name: 'build-a', pipeline: pipeline, stage_idx: 0, stage: 'build') } + let_it_be(:failed_build) { create(:ci_build, :failed, name: 'failed-build', pipeline: pipeline, stage_idx: 0, stage: 'build') } + let_it_be(:bridge) { create(:ci_bridge, name: 'ci-bridge-example', pipeline: pipeline, stage_idx: 0, stage: 'build') } let(:path) { %i[project pipeline] } let(:pipeline_graphql_data) { graphql_data_at(*path) } @@ -79,16 +79,6 @@ RSpec.describe 'getting pipeline information nested in a project' do end end - private - - def build_query_to_find_pipeline_shas(*pipelines) - pipeline_fields = pipelines.map.each_with_index do |pipeline, idx| - "pipeline#{idx}: pipeline(iid: \"#{pipeline.iid}\") { sha }" - end.join(' ') - - graphql_query_for('project', { 'fullPath' => project.full_path }, pipeline_fields) - end - context 'when enough data is requested' do let(:fields) do query_graphql_field(:jobs, nil, @@ -282,4 +272,69 @@ RSpec.describe 'getting pipeline information nested in a project' do end end end + + context 'N+1 queries on stages jobs' do + let(:depth) { 5 } + let(:fields) do + <<~FIELDS + stages { + nodes { + name + groups { + nodes { + name + jobs { + nodes { + name + needs { + nodes { + name + } + } + status: detailedStatus { + tooltip + hasDetails + detailsPath + action { + buttonTitle + path + title + } + } + } + } + } + } + } + } + FIELDS + end + + it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do + # warm up + post_graphql(query, current_user: current_user) + + control = ActiveRecord::QueryRecorder.new(skip_cached: false) do + post_graphql(query, current_user: current_user) + end + + create(:ci_build, name: 'test-a', pipeline: pipeline, stage_idx: 1, stage: 'test') + create(:ci_build, name: 'test-b', pipeline: pipeline, stage_idx: 1, stage: 'test') + create(:ci_build, name: 'deploy-a', pipeline: pipeline, stage_idx: 2, stage: 'deploy') + + expect do + post_graphql(query, current_user: current_user) + end.not_to exceed_all_query_limit(control) + end + end + + private + + def build_query_to_find_pipeline_shas(*pipelines) + pipeline_fields = pipelines.map.each_with_index do |pipeline, idx| + "pipeline#{idx}: pipeline(iid: \"#{pipeline.iid}\") { sha }" + end.join(' ') + + graphql_query_for('project', { 'fullPath' => project.full_path }, pipeline_fields) + end end diff --git a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb index 7d157563f5f..39a68d98d84 100644 --- a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb +++ b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'rendering project pipeline statistics' do include GraphqlHelpers let_it_be(:project) { create(:project) } + let(:user) { create(:user) } let(:fields) do diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb index 54375d4de1d..e44a7efb354 100644 --- a/spec/requests/api/graphql/project_query_spec.rb +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'getting project information' do let_it_be(:group) { create(:group) } let_it_be(:project, reload: true) { create(:project, :repository, group: group) } let_it_be(:current_user) { create(:user) } + let(:project_fields) { all_graphql_fields_for('project'.to_s.classify, max_depth: 1) } let(:query) do diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb index 6bd0703c121..ecc7fffaef7 100644 --- a/spec/requests/api/graphql/query_spec.rb +++ b/spec/requests/api/graphql/query_spec.rb @@ -8,6 +8,7 @@ RSpec.describe 'Query' do let_it_be(:project) { create(:project) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:developer) { create(:user) } + let(:current_user) { developer } describe '.designManagement' do @@ -15,6 +16,7 @@ RSpec.describe 'Query' do let_it_be(:version) { create(:design_version, issue: issue) } let_it_be(:design) { version.designs.first } + let(:query_result) { graphql_data.dig(*path) } let(:query) { graphql_query_for(:design_management, nil, dm_fields) } diff --git a/spec/requests/api/graphql/user/starred_projects_query_spec.rb b/spec/requests/api/graphql/user/starred_projects_query_spec.rb index 6cb02068f2a..a8c087d1fbf 100644 --- a/spec/requests/api/graphql/user/starred_projects_query_spec.rb +++ b/spec/requests/api/graphql/user/starred_projects_query_spec.rb @@ -60,6 +60,7 @@ RSpec.describe 'Getting starredProjects of the user' do context 'the current user is a member of a private project the user starred' do let_it_be(:other_user) { create(:user) } + let(:current_user) { other_user } before do diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb index 60520906e87..59b805bb25b 100644 --- a/spec/requests/api/graphql/user_query_spec.rb +++ b/spec/requests/api/graphql/user_query_spec.rb @@ -402,6 +402,7 @@ RSpec.describe 'getting user information' do context 'we request the groupMemberships' do let_it_be(:membership_a) { create(:group_member, user: user) } + let(:group_memberships) { graphql_data_at(:user, :group_memberships, :nodes) } let(:user_fields) { 'groupMemberships { nodes { id } }' } @@ -424,6 +425,7 @@ RSpec.describe 'getting user information' do context 'we request the projectMemberships' do let_it_be(:membership_a) { create(:project_member, user: user) } + let(:project_memberships) { graphql_data_at(:user, :project_memberships, :nodes) } let(:user_fields) { 'projectMemberships { nodes { id } }' } diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb index 463fca43cb5..7b081bb7568 100644 --- a/spec/requests/api/graphql_spec.rb +++ b/spec/requests/api/graphql_spec.rb @@ -315,6 +315,7 @@ RSpec.describe 'GraphQL' do describe 'resolver complexity' do let_it_be(:project) { create(:project, :public) } + let(:query) do graphql_query_for( 'project', @@ -350,6 +351,7 @@ RSpec.describe 'GraphQL' do describe 'complexity limits' do let_it_be(:project) { create(:project, :public) } + let!(:user) { create(:user) } let(:query_fields) do diff --git a/spec/requests/api/group_avatar_spec.rb b/spec/requests/api/group_avatar_spec.rb index be5cfbc234c..50379d29b09 100644 --- a/spec/requests/api/group_avatar_spec.rb +++ b/spec/requests/api/group_avatar_spec.rb @@ -4,17 +4,35 @@ require 'spec_helper' RSpec.describe API::GroupAvatar do def avatar_path(group) - "/groups/#{group.id}/avatar" + "/groups/#{ERB::Util.url_encode(group.full_path)}/avatar" end describe 'GET /groups/:id/avatar' do context 'when the group is public' do - it 'retrieves the avatar successfully' do - group = create(:group, :public, :with_avatar) + let(:group) { create(:group, :public, :with_avatar) } + it 'retrieves the avatar successfully' do get api(avatar_path(group)) expect(response).to have_gitlab_http_status(:ok) + expect(response.headers['Content-Disposition']) + .to eq(%(attachment; filename="dk.png"; filename*=UTF-8''dk.png)) + end + + context 'when the avatar is in the object storage' do + before do + stub_uploads_object_storage(AvatarUploader) + + group.avatar.migrate!(ObjectStorage::Store::REMOTE) + end + + it 'redirects to the file in the object storage' do + get api(avatar_path(group)) + + expect(response).to have_gitlab_http_status(:found) + expect(response.headers['Content-Disposition']) + .to eq(%(attachment; filename="dk.png"; filename*=UTF-8''dk.png)) + end end context 'when the group does not have avatar' do @@ -24,6 +42,18 @@ RSpec.describe API::GroupAvatar do get api(avatar_path(group)) expect(response).to have_gitlab_http_status(:not_found) + expect(response.body) + .to eq(%({"message":"404 Avatar Not Found"})) + end + end + + context 'when the group is a subgroup' do + it 'returns :ok' do + group = create(:group, :nested, :public, :with_avatar, name: 'g1.1') + + get api(avatar_path(group)) + + expect(response).to have_gitlab_http_status(:ok) end end end diff --git a/spec/requests/api/group_import_spec.rb b/spec/requests/api/group_import_spec.rb index f632e49bf3a..efad6334518 100644 --- a/spec/requests/api/group_import_spec.rb +++ b/spec/requests/api/group_import_spec.rb @@ -9,6 +9,7 @@ RSpec.describe API::GroupImport do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } + let(:path) { '/groups/import' } let(:file) { File.join('spec', 'fixtures', 'group_export.tar.gz') } let(:export_path) { "#{Dir.tmpdir}/group_export_spec" } diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb index c677e68b285..11738e3cba8 100644 --- a/spec/requests/api/group_labels_spec.rb +++ b/spec/requests/api/group_labels_spec.rb @@ -29,6 +29,32 @@ RSpec.describe API::GroupLabels do let(:expected_labels) { [group_label1.name] } it_behaves_like 'fetches labels' + + context 'and is subscribed' do + before do + group_label1.subscribe(user) + end + + it 'returns true' do + get api("/groups/#{group.id}/labels?search=#{group_label1.name}", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response[0]['subscribed']).to be true + end + end + + context 'and is unsubscribed' do + before do + group_label1.unsubscribe(user) + end + + it 'returns false' do + get api("/groups/#{group.id}/labels?search=#{group_label1.name}", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response[0]['subscribed']).to be false + end + end end context 'when the with_counts parameter is set' do diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb index e3e0164e5a7..2312d35c815 100644 --- a/spec/requests/api/group_milestones_spec.rb +++ b/spec/requests/api/group_milestones_spec.rb @@ -9,6 +9,7 @@ RSpec.describe API::GroupMilestones do let_it_be(:group_member) { create(:group_member, group: group, user: user) } let_it_be(:closed_milestone) { create(:closed_milestone, group: group, title: 'version1', description: 'closed milestone') } let_it_be(:milestone) { create(:milestone, group: group, title: 'version2', description: 'open milestone') } + let(:route) { "/groups/#{group.id}/milestones" } it_behaves_like 'group and project milestones', "/groups/:id/milestones" diff --git a/spec/requests/api/group_packages_spec.rb b/spec/requests/api/group_packages_spec.rb index 792aa2c1f20..a2b0b35c76a 100644 --- a/spec/requests/api/group_packages_spec.rb +++ b/spec/requests/api/group_packages_spec.rb @@ -6,6 +6,7 @@ RSpec.describe API::GroupPackages do let_it_be(:group) { create(:group, :public) } let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') } let_it_be(:user) { create(:user) } + let(:params) { {} } subject { get api(url), params: params } @@ -17,6 +18,7 @@ RSpec.describe API::GroupPackages do context 'with sorting' do let_it_be(:package1) { create(:npm_package, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") } let_it_be(:package2) { create(:nuget_package, project: project, version: '2.0.4') } + let(:package3) { create(:maven_package, project: project, version: '1.1.1', name: 'zzz') } before do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 0a47b93773b..ad7a2e3b1fb 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -17,7 +17,7 @@ RSpec.describe API::Groups do let_it_be(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) } let_it_be(:archived_project) { create(:project, namespace: group1, archived: true) } - before do + before_all do group1.add_owner(user1) group2.add_owner(user2) end @@ -255,13 +255,14 @@ RSpec.describe API::Groups do end context "when using sorting" do - let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") } - let(:group4) { create(:group, name: "same-name", path: "y#{group1.path}") } - let(:group5) { create(:group, name: "same-name") } + let_it_be(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") } + let_it_be(:group4) { create(:group, name: "same-name", path: "y#{group1.path}") } + let_it_be(:group5) { create(:group, name: "same-name") } + let(:response_groups) { json_response.map { |group| group['name'] } } let(:response_groups_ids) { json_response.map { |group| group['id'] } } - before do + before_all do group3.add_owner(user1) group4.add_owner(user1) group5.add_owner(user1) @@ -330,6 +331,44 @@ RSpec.describe API::Groups do expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort) end + context 'when searching with similarity ordering', :aggregate_failures do + let_it_be(:group6) { create(:group, name: 'same-name subgroup', parent: group4) } + let_it_be(:group7) { create(:group, name: 'same-name parent') } + + let(:params) { { order_by: 'similarity', search: 'same-name' } } + + before_all do + group6.add_owner(user1) + group7.add_owner(user1) + end + + subject { get api('/groups', user1), params: params } + + it 'sorts top level groups before subgroups with exact matches first' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response.length).to eq(4) + + expect(response_groups).to eq(['same-name', 'same-name parent', 'same-name subgroup', 'same-name']) + end + + context 'when `search` parameter is not given' do + let(:params) { { order_by: 'similarity' } } + + it 'sorts items ordered by name' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response.length).to eq(6) + + expect(response_groups).to eq(groups_visible_to_user(user1).order(:name).pluck(:name)) + end + end + end + def groups_visible_to_user(user) Group.where(id: user.authorized_groups.select(:id).reorder(nil)) end @@ -451,6 +490,7 @@ RSpec.describe API::Groups do expect(json_response['visibility']).to eq(Gitlab::VisibilityLevel.string_level(group1.visibility_level)) expect(json_response['avatar_url']).to eq(group1.avatar_url(only_path: false)) expect(json_response['share_with_group_lock']).to eq(group1.share_with_group_lock) + expect(json_response['prevent_sharing_groups_outside_hierarchy']).to eq(group2.namespace_settings.prevent_sharing_groups_outside_hierarchy) expect(json_response['require_two_factor_authentication']).to eq(group1.require_two_factor_authentication) expect(json_response['two_factor_grace_period']).to eq(group1.two_factor_grace_period) expect(json_response['auto_devops_enabled']).to eq(group1.auto_devops_enabled) @@ -661,6 +701,7 @@ RSpec.describe API::Groups do project_creation_level: "noone", subgroup_creation_level: "maintainer", default_branch_protection: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS, + prevent_sharing_groups_outside_hierarchy: true, avatar: fixture_file_upload(file_path) } @@ -685,6 +726,7 @@ RSpec.describe API::Groups do expect(json_response['shared_projects'].length).to eq(0) expect(json_response['default_branch_protection']).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) expect(json_response['avatar_url']).to end_with('dk.png') + expect(json_response['prevent_sharing_groups_outside_hierarchy']).to eq(true) end context 'updating the `default_branch_protection` attribute' do @@ -755,6 +797,15 @@ RSpec.describe API::Groups do expect(response).to have_gitlab_http_status(:bad_request) expect(json_response['message']['visibility_level']).to contain_exactly('private is not allowed since there are sub-groups with higher visibility.') end + + it 'does not update prevent_sharing_groups_outside_hierarchy' do + put api("/groups/#{subgroup.id}", user3), params: { description: 'it works', prevent_sharing_groups_outside_hierarchy: true } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.keys).not_to include('prevent_sharing_groups_outside_hierarchy') + expect(subgroup.reload.prevent_sharing_groups_outside_hierarchy).to eq(false) + expect(json_response['description']).to eq('it works') + end end end @@ -1381,6 +1432,7 @@ RSpec.describe API::Groups do let_it_be(:sub_child_group1) { create(:group, parent: child_group1) } let_it_be(:child_group2) { create(:group, :private, parent: group2) } let_it_be(:sub_child_group2) { create(:group, :private, parent: child_group2) } + let(:response_groups) { json_response.map { |group| group['name'] } } context 'when unauthenticated' do diff --git a/spec/requests/api/helm_packages_spec.rb b/spec/requests/api/helm_packages_spec.rb index 5871c0a5d5b..08b4489a6e3 100644 --- a/spec/requests/api/helm_packages_spec.rb +++ b/spec/requests/api/helm_packages_spec.rb @@ -9,55 +9,171 @@ RSpec.describe API::HelmPackages do let_it_be_with_reload(:project) { create(:project, :public) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } + let_it_be(:package) { create(:helm_package, project: project) } - describe 'GET /api/v4/projects/:id/packages/helm/:channel/charts/:file_name.tgz' do - let_it_be(:package) { create(:helm_package, project: project) } - - let(:channel) { package.package_files.first.helm_channel } + describe 'GET /api/v4/projects/:id/packages/helm/:channel/index.yaml' do + it_behaves_like 'handling helm chart index requests' do + let(:url) { "/projects/#{project.id}/packages/helm/#{package.package_files.first.helm_channel}/index.yaml" } + end + end - let(:url) { "/projects/#{project.id}/packages/helm/#{channel}/charts/#{package.name}-#{package.version}.tgz" } + describe 'GET /api/v4/projects/:id/packages/helm/:channel/charts/:file_name.tgz' do + let(:url) { "/projects/#{project.id}/packages/helm/#{package.package_files.first.helm_channel}/charts/#{package.name}-#{package.version}.tgz" } - subject { get api(url) } + subject { get api(url), headers: headers } context 'with valid project' do - where(:visibility, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - :public | :developer | true | true | 'process helm download content request' | :success - :public | :guest | true | true | 'process helm download content request' | :success - :public | :developer | true | false | 'rejects helm packages access' | :unauthorized - :public | :guest | true | false | 'rejects helm packages access' | :unauthorized - :public | :developer | false | true | 'process helm download content request' | :success - :public | :guest | false | true | 'process helm download content request' | :success - :public | :developer | false | false | 'rejects helm packages access' | :unauthorized - :public | :guest | false | false | 'rejects helm packages access' | :unauthorized - :public | :anonymous | false | true | 'process helm download content request' | :success - :private | :developer | true | true | 'process helm download content request' | :success - :private | :guest | true | true | 'rejects helm packages access' | :forbidden - :private | :developer | true | false | 'rejects helm packages access' | :unauthorized - :private | :guest | true | false | 'rejects helm packages access' | :unauthorized - :private | :developer | false | true | 'rejects helm packages access' | :not_found - :private | :guest | false | true | 'rejects helm packages access' | :not_found - :private | :developer | false | false | 'rejects helm packages access' | :unauthorized - :private | :guest | false | false | 'rejects helm packages access' | :unauthorized - :private | :anonymous | false | true | 'rejects helm packages access' | :unauthorized + where(:visibility, :user_role, :shared_examples_name, :expected_status) do + :public | :guest | 'process helm download content request' | :success + :public | :not_a_member | 'process helm download content request' | :success + :public | :anonymous | 'process helm download content request' | :success + :private | :reporter | 'process helm download content request' | :success + :private | :guest | 'rejects helm packages access' | :forbidden + :private | :not_a_member | 'rejects helm packages access' | :not_found + :private | :anonymous | 'rejects helm packages access' | :unauthorized end with_them do - let(:token) { user_token ? personal_access_token.token : 'wrong' } - let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } + let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) } let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } - subject { get api(url), headers: headers } - before do project.update!(visibility: visibility.to_s) end - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] + it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status] end end + context 'when an invalid token is passed' do + let(:headers) { basic_auth_header(user.username, 'wrong') } + + it_behaves_like 'returning response status', :unauthorized + end + it_behaves_like 'deploy token for package GET requests' + end + + describe 'POST /api/v4/projects/:id/packages/helm/api/:channel/charts/authorize' do + include_context 'workhorse headers' + + let(:channel) { 'stable' } + let(:url) { "/projects/#{project.id}/packages/helm/api/#{channel}/charts/authorize" } + let(:headers) { {} } + + subject { post api(url), headers: headers } + + context 'with valid project' do + where(:visibility_level, :user_role, :shared_examples_name, :expected_status) do + :public | :developer | 'process helm workhorse authorization' | :success + :public | :reporter | 'rejects helm packages access' | :forbidden + :public | :not_a_member | 'rejects helm packages access' | :forbidden + :public | :anonymous | 'rejects helm packages access' | :unauthorized + :private | :developer | 'process helm workhorse authorization' | :success + :private | :reporter | 'rejects helm packages access' | :forbidden + :private | :not_a_member | 'rejects helm packages access' | :not_found + :private | :anonymous | 'rejects helm packages access' | :unauthorized + end + + with_them do + let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) } + let(:headers) { user_headers.merge(workhorse_headers) } + + before do + project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s)) + end + + it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status] + end + end + + context 'when an invalid token is passed' do + let(:headers) { basic_auth_header(user.username, 'wrong') } + + it_behaves_like 'returning response status', :unauthorized + end + + it_behaves_like 'deploy token for package uploads' + + it_behaves_like 'job token for package uploads', authorize_endpoint: true, accept_invalid_username: true do + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + end it_behaves_like 'rejects helm access with unknown project id' end + + describe 'POST /api/v4/projects/:id/packages/helm/api/:channel/charts' do + include_context 'workhorse headers' + + let_it_be(:file_name) { 'package.tgz' } + + let(:channel) { 'stable' } + let(:url) { "/projects/#{project.id}/packages/helm/api/#{channel}/charts" } + let(:headers) { {} } + let(:params) { { chart: temp_file(file_name) } } + let(:file_key) { :chart } + let(:send_rewritten_field) { true } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } } + + subject do + workhorse_finalize( + api(url), + method: :post, + file_key: file_key, + params: params, + headers: headers, + send_rewritten_field: send_rewritten_field + ) + end + + context 'with valid project' do + where(:visibility_level, :user_role, :shared_examples_name, :expected_status) do + :public | :developer | 'process helm upload' | :created + :public | :reporter | 'rejects helm packages access' | :forbidden + :public | :not_a_member | 'rejects helm packages access' | :forbidden + :public | :anonymous | 'rejects helm packages access' | :unauthorized + :private | :developer | 'process helm upload' | :created + :private | :guest | 'rejects helm packages access' | :forbidden + :private | :not_a_member | 'rejects helm packages access' | :not_found + :private | :anonymous | 'rejects helm packages access' | :unauthorized + end + + with_them do + let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) } + let(:headers) { user_headers.merge(workhorse_headers) } + + before do + project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s)) + end + + it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status] + end + end + + context 'when an invalid token is passed' do + let(:headers) { basic_auth_header(user.username, 'wrong') } + + it_behaves_like 'returning response status', :unauthorized + end + + it_behaves_like 'deploy token for package uploads' + + it_behaves_like 'job token for package uploads', accept_invalid_username: true do + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + end + + it_behaves_like 'rejects helm access with unknown project id' + + context 'file size above maximum limit' do + let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) } + + before do + allow_next_instance_of(UploadedFile) do |uploaded_file| + allow(uploaded_file).to receive(:size).and_return(project.actual_limits.helm_max_file_size + 1) + end + end + + it_behaves_like 'returning response status', :bad_request + end + end end diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index ce0018d6d0d..8961f3177b6 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -10,6 +10,7 @@ RSpec.describe API::Helpers do include TermsHelper let_it_be(:user, reload: true) { create(:user) } + let(:admin) { create(:admin) } let(:key) { create(:key, user: user) } diff --git a/spec/requests/api/import_bitbucket_server_spec.rb b/spec/requests/api/import_bitbucket_server_spec.rb index 972b21ad2e0..2225f737f36 100644 --- a/spec/requests/api/import_bitbucket_server_spec.rb +++ b/spec/requests/api/import_bitbucket_server_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe API::ImportBitbucketServer do let(:base_uri) { "https://test:7990" } - let(:user) { create(:user, bio: 'test') } + let(:user) { create(:user) } let(:token) { "asdasd12345" } let(:secret) { "sekrettt" } let(:project_key) { 'TES' } @@ -14,6 +14,7 @@ RSpec.describe API::ImportBitbucketServer do describe "POST /import/bitbucket_server" do context 'with no optional parameters' do let_it_be(:project) { create(:project) } + let(:client) { double(BitbucketServer::Client) } before do @@ -48,6 +49,7 @@ RSpec.describe API::ImportBitbucketServer do context 'with a new project name' do let_it_be(:project) { create(:project, name: 'new-name') } + let(:client) { instance_double(BitbucketServer::Client) } before do @@ -83,6 +85,7 @@ RSpec.describe API::ImportBitbucketServer do context 'with an invalid URL' do let_it_be(:project) { create(:project, name: 'new-name') } + let(:client) { instance_double(BitbucketServer::Client) } before do diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index 631698554f9..49756df61c6 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -9,6 +9,7 @@ RSpec.describe API::Internal::Base do let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) } let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) } let_it_be(:project_snippet) { create(:project_snippet, :repository, author: user, project: project) } + let(:key) { create(:key, user: user) } let(:secret_token) { Gitlab::Shell.secret_token } let(:gl_repository) { "project-#{project.id}" } @@ -1176,59 +1177,68 @@ RSpec.describe API::Internal::Base do allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(user) end - context 'with Project' do - it 'executes PostReceiveService' do - message = <<~MESSAGE.strip - To create a merge request for #{branch_name}, visit: - http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name} - MESSAGE + shared_examples 'runs post-receive hooks' do + let(:gl_repository) { container.repository.gl_repository } + let(:messages) { [] } + it 'executes PostReceiveService' do subject + expect(response).to have_gitlab_http_status(:ok) expect(json_response).to eq({ - 'messages' => [{ 'message' => message, 'type' => 'basic' }], + 'messages' => messages, 'reference_counter_decreased' => true }) end + it 'tries to notify that the container has moved' do + expect(Gitlab::Checks::ContainerMoved).to receive(:fetch_message).with(user, container.repository) + + subject + end + it_behaves_like 'storing arguments in the application context' do - let(:expected_params) { { user: user.username, project: project.full_path } } + let(:expected_params) { expected_context } end end - context 'with PersonalSnippet' do - let(:gl_repository) { "snippet-#{personal_snippet.id}" } - - it 'executes PostReceiveService' do - subject + context 'with Project' do + it_behaves_like 'runs post-receive hooks' do + let(:container) { project } + let(:expected_context) { { user: user.username, project: project.full_path } } - expect(json_response).to eq({ - 'messages' => [], - 'reference_counter_decreased' => true - }) + let(:messages) do + [ + { + 'message' => <<~MESSAGE.strip, + To create a merge request for #{branch_name}, visit: + http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name} + MESSAGE + 'type' => 'basic' + } + ] + end end + end - it_behaves_like 'storing arguments in the application context' do - let(:expected_params) { { user: key.user.username } } - let(:gl_repository) { "snippet-#{personal_snippet.id}" } + context 'with PersonalSnippet' do + it_behaves_like 'runs post-receive hooks' do + let(:container) { personal_snippet } + let(:expected_context) { { user: key.user.username } } end end context 'with ProjectSnippet' do - let(:gl_repository) { "snippet-#{project_snippet.id}" } - - it 'executes PostReceiveService' do - subject - - expect(json_response).to eq({ - 'messages' => [], - 'reference_counter_decreased' => true - }) + it_behaves_like 'runs post-receive hooks' do + let(:container) { project_snippet } + let(:expected_context) { { user: key.user.username, project: project_snippet.project.full_path } } end + end - it_behaves_like 'storing arguments in the application context' do - let(:expected_params) { { user: key.user.username, project: project_snippet.project.full_path } } - let(:gl_repository) { "snippet-#{project_snippet.id}" } + context 'with ProjectWiki' do + it_behaves_like 'runs post-receive hooks' do + let(:container) { project.wiki } + let(:expected_context) { { user: key.user.username, project: project.full_path } } end end @@ -1236,7 +1246,7 @@ RSpec.describe API::Internal::Base do it 'does not try to notify that project moved' do allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(nil) - expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message) + expect(Gitlab::Checks::ContainerMoved).not_to receive(:fetch_message) subject @@ -1244,33 +1254,17 @@ RSpec.describe API::Internal::Base do end end - context 'when project is nil' do - context 'with Project' do - let(:gl_repository) { 'project-foo' } - - it 'does not try to notify that project moved' do - allow(Gitlab::GlRepository).to receive(:parse).and_return([nil, nil, Gitlab::GlRepository::PROJECT]) - - expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message) - - subject - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context 'with PersonalSnippet' do - let(:gl_repository) { "snippet-#{personal_snippet.id}" } + context 'when container is nil' do + let(:gl_repository) { 'project-foo' } - it 'does not try to notify that project moved' do - allow(Gitlab::GlRepository).to receive(:parse).and_return([personal_snippet, nil, Gitlab::GlRepository::SNIPPET]) + it 'does not try to notify that project moved' do + allow(Gitlab::GlRepository).to receive(:parse).and_return([nil, nil, Gitlab::GlRepository::PROJECT]) - expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message) + expect(Gitlab::Checks::ContainerMoved).not_to receive(:fetch_message) - subject + subject - expect(response).to have_gitlab_http_status(:ok) - end + expect(response).to have_gitlab_http_status(:ok) end end end @@ -1378,29 +1372,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/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb index 7a2cec974b9..2acf6951d50 100644 --- a/spec/requests/api/internal/kubernetes_spec.rb +++ b/spec/requests/api/internal/kubernetes_spec.rb @@ -133,36 +133,6 @@ RSpec.describe API::Internal::Kubernetes do ) ) end - - context 'on GitLab.com' do - before do - allow(::Gitlab).to receive(:com?).and_return(true) - end - - context 'kubernetes_agent_on_gitlab_com feature flag disabled' do - before do - stub_feature_flags(kubernetes_agent_on_gitlab_com: false) - end - - it 'returns 403' do - send_request(headers: { 'Authorization' => "Bearer #{agent_token.token}" }) - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - context 'kubernetes_agent_on_gitlab_com feature flag enabled' do - before do - stub_feature_flags(kubernetes_agent_on_gitlab_com: agent_token.agent.project) - end - - it 'returns success' do - send_request(headers: { 'Authorization' => "Bearer #{agent_token.token}" }) - - expect(response).to have_gitlab_http_status(:success) - end - end - end end end @@ -214,36 +184,6 @@ RSpec.describe API::Internal::Kubernetes do expect(response).to have_gitlab_http_status(:not_found) end end - - context 'on GitLab.com' do - before do - allow(::Gitlab).to receive(:com?).and_return(true) - end - - context 'kubernetes_agent_on_gitlab_com feature flag disabled' do - before do - stub_feature_flags(kubernetes_agent_on_gitlab_com: false) - end - - it 'returns 403' do - send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - context 'kubernetes_agent_on_gitlab_com feature flag enabled' do - before do - stub_feature_flags(kubernetes_agent_on_gitlab_com: agent_token.agent.project) - end - - it 'returns success' do - send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) - - expect(response).to have_gitlab_http_status(:success) - end - end - end end context 'project is private' do diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 26377c40b73..4b6868f42bc 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -200,6 +200,36 @@ RSpec.describe API::Labels do expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name]) end + context 'when search param is provided' do + context 'and user is subscribed' do + before do + priority_label.subscribe(user) + end + + it 'returns subscribed true' do + get api("/projects/#{project.id}/labels?search=#{priority_label.name}", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response[0]['name']).to eq(priority_label.name) + expect(json_response[0]['subscribed']).to be true + end + end + + context 'and user is not subscribed' do + before do + priority_label.unsubscribe(user) + end + + it 'returns subscribed false' do + get api("/projects/#{project.id}/labels?search=#{priority_label.name}", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response[0]['name']).to eq(priority_label.name) + expect(json_response[0]['subscribed']).to be false + end + end + end + context 'when the with_counts parameter is set' do before do create(:labeled_issue, project: project, labels: [group_label], author: user) diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb index 57aa0f36192..7fe516d3daa 100644 --- a/spec/requests/api/lint_spec.rb +++ b/spec/requests/api/lint_spec.rb @@ -19,6 +19,7 @@ RSpec.describe API::Lint do context 'when authenticated' do let_it_be(:api_user) { create(:user) } + it 'returns authorized' do post api('/ci/lint', api_user), params: { content: 'content' } @@ -43,6 +44,7 @@ RSpec.describe API::Lint do context 'when authenticated' do let_it_be(:api_user) { create(:user) } + it 'returns authentication success' do post api('/ci/lint', api_user), params: { content: 'content' } diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb index 35d91963ac9..faf671d350f 100644 --- a/spec/requests/api/markdown_spec.rb +++ b/spec/requests/api/markdown_spec.rb @@ -52,6 +52,7 @@ RSpec.describe API::Markdown do context "when arguments are valid" do let_it_be(:project) { create(:project) } let_it_be(:issue) { create(:issue, project: project) } + let(:issue_url) { "http://#{Gitlab.config.gitlab.host}/#{issue.project.namespace.path}/#{issue.project.path}/-/issues/#{issue.iid}" } let(:text) { ":tada: Hello world! :100: #{issue.to_reference}" } diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 038c3bc552a..4b5fc57571b 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1188,16 +1188,14 @@ RSpec.describe API::MergeRequests do expect(json_response['target_project_id']).to eq(merge_request.target_project.id) expect(json_response['draft']).to be false expect(json_response['work_in_progress']).to be false - expect(json_response['merge_when_pipeline_succeeds']).to be_falsy + expect(json_response['merge_when_pipeline_succeeds']).to be false expect(json_response['merge_status']).to eq('can_be_merged') - expect(json_response['should_close_merge_request']).to be_falsy - expect(json_response['force_close_merge_request']).to be_falsy expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size) expect(json_response['merge_error']).to eq(merge_request.merge_error) expect(json_response['user']['can_merge']).to be_truthy expect(json_response).not_to include('rebase_in_progress') - expect(json_response['first_contribution']).to be_falsy - expect(json_response['has_conflicts']).to be_falsy + expect(json_response['first_contribution']).to be false + expect(json_response['has_conflicts']).to be false expect(json_response['blocking_discussions_resolved']).to be_truthy expect(json_response['references']['short']).to eq("!#{merge_request.iid}") expect(json_response['references']['relative']).to eq("!#{merge_request.iid}") @@ -1396,7 +1394,7 @@ RSpec.describe API::MergeRequests do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user2) - expect(json_response['user']['can_merge']).to be_falsy + expect(json_response['user']['can_merge']).to be false end it 'returns `checking` as its merge_status instead of `cannot_be_merged_rechecking`' do @@ -2009,6 +2007,7 @@ RSpec.describe API::MergeRequests do context 'forked projects', :sidekiq_might_not_need_inline do let_it_be(:user2) { create(:user) } + let(:project) { create(:project, :public, :repository) } let!(:forked_project) { fork_project(project, user2, repository: true) } let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) } @@ -2664,7 +2663,7 @@ RSpec.describe API::MergeRequests do ) expect(response).to have_gitlab_http_status(:ok) - expect(source_repository.branch_exists?(source_branch)).to be_falsy + expect(source_repository.branch_exists?(source_branch)).to be false end end @@ -2682,7 +2681,7 @@ RSpec.describe API::MergeRequests do ) expect(response).to have_gitlab_http_status(:ok) - expect(source_repository.branch_exists?(source_branch)).to be_falsy + expect(source_repository.branch_exists?(source_branch)).to be false end it 'does not remove the source branch' do @@ -2804,7 +2803,7 @@ RSpec.describe API::MergeRequests do it 'sets to true' do merge_request.update!(merge_params: { 'force_remove_source_branch' => false } ) - expect(merge_request.force_remove_source_branch?).to be_falsey + expect(merge_request.force_remove_source_branch?).to be false put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: { state_event: "close", remove_source_branch: true } @@ -2889,6 +2888,7 @@ RSpec.describe API::MergeRequests do context "forked projects" do let_it_be(:user2) { create(:user) } + let(:project) { create(:project, :public, :repository) } let!(:forked_project) { fork_project(project, user2, repository: true) } let(:merge_request) do diff --git a/spec/requests/api/metrics/dashboard/annotations_spec.rb b/spec/requests/api/metrics/dashboard/annotations_spec.rb index 07de2925ee2..79a38702354 100644 --- a/spec/requests/api/metrics/dashboard/annotations_spec.rb +++ b/spec/requests/api/metrics/dashboard/annotations_spec.rb @@ -6,6 +6,7 @@ RSpec.describe API::Metrics::Dashboard::Annotations do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace) } let_it_be(:environment) { create(:environment, project: project) } + let(:dashboard) { 'config/prometheus/common_metrics.yml' } let(:starting_at) { Time.now.iso8601 } let(:ending_at) { 1.hour.from_now.iso8601 } diff --git a/spec/requests/api/metrics/user_starred_dashboards_spec.rb b/spec/requests/api/metrics/user_starred_dashboards_spec.rb index 533dff05f27..7f019e1226a 100644 --- a/spec/requests/api/metrics/user_starred_dashboards_spec.rb +++ b/spec/requests/api/metrics/user_starred_dashboards_spec.rb @@ -7,6 +7,7 @@ RSpec.describe API::Metrics::UserStarredDashboards do let_it_be(:dashboard_yml) { fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml') } let_it_be(:dashboard) { '.gitlab/dashboards/find&seek.yml' } let_it_be(:project) { create(:project, :private, :repository, :custom_repo, namespace: user.namespace, files: { dashboard => dashboard_yml }) } + let(:url) { "/projects/#{project.id}/metrics/user_starred_dashboards" } let(:params) do { diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb index 572736cfc86..f608f296295 100644 --- a/spec/requests/api/nuget_project_packages_spec.rb +++ b/spec/requests/api/nuget_project_packages_spec.rb @@ -92,9 +92,10 @@ RSpec.describe API::NugetProjectPackages do describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do let_it_be(:package_name) { 'Dummy.Package' } - let_it_be(:package) { create(:nuget_package, project: project, name: package_name) } + let_it_be(:package) { create(:nuget_package, :with_symbol_package, project: project, name: package_name) } - let(:url) { "/projects/#{target.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.nupkg" } + let(:format) { 'nupkg' } + let(:url) { "/projects/#{target.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.#{format}" } subject { get api(url) } @@ -154,56 +155,14 @@ RSpec.describe API::NugetProjectPackages do subject { put api(url), headers: headers } - context 'with valid project' do - where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'process nuget workhorse authorization' | :success - 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden - 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden - 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden - 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :developer | true | true | 'process nuget workhorse authorization' | :success - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden - 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found - 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found - 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized - end - - with_them do - let(:token) { user_token ? personal_access_token.token : 'wrong' } - let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } - let(:headers) { user_headers.merge(workhorse_headers) } - - before do - update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) - end - - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] - end - end - - it_behaves_like 'deploy token for package uploads' - - it_behaves_like 'job token for package uploads', authorize_endpoint: true do - let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } - end - - it_behaves_like 'rejects nuget access with unknown target id' - - it_behaves_like 'rejects nuget access with invalid target id' + it_behaves_like 'nuget authorize upload endpoint' end describe 'PUT /api/v4/projects/:id/packages/nuget' do include_context 'workhorse headers' let_it_be(:file_name) { 'package.nupkg' } + let(:url) { "/projects/#{target.id}/packages/nuget" } let(:headers) { {} } let(:params) { { package: temp_file(file_name) } } @@ -221,63 +180,43 @@ RSpec.describe API::NugetProjectPackages do ) end - context 'with valid project' do - where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do - 'PUBLIC' | :developer | true | true | 'process nuget upload' | :created - 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden - 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden - 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden - 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized - 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :developer | true | true | 'process nuget upload' | :created - 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden - 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found - 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found - 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized - 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized - end - - with_them do - let(:token) { user_token ? personal_access_token.token : 'wrong' } - let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) } - let(:headers) { user_headers.merge(workhorse_headers) } - let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } } - - before do - update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false)) - end + it_behaves_like 'nuget upload endpoint' + end - it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] - end - end + describe 'PUT /api/v4/projects/:id/packages/nuget/symbolpackage/authorize' do + include_context 'workhorse headers' - it_behaves_like 'deploy token for package uploads' + let(:url) { "/projects/#{target.id}/packages/nuget/symbolpackage/authorize" } + let(:headers) { {} } - it_behaves_like 'job token for package uploads' do - let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } - end + subject { put api(url), headers: headers } - it_behaves_like 'rejects nuget access with unknown target id' + it_behaves_like 'nuget authorize upload endpoint' + end - it_behaves_like 'rejects nuget access with invalid target id' + describe 'PUT /api/v4/projects/:id/packages/nuget/symbolpackage' do + include_context 'workhorse headers' - context 'file size above maximum limit' do - let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) } + let_it_be(:file_name) { 'package.snupkg' } - before do - allow_next_instance_of(UploadedFile) do |uploaded_file| - allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1) - end - end + let(:url) { "/projects/#{target.id}/packages/nuget/symbolpackage" } + let(:headers) { {} } + let(:params) { { package: temp_file(file_name) } } + let(:file_key) { :package } + let(:send_rewritten_field) { true } - it_behaves_like 'returning response status', :bad_request + subject do + workhorse_finalize( + api(url), + method: :put, + file_key: file_key, + params: params, + headers: headers, + send_rewritten_field: send_rewritten_field + ) end + + it_behaves_like 'nuget upload endpoint', symbol_package: true end def update_visibility_to(visibility) diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml index 2932447f663..8341fac3191 100644 --- a/spec/requests/api/project_attributes.yml +++ b/spec/requests/api/project_attributes.yml @@ -137,6 +137,7 @@ project_setting: - has_confluence - has_vulnerabilities - prevent_merge_without_jira_issue + - previous_default_branch - project_id - push_rule_id - show_default_award_emojis diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index f784f677c25..253b61e5865 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -335,6 +335,7 @@ RSpec.describe API::ProjectClusters do let(:namespace) { 'new-namespace' } let(:platform_kubernetes_attributes) { { namespace: namespace } } let_it_be(:management_project) { create(:project, namespace: project.namespace) } + let(:management_project_id) { management_project.id } let(:update_params) do diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index 695d2c3fe2c..1170a9ba6cb 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -11,6 +11,7 @@ RSpec.describe API::ProjectContainerRepositories do let_it_be(:developer) { create(:user) } let_it_be(:reporter) { create(:user) } let_it_be(:guest) { create(:user) } + let(:root_repository) { create(:container_repository, :root, project: project) } let(:test_repository) { create(:container_repository, project: project) } let(:root_repository2) { create(:container_repository, :root, project: project2) } diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb index 71535e66353..606279ec20a 100644 --- a/spec/requests/api/project_milestones_spec.rb +++ b/spec/requests/api/project_milestones_spec.rb @@ -21,6 +21,7 @@ RSpec.describe API::ProjectMilestones do let_it_be(:group) { create(:group, :private, parent: ancestor_group) } let_it_be(:ancestor_group_milestone) { create(:milestone, group: ancestor_group) } let_it_be(:group_milestone) { create(:milestone, group: group) } + let(:params) { { include_parent_milestones: true } } shared_examples 'listing all milestones' do diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 6a9cf6e16e2..8cd1f15a88d 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -138,7 +138,7 @@ RSpec.describe API::ProjectSnippets do aggregate_failures do expect(snippet.repository.exists?).to be_truthy - blob = snippet.repository.blob_at('master', file_path) + blob = snippet.repository.blob_at(snippet.default_branch, file_path) expect(blob.data).to eq file_content end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 529a75af122..a869866c698 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -164,24 +164,21 @@ RSpec.describe API::Projects do end end - shared_examples_for 'projects response without N + 1 queries' do + shared_examples_for 'projects response without N + 1 queries' do |threshold| + let(:additional_project) { create(:project, :public) } + it 'avoids N + 1 queries' do + get api('/projects', current_user) + control = ActiveRecord::QueryRecorder.new do get api('/projects', current_user) end - if defined?(additional_project) - additional_project - else - create(:project, :public) - end + additional_project - # TODO: We're currently querying to detect if a project is a fork - # in 2 ways. Lower this back to 8 when `ForkedProjectLink` relation is - # removed expect do get api('/projects', current_user) - end.not_to exceed_query_limit(control).with_threshold(9) + end.not_to exceed_query_limit(control).with_threshold(threshold) end end @@ -194,7 +191,7 @@ RSpec.describe API::Projects do let(:projects) { [project] } end - it_behaves_like 'projects response without N + 1 queries' do + it_behaves_like 'projects response without N + 1 queries', 1 do let(:current_user) { nil } end end @@ -206,7 +203,7 @@ RSpec.describe API::Projects do let(:projects) { user_projects } end - it_behaves_like 'projects response without N + 1 queries' do + it_behaves_like 'projects response without N + 1 queries', 0 do let(:current_user) { user } end @@ -215,7 +212,7 @@ RSpec.describe API::Projects do create(:project, :public, group: create(:group)) end - it_behaves_like 'projects response without N + 1 queries' do + it_behaves_like 'projects response without N + 1 queries', 0 do let(:current_user) { user } let(:additional_project) { create(:project, :public, group: create(:group)) } end @@ -233,20 +230,6 @@ RSpec.describe API::Projects do expect(project_response['container_registry_enabled']).to eq(false) end - it 'reads projects.container_registry_enabled when read_container_registry_access_level is disabled' do - stub_feature_flags(read_container_registry_access_level: false) - - project.project_feature.update!(container_registry_access_level: ProjectFeature::DISABLED) - project.update_column(:container_registry_enabled, true) - - get api('/projects', user) - project_response = json_response.find { |p| p['id'] == project.id } - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_an Array - expect(project_response['container_registry_enabled']).to eq(true) - end - it 'includes project topics' do get api('/projects', user) @@ -386,7 +369,7 @@ RSpec.describe API::Projects do end context 'when external issue tracker is enabled' do - let!(:jira_service) { create(:jira_service, project: project) } + let!(:jira_integration) { create(:jira_integration, project: project) } it 'includes open_issues_count' do get api('/projects', user) @@ -880,7 +863,7 @@ RSpec.describe API::Projects do get api(url, current_user), params: params link = response.header['Link'] - url = link&.match(/<[^>]+(\/projects\?[^>]+)>; rel="next"/) do |match| + url = link&.match(%r{<[^>]+(/projects\?[^>]+)>; rel="next"}) do |match| match[1] end @@ -1016,7 +999,8 @@ RSpec.describe API::Projects do request_access_enabled: true, only_allow_merge_if_all_discussions_are_resolved: false, ci_config_path: 'a/custom/path', - merge_method: 'ff' + merge_method: 'ff', + squash_option: 'always' }).tap do |attrs| attrs[:operations_access_level] = 'disabled' attrs[:analytics_access_level] = 'disabled' @@ -2464,6 +2448,14 @@ RSpec.describe API::Projects do describe 'GET /projects/:id/users' do shared_examples_for 'project users response' do + let(:reporter_1) { create(:user) } + let(:reporter_2) { create(:user) } + + before do + project.add_reporter(reporter_1) + project.add_reporter(reporter_2) + end + it 'returns the project users' do get api("/projects/#{project.id}/users", current_user) @@ -2472,12 +2464,15 @@ RSpec.describe API::Projects do expect(response).to have_gitlab_http_status(:ok) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(1) + expect(json_response.size).to eq(3) first_user = json_response.first expect(first_user['username']).to eq(user.username) expect(first_user['name']).to eq(user.name) expect(first_user.keys).to include(*%w[name username id state avatar_url web_url]) + + ids = json_response.map { |raw_user| raw_user['id'] } + expect(ids).to eq([user.id, reporter_1.id, reporter_2.id]) end end @@ -2490,9 +2485,26 @@ RSpec.describe API::Projects do context 'when authenticated' do context 'valid request' do - it_behaves_like 'project users response' do - let(:project) { project4 } - let(:current_user) { user4 } + context 'when sort_by_project_authorizations_user_id FF is off' do + before do + stub_feature_flags(sort_by_project_users_by_project_authorizations_user_id: false) + end + + it_behaves_like 'project users response' do + let(:project) { project4 } + let(:current_user) { user4 } + end + end + + context 'when sort_by_project_authorizations_user_id FF is on' do + before do + stub_feature_flags(sort_by_project_users_by_project_authorizations_user_id: true) + end + + it_behaves_like 'project users response' do + let(:project) { project4 } + let(:current_user) { user4 } + end end end @@ -3125,6 +3137,29 @@ RSpec.describe API::Projects do expect(json_response['topics']).to eq(%w[topic2]) end + + it 'updates squash_option' do + project3.update!(squash_option: 'always') + + project_param = { squash_option: "default_on" } + + expect { put api("/projects/#{project3.id}", user), params: project_param } + .to change { project3.reload.squash_option } + .from('always') + .to('default_on') + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['squash_option']).to eq("default_on") + end + + it 'does not update an invalid squash_option' do + project_param = { squash_option: "jawn" } + + expect { put api("/projects/#{project3.id}", user), params: project_param } + .not_to change { project3.reload.squash_option } + + expect(response).to have_gitlab_http_status(:bad_request) + end end context 'when authenticated as project maintainer' do diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index 86925e6a0ba..e66326db2a2 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -14,6 +14,7 @@ RSpec.describe API::PypiPackages do let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + let(:headers) { {} } context 'simple API endpoint' do @@ -117,6 +118,7 @@ RSpec.describe API::PypiPackages do include_context 'workhorse headers' let_it_be(:file_name) { 'package.whl' } + let(:url) { "/projects/#{project.id}/packages/pypi" } let(:headers) { {} } let(:requires_python) { '>=3.7' } diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb index c03dd0331cf..00326426af5 100644 --- a/spec/requests/api/release/links_spec.rb +++ b/spec/requests/api/release/links_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe API::Release::Links do let(:project) { create(:project, :repository, :private) } let(:maintainer) { create(:user) } + let(:developer) { create(:user) } let(:reporter) { create(:user) } let(:non_project_member) { create(:user) } let(:commit) { create(:commit, project: project) } @@ -18,6 +19,7 @@ RSpec.describe API::Release::Links do before do project.add_maintainer(maintainer) + project.add_developer(developer) project.add_reporter(reporter) project.repository.add_tag(maintainer, 'v0.1', commit.id) @@ -196,6 +198,28 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'accepts the request' do + post api("/projects/#{project.id}/releases/v0.1/assets/links", developer), params: params + + expect(response).to have_gitlab_http_status(:created) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'forbids the request' do + post api("/projects/#{project.id}/releases/v0.1/assets/links", developer), params: params + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + context 'when name is empty' do let(:params) do { @@ -290,6 +314,28 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'accepts the request' do + put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", developer), params: params + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'forbids the request' do + put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", developer), params: params + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + context 'when params is empty' do let(:params) { {} } @@ -365,6 +411,28 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'accepts the request' do + delete api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", developer) + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'forbids the request' do + delete api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", developer) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + context 'when there are no corresponding release link' do let!(:release_link) { } diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index 81a4fcdbcac..03e0954e5ab 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -463,9 +463,23 @@ RSpec.describe API::Releases do end context 'when specified tag is not found in the project' do - it 'cannot find the release entry' do + it 'returns 404 for maintater' do get api("/projects/#{project.id}/releases/non_exist_tag", maintainer) + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response['message']).to eq('404 Not Found') + end + + it 'returns project not found for no user' do + get api("/projects/#{project.id}/releases/non_exist_tag", nil) + + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response['message']).to eq('404 Project Not Found') + end + + it 'returns forbidden for guest' do + get api("/projects/#{project.id}/releases/non_existing_tag", guest) + expect(response).to have_gitlab_http_status(:forbidden) end end @@ -662,6 +676,28 @@ RSpec.describe API::Releases do end.not_to change { Project.find_by_id(project.id).repository.tag_count } end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'accepts the request' do + post api("/projects/#{project.id}/releases", developer), params: params + + expect(response).to have_gitlab_http_status(:created) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'forbids the request' do + post api("/projects/#{project.id}/releases", developer), params: params + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + context 'when user is a reporter' do it 'forbids the request' do post api("/projects/#{project.id}/releases", reporter), params: params @@ -1000,6 +1036,28 @@ RSpec.describe API::Releases do expect(project.releases.last.released_at).to eq('2015-10-10T05:00:00Z') end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'accepts the request' do + put api("/projects/#{project.id}/releases/v0.1", developer), params: params + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'forbids the request' do + put api("/projects/#{project.id}/releases/v0.1", developer), params: params + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + context 'when user tries to update sha' do let(:params) { { sha: 'xxx' } } @@ -1180,6 +1238,28 @@ RSpec.describe API::Releases do expect(response).to match_response_schema('public_api/v4/release') end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'accepts the request' do + delete api("/projects/#{project.id}/releases/v0.1", developer) + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'forbids the request' do + delete api("/projects/#{project.id}/releases/v0.1", developer) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + context 'when there are no corresponding releases' do let!(:release) { } diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 1b96efeca22..d019e89e0b4 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -477,6 +477,17 @@ RSpec.describe API::Repositories do let(:request) { get api(route, guest) } end end + + context 'api_caching_rate_limit_repository_compare is disabled' do + before do + stub_feature_flags(api_caching_rate_limit_repository_compare: false) + end + + it_behaves_like 'repository compare' do + let(:project) { create(:project, :public, :repository) } + let(:current_user) { nil } + end + end end describe 'GET /projects/:id/repository/contributors' do diff --git a/spec/requests/api/resource_access_tokens_spec.rb b/spec/requests/api/resource_access_tokens_spec.rb index 1a3c805fe9f..23061ab4bf0 100644 --- a/spec/requests/api/resource_access_tokens_spec.rb +++ b/spec/requests/api/resource_access_tokens_spec.rb @@ -38,6 +38,7 @@ RSpec.describe API::ResourceAccessTokens do expect(api_get_token["name"]).to eq(token.name) expect(api_get_token["scopes"]).to eq(token.scopes) + expect(api_get_token["access_level"]).to eq(project.team.max_member_access(token.user.id)) expect(api_get_token["expires_at"]).to eq(token.expires_at.to_date.iso8601) expect(api_get_token).not_to have_key('token') end @@ -211,8 +212,9 @@ RSpec.describe API::ResourceAccessTokens do end describe "POST projects/:id/access_tokens" do - let(:params) { { name: "test", scopes: ["api"], expires_at: expires_at } } + let(:params) { { name: "test", scopes: ["api"], expires_at: expires_at, access_level: access_level } } let(:expires_at) { 1.month.from_now } + let(:access_level) { 20 } subject(:create_token) { post api("/projects/#{project_id}/access_tokens", user), params: params } @@ -231,6 +233,7 @@ RSpec.describe API::ResourceAccessTokens do expect(response).to have_gitlab_http_status(:created) expect(json_response["name"]).to eq("test") expect(json_response["scopes"]).to eq(["api"]) + expect(json_response["access_level"]).to eq(20) expect(json_response["expires_at"]).to eq(expires_at.to_date.iso8601) expect(json_response["token"]).to be_present end @@ -248,6 +251,21 @@ RSpec.describe API::ResourceAccessTokens do expect(json_response["expires_at"]).to eq(nil) end end + + context "when 'access_level' is not set" do + let(:access_level) { nil } + + it 'creates a project access token with the default access level', :aggregate_failures do + create_token + + expect(response).to have_gitlab_http_status(:created) + expect(json_response["name"]).to eq("test") + expect(json_response["scopes"]).to eq(["api"]) + expect(json_response["access_level"]).to eq(40) + expect(json_response["expires_at"]).to eq(expires_at.to_date.iso8601) + expect(json_response["token"]).to be_present + end + end end context "with invalid params" do diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb index 7d863b55bbe..9b104520b52 100644 --- a/spec/requests/api/rubygem_packages_spec.rb +++ b/spec/requests/api/rubygem_packages_spec.rb @@ -14,6 +14,7 @@ RSpec.describe API::RubygemPackages do let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:headers) { {} } + let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } } let(:tokens) do @@ -244,6 +245,7 @@ RSpec.describe API::RubygemPackages do let(:url) { "/projects/#{project.id}/packages/rubygems/api/v1/gems" } let_it_be(:file_name) { 'package.gem' } + let(:headers) { {} } let(:params) { { file: temp_file(file_name) } } let(:file_key) { :file } diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index f7394fa0cb4..e550132e776 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -24,14 +24,14 @@ RSpec.describe API::Services do expect(response).to have_gitlab_http_status(:forbidden) end - context 'project with services' do + context 'with integrations' do let!(:active_integration) { create(:emails_on_push_integration, project: project, active: true) } let!(:integration) { create(:custom_issue_tracker_integration, project: project, active: false) } - it "returns a list of all active services" do + it "returns a list of all active integrations" do get api("/projects/#{project.id}/services", user) - aggregate_failures 'expect successful response with all active services' do + aggregate_failures 'expect successful response with all active integrations' do expect(response).to have_gitlab_http_status(:ok) expect(json_response).to be_an Array expect(json_response.count).to eq(1) @@ -42,41 +42,39 @@ RSpec.describe API::Services do end end - Integration.available_services_names.each do |service| - describe "PUT /projects/:id/services/#{service.dasherize}" do - include_context service + Integration.available_integration_names.each do |integration| + describe "PUT /projects/:id/services/#{integration.dasherize}" do + include_context integration - it "updates #{service} settings" do - put api("/projects/#{project.id}/services/#{dashed_service}", user), params: service_attrs + it "updates #{integration} settings" do + put api("/projects/#{project.id}/services/#{dashed_integration}", user), params: integration_attrs expect(response).to have_gitlab_http_status(:ok) - current_service = project.integrations.first - events = current_service.event_names.empty? ? ["foo"].freeze : current_service.event_names + current_integration = project.integrations.first + events = current_integration.event_names.empty? ? ["foo"].freeze : current_integration.event_names query_strings = [] events.each do |event| - query_strings << "#{event}=#{!current_service[event]}" + query_strings << "#{event}=#{!current_integration[event]}" end query_strings = query_strings.join('&') - put api("/projects/#{project.id}/services/#{dashed_service}?#{query_strings}", user), params: service_attrs + put api("/projects/#{project.id}/services/#{dashed_integration}?#{query_strings}", user), params: integration_attrs expect(response).to have_gitlab_http_status(:ok) - expect(json_response['slug']).to eq(dashed_service) + expect(json_response['slug']).to eq(dashed_integration) events.each do |event| next if event == "foo" - expect(project.integrations.first[event]).not_to eq(current_service[event]), - "expected #{!current_service[event]} for event #{event} for service #{current_service.title}, got #{current_service[event]}" + expect(project.integrations.first[event]).not_to eq(current_integration[event]), + "expected #{!current_integration[event]} for event #{event} for service #{current_integration.title}, got #{current_integration[event]}" end end it "returns if required fields missing" do - attrs = service_attrs - - required_attributes = service_attrs_list.select do |attr| - service_klass.validators_on(attr).any? do |v| - v.class == ActiveRecord::Validations::PresenceValidator && + required_attributes = integration_attrs_list.select do |attr| + integration_klass.validators_on(attr).any? do |v| + v.instance_of?(ActiveRecord::Validations::PresenceValidator) && # exclude presence validators with conditional since those are not really required ![:if, :unless].any? { |cond| v.options.include?(cond) } end @@ -85,85 +83,85 @@ RSpec.describe API::Services do if required_attributes.empty? expected_code = :ok else - attrs.delete(required_attributes.sample) + integration_attrs.delete(required_attributes.sample) expected_code = :bad_request end - put api("/projects/#{project.id}/services/#{dashed_service}", user), params: attrs + put api("/projects/#{project.id}/services/#{dashed_integration}", user), params: integration_attrs expect(response).to have_gitlab_http_status(expected_code) end end - describe "DELETE /projects/:id/services/#{service.dasherize}" do - include_context service + describe "DELETE /projects/:id/services/#{integration.dasherize}" do + include_context integration before do - initialize_service(service) + initialize_integration(integration) end - it "deletes #{service}" do - delete api("/projects/#{project.id}/services/#{dashed_service}", user) + it "deletes #{integration}" do + delete api("/projects/#{project.id}/services/#{dashed_integration}", user) expect(response).to have_gitlab_http_status(:no_content) - project.send(service_method).reload - expect(project.send(service_method).activated?).to be_falsey + project.send(integration_method).reload + expect(project.send(integration_method).activated?).to be_falsey end end - describe "GET /projects/:id/services/#{service.dasherize}" do - include_context service + describe "GET /projects/:id/services/#{integration.dasherize}" do + include_context integration - let!(:initialized_service) { initialize_service(service, active: true) } + let!(:initialized_integration) { initialize_integration(integration, active: true) } let_it_be(:project2) do create(:project, creator_id: user.id, namespace: user.namespace) end - def deactive_service! - return initialized_service.update!(active: false) unless initialized_service.is_a?(PrometheusService) + def deactive_integration! + return initialized_integration.update!(active: false) unless initialized_integration.is_a?(::Integrations::Prometheus) - # PrometheusService sets `#active` itself within a `before_save`: - initialized_service.manual_configuration = false - initialized_service.save! + # Integrations::Prometheus sets `#active` itself within a `before_save`: + initialized_integration.manual_configuration = false + initialized_integration.save! end it 'returns authentication error when unauthenticated' do - get api("/projects/#{project.id}/services/#{dashed_service}") + get api("/projects/#{project.id}/services/#{dashed_integration}") expect(response).to have_gitlab_http_status(:unauthorized) end - it "returns all properties of active service #{service}" do - get api("/projects/#{project.id}/services/#{dashed_service}", user) + it "returns all properties of active service #{integration}" do + get api("/projects/#{project.id}/services/#{dashed_integration}", user) - expect(initialized_service).to be_active + expect(initialized_integration).to be_active expect(response).to have_gitlab_http_status(:ok) - expect(json_response['properties'].keys).to match_array(service_instance.api_field_names) + expect(json_response['properties'].keys).to match_array(integration_instance.api_field_names) end - it "returns all properties of inactive service #{service}" do - deactive_service! + it "returns all properties of inactive integration #{integration}" do + deactive_integration! - get api("/projects/#{project.id}/services/#{dashed_service}", user) + get api("/projects/#{project.id}/services/#{dashed_integration}", user) - expect(initialized_service).not_to be_active + expect(initialized_integration).not_to be_active expect(response).to have_gitlab_http_status(:ok) - expect(json_response['properties'].keys).to match_array(service_instance.api_field_names) + expect(json_response['properties'].keys).to match_array(integration_instance.api_field_names) end - it "returns not found if service does not exist" do - get api("/projects/#{project2.id}/services/#{dashed_service}", user) + it "returns not found if integration does not exist" do + get api("/projects/#{project2.id}/services/#{dashed_integration}", user) expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Service Not Found') end - it "returns not found if service exists but is in `Project#disabled_services`" do + it "returns not found if service exists but is in `Project#disabled_integrations`" do expect_next_found_instance_of(Project) do |project| - expect(project).to receive(:disabled_services).at_least(:once).and_return([service]) + expect(project).to receive(:disabled_integrations).at_least(:once).and_return([integration]) end - get api("/projects/#{project.id}/services/#{dashed_service}", user) + get api("/projects/#{project.id}/services/#{dashed_integration}", user) expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Service Not Found') @@ -171,7 +169,7 @@ RSpec.describe API::Services do it "returns error when authenticated but not a project owner" do project.add_developer(user2) - get api("/projects/#{project.id}/services/#{dashed_service}", user2) + get api("/projects/#{project.id}/services/#{dashed_integration}", user2) expect(response).to have_gitlab_http_status(:forbidden) end @@ -179,10 +177,10 @@ RSpec.describe API::Services do end describe 'POST /projects/:id/services/:slug/trigger' do - describe 'Mattermost Service' do - let(:service_name) { 'mattermost_slash_commands' } + describe 'Mattermost integration' do + let(:integration_name) { 'mattermost_slash_commands' } - context 'no service is available' do + context 'when no integration is available' do it 'returns a not found message' do post api("/projects/#{project.id}/services/idonotexist/trigger") @@ -191,34 +189,34 @@ RSpec.describe API::Services do end end - context 'the service exists' do + context 'when the integration exists' do let(:params) { { token: 'token' } } - context 'the service is not active' do + context 'when the integration is not active' do before do - project.create_mattermost_slash_commands_service( + project.create_mattermost_slash_commands_integration( active: false, properties: params ) end - it 'when the service is inactive' do - post api("/projects/#{project.id}/services/#{service_name}/trigger"), params: params + it 'when the integration is inactive' do + post api("/projects/#{project.id}/services/#{integration_name}/trigger"), params: params expect(response).to have_gitlab_http_status(:not_found) end end - context 'the service is active' do + context 'when the integration is active' do before do - project.create_mattermost_slash_commands_service( + project.create_mattermost_slash_commands_integration( active: true, properties: params ) end it 'returns status 200' do - post api("/projects/#{project.id}/services/#{service_name}/trigger"), params: params + post api("/projects/#{project.id}/services/#{integration_name}/trigger"), params: params expect(response).to have_gitlab_http_status(:ok) end @@ -226,7 +224,7 @@ RSpec.describe API::Services do context 'when the project can not be found' do it 'returns a generic 404' do - post api("/projects/404/services/#{service_name}/trigger"), params: params + post api("/projects/404/services/#{integration_name}/trigger"), params: params expect(response).to have_gitlab_http_status(:not_found) expect(json_response["message"]).to eq("404 Service Not Found") @@ -235,18 +233,18 @@ RSpec.describe API::Services do end end - describe 'Slack Service' do - let(:service_name) { 'slack_slash_commands' } + describe 'Slack Integration' do + let(:integration_name) { 'slack_slash_commands' } before do - project.create_slack_slash_commands_service( + project.create_slack_slash_commands_integration( active: true, properties: { token: 'token' } ) end it 'returns status 200' do - post api("/projects/#{project.id}/services/#{service_name}/trigger"), params: { token: 'token', text: 'help' } + post api("/projects/#{project.id}/services/#{integration_name}/trigger"), params: { token: 'token', text: 'help' } expect(response).to have_gitlab_http_status(:ok) expect(json_response['response_type']).to eq("ephemeral") @@ -254,29 +252,29 @@ RSpec.describe API::Services do end end - describe 'Mattermost service' do - let(:service_name) { 'mattermost' } + describe 'Mattermost integration' do + let(:integration_name) { 'mattermost' } let(:params) do { webhook: 'https://hook.example.com', username: 'username' } end before do - project.create_mattermost_service( + project.create_mattermost_integration( active: true, properties: params ) end it 'accepts a username for update' do - put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(username: 'new_username') + put api("/projects/#{project.id}/services/#{integration_name}", user), params: params.merge(username: 'new_username') expect(response).to have_gitlab_http_status(:ok) expect(json_response['properties']['username']).to eq('new_username') end end - describe 'Microsoft Teams service' do - let(:service_name) { 'microsoft-teams' } + describe 'Microsoft Teams integration' do + let(:integration_name) { 'microsoft-teams' } let(:params) do { webhook: 'https://hook.example.com', @@ -286,29 +284,31 @@ RSpec.describe API::Services do end before do - project.create_microsoft_teams_service( + project.create_microsoft_teams_integration( active: true, properties: params ) end it 'accepts branches_to_be_notified for update' do - put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(branches_to_be_notified: 'all') + put api("/projects/#{project.id}/services/#{integration_name}", user), + params: params.merge(branches_to_be_notified: 'all') expect(response).to have_gitlab_http_status(:ok) expect(json_response['properties']['branches_to_be_notified']).to eq('all') end it 'accepts notify_only_broken_pipelines for update' do - put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(notify_only_broken_pipelines: true) + put api("/projects/#{project.id}/services/#{integration_name}", user), + params: params.merge(notify_only_broken_pipelines: true) expect(response).to have_gitlab_http_status(:ok) expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true) end end - describe 'Hangouts Chat service' do - let(:service_name) { 'hangouts-chat' } + describe 'Hangouts Chat integration' do + let(:integration_name) { 'hangouts-chat' } let(:params) do { webhook: 'https://hook.example.com', @@ -324,16 +324,38 @@ RSpec.describe API::Services do end it 'accepts branches_to_be_notified for update', :aggregate_failures do - put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(branches_to_be_notified: 'all') + put api("/projects/#{project.id}/services/#{integration_name}", user), params: params.merge(branches_to_be_notified: 'all') expect(response).to have_gitlab_http_status(:ok) expect(json_response['properties']['branches_to_be_notified']).to eq('all') end it 'only requires the webhook param' do - put api("/projects/#{project.id}/services/#{service_name}", user), params: { webhook: 'https://hook.example.com' } + put api("/projects/#{project.id}/services/#{integration_name}", user), params: { webhook: 'https://hook.example.com' } expect(response).to have_gitlab_http_status(:ok) end end + + describe 'Pipelines Email Integration' do + let(:integration_name) { 'pipelines-email' } + + context 'notify_only_broken_pipelines property was saved as a string' do + before do + project.create_pipelines_email_integration( + active: false, + properties: { + "notify_only_broken_pipelines": "true", + "branches_to_be_notified": "default" + } + ) + end + + it 'returns boolean values for notify_only_broken_pipelines' do + get api("/projects/#{project.id}/services/#{integration_name}", user) + + expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true) + end + end + end end diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 4a4aeaea714..4008b57a1cf 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -127,6 +127,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do spam_check_endpoint_enabled: true, spam_check_endpoint_url: 'grpc://example.com/spam_check', spam_check_api_key: 'SPAM_CHECK_API_KEY', + mailgun_events_enabled: true, + mailgun_signing_key: 'MAILGUN_SIGNING_KEY', disabled_oauth_sign_in_sources: 'unknown', import_sources: 'github,bitbucket', wiki_page_max_content_bytes: 12345, @@ -175,6 +177,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do expect(json_response['spam_check_endpoint_enabled']).to be_truthy expect(json_response['spam_check_endpoint_url']).to eq('grpc://example.com/spam_check') expect(json_response['spam_check_api_key']).to eq('SPAM_CHECK_API_KEY') + expect(json_response['mailgun_events_enabled']).to be(true) + expect(json_response['mailgun_signing_key']).to eq('MAILGUN_SIGNING_KEY') expect(json_response['disabled_oauth_sign_in_sources']).to eq([]) expect(json_response['import_sources']).to match_array(%w(github bitbucket)) expect(json_response['wiki_page_max_content_bytes']).to eq(12345) @@ -493,6 +497,15 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do end end + context "missing mailgun_signing_key value when mailgun_events_enabled is true" do + it "returns a blank parameter error message" do + put api("/application/settings", admin), params: { mailgun_events_enabled: true } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('mailgun_signing_key is missing') + end + end + context "personal access token prefix settings" do context "handles validation errors" do it "fails to update the settings with too long prefix" do diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb index 227c53f8fb9..f4d15d0525e 100644 --- a/spec/requests/api/snippets_spec.rb +++ b/spec/requests/api/snippets_spec.rb @@ -223,7 +223,7 @@ RSpec.describe API::Snippets, factory_default: :keep do it 'commit the files to the repository' do subject - blob = snippet.repository.blob_at('master', file_path) + blob = snippet.repository.blob_at(snippet.default_branch, file_path) expect(blob.data).to eq file_content end diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 3cea1af686e..1511872d183 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -81,6 +81,7 @@ RSpec.describe API::SystemHooks do expect(json_response['push_events']).to be false expect(json_response['tag_push_events']).to be false expect(json_response['merge_requests_events']).to be false + expect(json_response['repository_update_events']).to be true end it 'sets explicit values for events' do @@ -92,7 +93,8 @@ RSpec.describe API::SystemHooks do enable_ssl_verification: false, push_events: true, tag_push_events: true, - merge_requests_events: true + merge_requests_events: true, + repository_update_events: false } expect(response).to have_gitlab_http_status(:created) @@ -100,6 +102,7 @@ RSpec.describe API::SystemHooks do expect(json_response['push_events']).to be true expect(json_response['tag_push_events']).to be true expect(json_response['merge_requests_events']).to be true + expect(json_response['repository_update_events']).to be false end end diff --git a/spec/requests/api/unleash_spec.rb b/spec/requests/api/unleash_spec.rb index 9989f8d28bd..0718710f15c 100644 --- a/spec/requests/api/unleash_spec.rb +++ b/spec/requests/api/unleash_spec.rb @@ -6,6 +6,7 @@ RSpec.describe API::Unleash do include FeatureFlagHelpers let_it_be(:project, refind: true) { create(:project) } + let(:project_id) { project.id } let(:params) { } let(:headers) { } diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index d724cb9612c..383940ce34a 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -8,6 +8,7 @@ RSpec.describe API::Users do let_it_be(:key) { create(:key, user: user) } let_it_be(:gpg_key) { create(:gpg_key, user: user) } let_it_be(:email) { create(:email, user: user) } + let(:omniauth_user) { create(:omniauth_user) } let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } let(:private_user) { create(:user, private_profile: true) } @@ -2967,6 +2968,7 @@ RSpec.describe API::Users do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:group) { create(:group) } + let(:requesting_user) { create(:user) } before_all do diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb index 64fde3db19f..ec34dc7e7a1 100644 --- a/spec/requests/api/wikis_spec.rb +++ b/spec/requests/api/wikis_spec.rb @@ -611,11 +611,12 @@ RSpec.describe API::Wikis do let(:payload) { { file: fixture_file_upload('spec/fixtures/dk.png') } } let(:url) { "/projects/#{project.id}/wikis/attachments" } let(:file_path) { "#{Wikis::CreateAttachmentService::ATTACHMENT_PATH}/fixed_hex/dk.png" } + let(:branch) { wiki.default_branch } let(:result_hash) do { file_name: 'dk.png', file_path: file_path, - branch: 'master', + branch: branch, link: { url: file_path, markdown: "![dk](#{file_path})" |