summaryrefslogtreecommitdiff
path: root/spec/requests
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-16 18:25:58 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-16 18:25:58 +0000
commita5f4bba440d7f9ea47046a0a561d49adf0a1e6d4 (patch)
treefb69158581673816a8cd895f9d352dcb3c678b1e /spec/requests
parentd16b2e8639e99961de6ddc93909f3bb5c1445ba1 (diff)
downloadgitlab-ce-a5f4bba440d7f9ea47046a0a561d49adf0a1e6d4.tar.gz
Add latest changes from gitlab-org/gitlab@14-0-stable-eev14.0.0-rc42
Diffstat (limited to 'spec/requests')
-rw-r--r--spec/requests/api/branches_spec.rb43
-rw-r--r--spec/requests/api/ci/runner/jobs_put_spec.rb70
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb47
-rw-r--r--spec/requests/api/ci/runner/runners_post_spec.rb37
-rw-r--r--spec/requests/api/ci/runners_spec.rb10
-rw-r--r--spec/requests/api/commit_statuses_spec.rb66
-rw-r--r--spec/requests/api/commits_spec.rb19
-rw-r--r--spec/requests/api/composer_packages_spec.rb2
-rw-r--r--spec/requests/api/conan_instance_packages_spec.rb2
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb14
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb28
-rw-r--r--spec/requests/api/feature_flag_scopes_spec.rb319
-rw-r--r--spec/requests/api/feature_flags_spec.rb216
-rw-r--r--spec/requests/api/files_spec.rb3
-rw-r--r--spec/requests/api/generic_packages_spec.rb3
-rw-r--r--spec/requests/api/go_proxy_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/ci_cd_setting_spec.rb1
-rw-r--r--spec/requests/api/graphql/group/group_members_spec.rb17
-rw-r--r--spec/requests/api/graphql/group/milestones_spec.rb7
-rw-r--r--spec/requests/api/graphql/group/timelogs_spec.rb49
-rw-r--r--spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb32
-rw-r--r--spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb122
-rw-r--r--spec/requests/api/graphql/mutations/labels/create_spec.rb3
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb6
-rw-r--r--spec/requests/api/graphql/mutations/todos/restore_many_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/composer_spec.rb51
-rw-r--r--spec/requests/api/graphql/packages/conan_spec.rb50
-rw-r--r--spec/requests/api/graphql/packages/maven_spec.rb92
-rw-r--r--spec/requests/api/graphql/packages/nuget_spec.rb61
-rw-r--r--spec/requests/api/graphql/packages/pypi_spec.rb27
-rw-r--r--spec/requests/api/graphql/project/base_service_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/project_members_spec.rb14
-rw-r--r--spec/requests/api/graphql/project/releases_spec.rb106
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb25
-rw-r--r--spec/requests/api/group_avatar_spec.rb64
-rw-r--r--spec/requests/api/group_container_repositories_spec.rb1
-rw-r--r--spec/requests/api/group_export_spec.rb19
-rw-r--r--spec/requests/api/group_labels_spec.rb4
-rw-r--r--spec/requests/api/groups_spec.rb41
-rw-r--r--spec/requests/api/helm_packages_spec.rb63
-rw-r--r--spec/requests/api/internal/base_spec.rb53
-rw-r--r--spec/requests/api/invitations_spec.rb50
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb11
-rw-r--r--spec/requests/api/labels_spec.rb14
-rw-r--r--spec/requests/api/maven_packages_spec.rb426
-rw-r--r--spec/requests/api/members_spec.rb68
-rw-r--r--spec/requests/api/merge_requests_spec.rb45
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb5
-rw-r--r--spec/requests/api/nuget_group_packages_spec.rb2
-rw-r--r--spec/requests/api/nuget_project_packages_spec.rb8
-rw-r--r--spec/requests/api/project_attributes.yml4
-rw-r--r--spec/requests/api/project_container_repositories_spec.rb6
-rw-r--r--spec/requests/api/project_debian_distributions_spec.rb66
-rw-r--r--spec/requests/api/project_export_spec.rb13
-rw-r--r--spec/requests/api/project_packages_spec.rb32
-rw-r--r--spec/requests/api/project_repository_storage_moves_spec.rb2
-rw-r--r--spec/requests/api/project_statistics_spec.rb10
-rw-r--r--spec/requests/api/projects_spec.rb214
-rw-r--r--spec/requests/api/pypi_packages_spec.rb242
-rw-r--r--spec/requests/api/releases_spec.rb2
-rw-r--r--spec/requests/api/repositories_spec.rb10
-rw-r--r--spec/requests/api/rubygem_packages_spec.rb14
-rw-r--r--spec/requests/api/services_spec.rb2
-rw-r--r--spec/requests/api/settings_spec.rb4
-rw-r--r--spec/requests/api/tags_spec.rb129
-rw-r--r--spec/requests/api/terraform/modules/v1/packages_spec.rb13
-rw-r--r--spec/requests/api/terraform/state_spec.rb22
-rw-r--r--spec/requests/api/unleash_spec.rb162
-rw-r--r--spec/requests/api/users_preferences_spec.rb12
-rw-r--r--spec/requests/api/users_spec.rb23
-rw-r--r--spec/requests/api/wikis_spec.rb45
-rw-r--r--spec/requests/git_http_spec.rb4
-rw-r--r--spec/requests/groups/email_campaigns_controller_spec.rb13
-rw-r--r--spec/requests/lfs_http_spec.rb4
-rw-r--r--spec/requests/oauth/tokens_controller_spec.rb71
-rw-r--r--spec/requests/openid_connect_spec.rb110
-rw-r--r--spec/requests/users_controller_spec.rb42
79 files changed, 1829 insertions, 1870 deletions
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index a38ba782c44..36fbe86ac76 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -8,8 +8,8 @@ RSpec.describe API::Branches do
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:branch_name) { 'feature' }
let(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
- let(:branch_with_dot) { project.repository.find_branch('ends-with.json') }
- let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
+ let(:branch_with_dot) { 'ends-with.json' }
+ let(:branch_with_slash) { 'improve/awesome' }
let(:project_id) { project.id }
let(:current_user) { nil }
@@ -105,7 +105,7 @@ RSpec.describe API::Branches do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/branches')
- expect(response.headers).not_to include('Link', 'Links')
+ expect(response.headers).not_to include('Link')
branch_names = json_response.map { |x| x['name'] }
expect(branch_names).to match_array(project.repository.branch_names)
end
@@ -116,7 +116,7 @@ RSpec.describe API::Branches do
get api(route, current_user), params: base_params.merge(per_page: 2)
expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers).to include('Link', 'Links')
+ expect(response.headers).to include('Link')
expect(json_response.count).to eq 2
check_merge_status(json_response)
@@ -285,6 +285,13 @@ RSpec.describe API::Branches do
let(:request) { get api(route, current_user) }
end
end
+
+ context 'when repository does not exist' do
+ it_behaves_like '404 response' do
+ let(:project) { create(:project, creator: user) }
+ let(:request) { get api(route, current_user) }
+ end
+ end
end
context 'when unauthenticated', 'and project is public' do
@@ -320,19 +327,19 @@ RSpec.describe API::Branches do
end
context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot.name }
+ let(:branch_name) { branch_with_dot }
it_behaves_like 'repository branch'
end
context 'when branch contains dot txt' do
- let(:branch_name) { project.repository.find_branch('ends-with.txt').name }
+ let(:branch_name) { 'ends-with.txt' }
it_behaves_like 'repository branch'
end
context 'when branch contains a slash' do
- let(:branch_name) { branch_with_slash.name }
+ let(:branch_name) { branch_with_slash }
it_behaves_like '404 response' do
let(:request) { get api(route, current_user) }
@@ -340,7 +347,7 @@ RSpec.describe API::Branches do
end
context 'when branch contains an escaped slash' do
- let(:branch_name) { CGI.escape(branch_with_slash.name) }
+ let(:branch_name) { CGI.escape(branch_with_slash) }
it_behaves_like 'repository branch'
end
@@ -351,7 +358,7 @@ RSpec.describe API::Branches do
it_behaves_like 'repository branch'
context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot.name }
+ let(:branch_name) { branch_with_dot }
it_behaves_like 'repository branch'
end
@@ -475,13 +482,13 @@ RSpec.describe API::Branches do
it_behaves_like 'repository new protected branch'
context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot.name }
+ let(:branch_name) { branch_with_dot }
it_behaves_like 'repository new protected branch'
end
context 'when branch contains a slash' do
- let(:branch_name) { branch_with_slash.name }
+ let(:branch_name) { branch_with_slash }
it_behaves_like '404 response' do
let(:request) { put api(route, current_user) }
@@ -489,7 +496,7 @@ RSpec.describe API::Branches do
end
context 'when branch contains an escaped slash' do
- let(:branch_name) { CGI.escape(branch_with_slash.name) }
+ let(:branch_name) { CGI.escape(branch_with_slash) }
it_behaves_like 'repository new protected branch'
end
@@ -500,7 +507,7 @@ RSpec.describe API::Branches do
it_behaves_like 'repository new protected branch'
context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot.name }
+ let(:branch_name) { branch_with_dot }
it_behaves_like 'repository new protected branch'
end
@@ -609,13 +616,13 @@ RSpec.describe API::Branches do
it_behaves_like 'repository unprotected branch'
context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot.name }
+ let(:branch_name) { branch_with_dot }
it_behaves_like 'repository unprotected branch'
end
context 'when branch contains a slash' do
- let(:branch_name) { branch_with_slash.name }
+ let(:branch_name) { branch_with_slash }
it_behaves_like '404 response' do
let(:request) { put api(route, current_user) }
@@ -623,7 +630,7 @@ RSpec.describe API::Branches do
end
context 'when branch contains an escaped slash' do
- let(:branch_name) { CGI.escape(branch_with_slash.name) }
+ let(:branch_name) { CGI.escape(branch_with_slash) }
it_behaves_like 'repository unprotected branch'
end
@@ -634,7 +641,7 @@ RSpec.describe API::Branches do
it_behaves_like 'repository unprotected branch'
context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot.name }
+ let(:branch_name) { branch_with_dot }
it_behaves_like 'repository unprotected branch'
end
@@ -732,7 +739,7 @@ RSpec.describe API::Branches do
end
it 'removes a branch with dots in the branch name' do
- delete api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}", user)
+ delete api("/projects/#{project.id}/repository/branches/#{branch_with_dot}", user)
expect(response).to have_gitlab_http_status(:no_content)
end
diff --git a/spec/requests/api/ci/runner/jobs_put_spec.rb b/spec/requests/api/ci/runner/jobs_put_spec.rb
index 3d5021fba08..8c95748aa5f 100644
--- a/spec/requests/api/ci/runner/jobs_put_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_put_spec.rb
@@ -17,18 +17,14 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
describe '/api/v4/jobs' do
- let(:group) { create(:group, :nested) }
- let(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
- let(:runner) { create(:ci_runner, :project, projects: [project]) }
- let(:user) { create(:user) }
- let(:job) do
- create(:ci_build, :artifacts, :extended_options,
- pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0)
- end
+ let_it_be(:group) { create(:group, :nested) }
+ let_it_be(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
+ let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:user) { create(:user) }
describe 'PUT /api/v4/jobs/:id' do
- let(:job) do
+ let_it_be_with_reload(:job) do
create(:ci_build, :pending, :trace_live, pipeline: pipeline, project: project, user: user, runner_id: runner.id)
end
@@ -204,53 +200,6 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
- context 'when trace is given' do
- it 'creates a trace artifact' do
- allow(BuildFinishedWorker).to receive(:perform_async).with(job.id) do
- ArchiveTraceWorker.new.perform(job.id)
- end
-
- update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
-
- job.reload
- expect(response).to have_gitlab_http_status(:ok)
- expect(job.trace.raw).to eq 'BUILD TRACE UPDATED'
- expect(job.job_artifacts_trace.open.read).to eq 'BUILD TRACE UPDATED'
- end
-
- context 'when concurrent update of trace is happening' do
- before do
- job.trace.write('wb') do
- update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
- end
- end
-
- it 'returns that operation conflicts' do
- expect(response).to have_gitlab_http_status(:conflict)
- end
- end
- end
-
- context 'when no trace is given' do
- it 'does not override trace information' do
- update_job
-
- expect(job.reload.trace.raw).to eq 'BUILD TRACE'
- end
-
- context 'when running state is sent' do
- it 'updates update_at value' do
- expect { update_job_after_time }.to change { job.reload.updated_at }
- end
- end
-
- context 'when other state is sent' do
- it "doesn't update update_at value" do
- expect { update_job_after_time(20.minutes, state: 'success') }.not_to change { job.reload.updated_at }
- end
- end
- end
-
context 'when job has been erased' do
let(:job) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) }
@@ -267,20 +216,19 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
job.drop!(:script_failure)
end
- it 'does not update job status and job trace' do
- update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
+ it 'does not update job status' do
+ update_job(state: 'success')
job.reload
expect(response).to have_gitlab_http_status(:forbidden)
expect(response.header['Job-Status']).to eq 'failed'
- expect(job.trace.raw).to eq 'Job failed'
expect(job).to be_failed
end
end
context 'when job does not exist anymore' do
it 'returns 403 Forbidden' do
- update_job(non_existing_record_id, state: 'success', trace: 'BUILD TRACE UPDATED')
+ update_job(non_existing_record_id, state: 'success')
expect(response).to have_gitlab_http_status(:forbidden)
end
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 63da3340a45..8896bd44077 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let(:runner) { create(:ci_runner, :project, projects: [project]) }
let(:user) { create(:user) }
let(:job) do
- create(:ci_build, :artifacts, :extended_options,
+ create(:ci_build, :pending, :queued, :artifacts, :extended_options,
pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0)
end
@@ -129,7 +129,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when other projects have pending jobs' do
before do
job.success
- create(:ci_build, :pending)
+ create(:ci_build, :pending, :queued)
end
it_behaves_like 'no jobs available'
@@ -239,7 +239,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when job is made for tag' do
- let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
it 'sets branch as ref_type' do
request_job
@@ -297,7 +297,7 @@ 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, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, queued_at: 60.seconds.ago) }
+ let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, queued_at: 60.seconds.ago) }
context 'job is queued less than job_age parameter' do
let(:job_age) { 120 }
@@ -359,7 +359,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when job is for a release' do
- let!(:job) { create(:ci_build, :release_options, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :pending, :queued, :release_options, pipeline: pipeline) }
context 'when `multi_build_steps` is passed by the runner' do
it 'exposes release info' do
@@ -398,7 +398,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when job is made for merge request' do
let(:pipeline) { create(:ci_pipeline, source: :merge_request_event, project: project, ref: 'feature', merge_request: merge_request) }
- let!(:job) { create(:ci_build, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) }
let(:merge_request) { create(:merge_request) }
it 'sets branch as ref_type' do
@@ -439,6 +439,13 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
+ it "sets the runner's config" do
+ request_job info: { 'config' => { 'gpus' => 'all', 'ignored' => 'hello' } }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(runner.reload.config).to eq( { 'gpus' => 'all' } )
+ end
+
it "sets the runner's ip_address" do
post api('/jobs/request'),
params: { token: runner.token },
@@ -472,9 +479,9 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when project and pipeline have multiple jobs' do
- let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:job2) { create(:ci_build, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
- let!(:test_job) { create(:ci_build, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
+ let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:test_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
before do
job.success
@@ -524,8 +531,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when pipeline have jobs with artifacts' do
- let!(:job) { create(:ci_build, :tag, :artifacts, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:test_job) { create(:ci_build, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
+ let!(:job) { create(:ci_build, :pending, :queued, :tag, :artifacts, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:test_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
before do
job.success
@@ -544,10 +551,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when explicit dependencies are defined' do
- let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:job2) { create(:ci_build, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:test_job) do
- create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'deploy',
+ create(:ci_build, :pending, :queued, pipeline: pipeline, token: 'test-job-token', name: 'deploy',
stage: 'deploy', stage_idx: 1,
options: { script: ['bash'], dependencies: [job2.name] })
end
@@ -568,10 +575,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when dependencies is an empty array' do
- let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:job2) { create(:ci_build, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:empty_dependencies_job) do
- create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'empty_dependencies_job',
+ create(:ci_build, :pending, :queued, pipeline: pipeline, token: 'test-job-token', name: 'empty_dependencies_job',
stage: 'deploy', stage_idx: 1,
options: { script: ['bash'], dependencies: [] })
end
@@ -732,7 +739,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
describe 'port support' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: options) }
+ let(:job) { create(:ci_build, :pending, :queued, pipeline: pipeline, options: options) }
context 'when job image has ports' do
let(:options) do
@@ -784,7 +791,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
describe 'a job with excluded artifacts' do
context 'when excluded paths are defined' do
let(:job) do
- create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'test',
+ create(:ci_build, :pending, :queued, pipeline: pipeline, token: 'test-job-token', name: 'test',
stage: 'deploy', stage_idx: 1,
options: { artifacts: { paths: ['abc'], exclude: ['cde'] } })
end
@@ -832,7 +839,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
subject { request_job }
context 'when triggered by a user' do
- let(:job) { create(:ci_build, user: user, project: project) }
+ let(:job) { create(:ci_build, :pending, :queued, user: user, project: project) }
subject { request_job(id: job.id) }
diff --git a/spec/requests/api/ci/runner/runners_post_spec.rb b/spec/requests/api/ci/runner/runners_post_spec.rb
index b38630183f4..1696fe63d5d 100644
--- a/spec/requests/api/ci/runner/runners_post_spec.rb
+++ b/spec/requests/api/ci/runner/runners_post_spec.rb
@@ -94,7 +94,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when it exceeds the application limits' do
before do
- create(:ci_runner, runner_type: :project_type, projects: [project])
+ create(:ci_runner, runner_type: :project_type, projects: [project], contacted_at: 1.second.ago)
create(:plan_limits, :default_plan, ci_registered_project_runners: 1)
end
@@ -106,6 +106,22 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(project.runners.reload.size).to eq(1)
end
end
+
+ context 'when abandoned runners cause application limits to not be exceeded' do
+ before do
+ create(:ci_runner, runner_type: :project_type, projects: [project], created_at: 14.months.ago, contacted_at: 13.months.ago)
+ create(:plan_limits, :default_plan, ci_registered_project_runners: 1)
+ end
+
+ it 'creates runner' do
+ request
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['message']).to be_nil
+ expect(project.runners.reload.size).to eq(2)
+ expect(project.runners.recent.size).to eq(1)
+ end
+ end
end
context 'when group token is used' do
@@ -135,7 +151,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when it exceeds the application limits' do
before do
- create(:ci_runner, runner_type: :group_type, groups: [group])
+ create(:ci_runner, runner_type: :group_type, groups: [group], contacted_at: nil, created_at: 1.month.ago)
create(:plan_limits, :default_plan, ci_registered_group_runners: 1)
end
@@ -147,6 +163,23 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(group.runners.reload.size).to eq(1)
end
end
+
+ context 'when abandoned runners cause application limits to not be exceeded' do
+ before do
+ create(:ci_runner, runner_type: :group_type, groups: [group], created_at: 4.months.ago, contacted_at: 3.months.ago)
+ create(:ci_runner, runner_type: :group_type, groups: [group], contacted_at: nil, created_at: 4.months.ago)
+ create(:plan_limits, :default_plan, ci_registered_group_runners: 1)
+ end
+
+ it 'creates runner' do
+ request
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['message']).to be_nil
+ expect(group.runners.reload.size).to eq(3)
+ expect(group.runners.recent.size).to eq(1)
+ end
+ end
end
end
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 1727bc830fc..82fb4440429 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -137,11 +137,11 @@ RSpec.describe API::Ci::Runners do
get api('/runners/all', admin)
expect(json_response).to match_array [
- a_hash_including('description' => 'Project runner'),
- a_hash_including('description' => 'Two projects runner'),
- a_hash_including('description' => 'Group runner A'),
- a_hash_including('description' => 'Group runner B'),
- a_hash_including('description' => 'Shared runner')
+ a_hash_including('description' => 'Project runner', 'is_shared' => false, 'runner_type' => 'project_type'),
+ a_hash_including('description' => 'Two projects runner', 'is_shared' => false, 'runner_type' => 'project_type'),
+ a_hash_including('description' => 'Group runner A', 'is_shared' => false, 'runner_type' => 'group_type'),
+ a_hash_including('description' => 'Group runner B', 'is_shared' => false, 'runner_type' => 'group_type'),
+ a_hash_including('description' => 'Shared runner', 'is_shared' => true, 'runner_type' => 'instance_type')
]
end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index ac125e81acd..ccc9f8c50c4 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -3,12 +3,12 @@
require 'spec_helper'
RSpec.describe API::CommitStatuses do
- let!(:project) { create(:project, :repository) }
- let(:commit) { project.repository.commit }
- let(:guest) { create_user(:guest) }
- let(:reporter) { create_user(:reporter) }
- let(:developer) { create_user(:developer) }
- let(:sha) { commit.id }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:commit) { project.repository.commit }
+ let_it_be(:guest) { create_user(:guest) }
+ let_it_be(:reporter) { create_user(:reporter) }
+ let_it_be(:developer) { create_user(:developer) }
+ let_it_be(:sha) { commit.id }
describe "GET /projects/:id/repository/commits/:sha/statuses" do
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
@@ -233,27 +233,44 @@ RSpec.describe API::CommitStatuses do
end
end
- context 'when updatig a commit status' do
+ context 'when updating a commit status' do
+ let(:parameters) do
+ {
+ state: 'success',
+ name: 'coverage',
+ ref: 'master'
+ }
+ end
+
+ let(:updatable_optional_attributes) do
+ {
+ description: 'new description',
+ coverage: 90.0
+ }
+ end
+
+ # creating the initial commit status
before do
post api(post_url, developer), params: {
state: 'running',
context: 'coverage',
ref: 'master',
description: 'coverage test',
- coverage: 0.0,
+ coverage: 10.0,
target_url: 'http://gitlab.com/status'
}
+ end
+ subject(:send_request) do
post api(post_url, developer), params: {
- state: 'success',
- name: 'coverage',
- ref: 'master',
- description: 'new description',
- coverage: 90.0
+ **parameters,
+ **updatable_optional_attributes
}
end
it 'updates a commit status' do
+ send_request
+
expect(response).to have_gitlab_http_status(:created)
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success')
@@ -265,7 +282,28 @@ RSpec.describe API::CommitStatuses do
end
it 'does not create a new commit status' do
- expect(CommitStatus.count).to eq 1
+ expect { send_request }.not_to change { CommitStatus.count }
+ end
+
+ context 'when the `state` parameter is sent the same' do
+ let(:parameters) do
+ {
+ state: 'running',
+ name: 'coverage',
+ ref: 'master'
+ }
+ end
+
+ it 'does not update the commit status' do
+ send_request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+
+ commit_status = project.commit_statuses.find_by!(name: 'coverage')
+
+ expect(commit_status.description).to eq('coverage test')
+ expect(commit_status.coverage).to eq(10.0)
+ end
end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index ac3aa808f37..1162ae76d15 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -284,6 +284,18 @@ RSpec.describe API::Commits do
end
end
end
+
+ context 'with the optional trailers parameter' do
+ it 'includes the Git trailers' do
+ get api("/projects/#{project_id}/repository/commits?ref_name=6d394385cf567f80a8fd85055db1ab4c5295806f&trailers=true", current_user)
+
+ commit = json_response[0]
+
+ expect(commit['trailers']).to eq(
+ 'Signed-off-by' => 'Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>'
+ )
+ end
+ end
end
end
@@ -1503,6 +1515,13 @@ RSpec.describe API::Commits do
expect(json_response).to eq("dry_run" => "success")
expect(project.commit(branch)).to eq(head)
end
+
+ it 'supports the use of a custom commit message' do
+ post api(route, user), params: { branch: branch, message: 'foo' }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response["message"]).to eq('foo')
+ end
end
context 'when repository is disabled' do
diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb
index 0ff88cb41a8..4120edabea3 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) { {} }
using RSpec::Parameterized::TableSyntax
@@ -428,6 +429,7 @@ RSpec.describe API::ComposerPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
diff --git a/spec/requests/api/conan_instance_packages_spec.rb b/spec/requests/api/conan_instance_packages_spec.rb
index 817530f0bad..ff3b332c620 100644
--- a/spec/requests/api/conan_instance_packages_spec.rb
+++ b/spec/requests/api/conan_instance_packages_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe API::ConanInstancePackages do
+ let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } }
+
include_context 'conan api setup'
describe 'GET /api/v4/packages/conan/v1/ping' do
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index 42c6c987872..c3abb06c5c1 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -7,33 +7,33 @@ RSpec.describe API::DebianGroupPackages do
include_context 'Debian repository shared context', :group, false do
describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/Release.gpg" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/Release" }
+ 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, /^TODO Release$/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/InRelease" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Packages'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/
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" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO File'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/
end
end
end
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index f400b6e928c..c11c4ecc12a 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -7,43 +7,55 @@ RSpec.describe API::DebianProjectPackages do
include_context 'Debian repository shared context', :project, true do
describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/Release.gpg" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/Release" }
+ 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, /^TODO Release$/
end
describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/InRelease" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Packages'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/
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" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO File'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/
end
describe 'PUT projects/:id/packages/debian/:file_name' do
let(:method) { :put }
let(:url) { "/projects/#{container.id}/packages/debian/#{file_name}" }
+ let(:snowplow_gitlab_standard_context) { { project: container, user: user, namespace: container.namespace } }
- it_behaves_like 'Debian repository write endpoint', 'upload request', :created
+ context 'with a deb' do
+ let(:file_name) { 'libsample0_1.2.3~alpha2_amd64.deb' }
+
+ it_behaves_like 'Debian repository write endpoint', 'upload request', :created
+ end
+
+ context 'with a changes file' do
+ let(:file_name) { 'sample_1.2.3~alpha2_amd64.changes' }
+
+ it_behaves_like 'Debian repository write endpoint', 'upload request', :created
+ end
end
describe 'PUT projects/:id/packages/debian/:file_name/authorize' do
+ let(:file_name) { 'libsample0_1.2.3~alpha2_amd64.deb' }
let(:method) { :put }
let(:url) { "/projects/#{container.id}/packages/debian/#{file_name}/authorize" }
diff --git a/spec/requests/api/feature_flag_scopes_spec.rb b/spec/requests/api/feature_flag_scopes_spec.rb
deleted file mode 100644
index da5b2cbb7ae..00000000000
--- a/spec/requests/api/feature_flag_scopes_spec.rb
+++ /dev/null
@@ -1,319 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.describe API::FeatureFlagScopes do
- include FeatureFlagHelpers
-
- let(:project) { create(:project, :repository) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:user) { developer }
-
- before do
- project.add_developer(developer)
- project.add_reporter(reporter)
- end
-
- shared_examples_for 'check user permission' do
- context 'when user is reporter' do
- let(:user) { reporter }
-
- it 'forbids the request' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
- end
-
- shared_examples_for 'not found' do
- it 'returns Not Found' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- describe 'GET /projects/:id/feature_flag_scopes' do
- subject do
- get api("/projects/#{project.id}/feature_flag_scopes", user),
- params: params
- end
-
- let(:feature_flag_1) { create_flag(project, 'flag_1', true) }
- let(:feature_flag_2) { create_flag(project, 'flag_2', true) }
-
- before do
- create_scope(feature_flag_1, 'staging', false)
- create_scope(feature_flag_1, 'production', true)
- create_scope(feature_flag_2, 'review/*', false)
- end
-
- context 'when environment is production' do
- let(:params) { { environment: 'production' } }
-
- it_behaves_like 'check user permission'
-
- it 'returns all effective feature flags under the environment' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag_detailed_scopes')
- expect(json_response.second).to include({ 'name' => 'flag_1', 'active' => true })
- expect(json_response.first).to include({ 'name' => 'flag_2', 'active' => true })
- end
- end
-
- context 'when environment is staging' do
- let(:params) { { environment: 'staging' } }
-
- it 'returns all effective feature flags under the environment' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.second).to include({ 'name' => 'flag_1', 'active' => false })
- expect(json_response.first).to include({ 'name' => 'flag_2', 'active' => true })
- end
- end
-
- context 'when environment is review/feature X' do
- let(:params) { { environment: 'review/feature X' } }
-
- it 'returns all effective feature flags under the environment' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.second).to include({ 'name' => 'flag_1', 'active' => true })
- expect(json_response.first).to include({ 'name' => 'flag_2', 'active' => false })
- end
- end
- end
-
- describe 'GET /projects/:id/feature_flags/:name/scopes' do
- subject do
- get api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes", user)
- end
-
- context 'when there are two scopes' do
- let(:feature_flag) { create_flag(project, 'test') }
- let!(:additional_scope) { create_scope(feature_flag, 'production', false) }
-
- it_behaves_like 'check user permission'
-
- it 'returns scopes of the feature flag' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag_scopes')
- expect(json_response.count).to eq(2)
- expect(json_response.first['environment_scope']).to eq(feature_flag.scopes[0].environment_scope)
- expect(json_response.second['environment_scope']).to eq(feature_flag.scopes[1].environment_scope)
- end
- end
-
- context 'when there are no feature flags' do
- let(:feature_flag) { double(:feature_flag, name: 'test') }
-
- it_behaves_like 'not found'
- end
- end
-
- describe 'POST /projects/:id/feature_flags/:name/scopes' do
- subject do
- post api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes", user),
- params: params
- end
-
- let(:params) do
- {
- environment_scope: 'staging',
- active: true,
- strategies: [{ name: 'userWithId', parameters: { 'userIds': 'a,b,c' } }].to_json
- }
- end
-
- context 'when there is a corresponding feature flag' do
- let!(:feature_flag) { create(:operations_feature_flag, project: project) }
-
- it_behaves_like 'check user permission'
-
- it 'creates a new scope' do
- subject
-
- expect(response).to have_gitlab_http_status(:created)
- expect(response).to match_response_schema('public_api/v4/feature_flag_scope')
- expect(json_response['environment_scope']).to eq(params[:environment_scope])
- expect(json_response['active']).to eq(params[:active])
- expect(json_response['strategies']).to eq(Gitlab::Json.parse(params[:strategies]))
- end
-
- context 'when the scope already exists' do
- before do
- create_scope(feature_flag, params[:environment_scope])
- end
-
- it 'returns error' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to include('Scopes environment scope (staging) has already been taken')
- end
- end
- end
-
- context 'when feature flag is not found' do
- let(:feature_flag) { double(:feature_flag, name: 'test') }
-
- it_behaves_like 'not found'
- end
- end
-
- describe 'GET /projects/:id/feature_flags/:name/scopes/:environment_scope' do
- subject do
- get api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes/#{environment_scope}",
- user)
- end
-
- let(:environment_scope) { scope.environment_scope }
-
- shared_examples_for 'successful response' do
- it 'returns a scope' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag_scope')
- expect(json_response['id']).to eq(scope.id)
- expect(json_response['active']).to eq(scope.active)
- expect(json_response['environment_scope']).to eq(scope.environment_scope)
- end
- end
-
- context 'when there is a feature flag' do
- let!(:feature_flag) { create(:operations_feature_flag, project: project) }
- let(:scope) { feature_flag.default_scope }
-
- it_behaves_like 'check user permission'
- it_behaves_like 'successful response'
-
- context 'when environment scope includes slash' do
- let!(:scope) { create_scope(feature_flag, 'review/*', false) }
-
- it_behaves_like 'not found'
-
- context 'when URL-encoding the environment scope parameter' do
- let(:environment_scope) { CGI.escape(scope.environment_scope) }
-
- it_behaves_like 'successful response'
- end
- end
- end
-
- context 'when there are no feature flags' do
- let(:feature_flag) { double(:feature_flag, name: 'test') }
- let(:scope) { double(:feature_flag_scope, environment_scope: 'prd') }
-
- it_behaves_like 'not found'
- end
- end
-
- describe 'PUT /projects/:id/feature_flags/:name/scopes/:environment_scope' do
- subject do
- put api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes/#{environment_scope}",
- user), params: params
- end
-
- let(:environment_scope) { scope.environment_scope }
-
- let(:params) do
- {
- active: true,
- strategies: [{ name: 'userWithId', parameters: { 'userIds': 'a,b,c' } }].to_json
- }
- end
-
- context 'when there is a corresponding feature flag' do
- let!(:feature_flag) { create(:operations_feature_flag, project: project) }
- let(:scope) { create_scope(feature_flag, 'staging', false, [{ name: "default", parameters: {} }]) }
-
- it_behaves_like 'check user permission'
-
- it 'returns the updated scope' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag_scope')
- expect(json_response['id']).to eq(scope.id)
- expect(json_response['active']).to eq(params[:active])
- expect(json_response['strategies']).to eq(Gitlab::Json.parse(params[:strategies]))
- end
-
- context 'when there are no corresponding feature flag scopes' do
- let(:scope) { double(:feature_flag_scope, environment_scope: 'prd') }
-
- it_behaves_like 'not found'
- end
- end
-
- context 'when there are no corresponding feature flags' do
- let(:feature_flag) { double(:feature_flag, name: 'test') }
- let(:scope) { double(:feature_flag_scope, environment_scope: 'prd') }
-
- it_behaves_like 'not found'
- end
- end
-
- describe 'DELETE /projects/:id/feature_flags/:name/scopes/:environment_scope' do
- subject do
- delete api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes/#{environment_scope}",
- user)
- end
-
- let(:environment_scope) { scope.environment_scope }
-
- shared_examples_for 'successful response' do
- it 'destroys the scope' do
- expect { subject }
- .to change { Operations::FeatureFlagScope.exists?(environment_scope: scope.environment_scope) }
- .from(true).to(false)
-
- expect(response).to have_gitlab_http_status(:no_content)
- end
- end
-
- context 'when there is a feature flag' do
- let!(:feature_flag) { create(:operations_feature_flag, project: project) }
-
- context 'when there is a targeted scope' do
- let!(:scope) { create_scope(feature_flag, 'production', false) }
-
- it_behaves_like 'check user permission'
- it_behaves_like 'successful response'
-
- context 'when environment scope includes slash' do
- let!(:scope) { create_scope(feature_flag, 'review/*', false) }
-
- it_behaves_like 'not found'
-
- context 'when URL-encoding the environment scope parameter' do
- let(:environment_scope) { CGI.escape(scope.environment_scope) }
-
- it_behaves_like 'successful response'
- end
- end
- end
-
- context 'when there are no targeted scopes' do
- let!(:scope) { double(:feature_flag_scope, environment_scope: 'production') }
-
- it_behaves_like 'not found'
- end
- end
-
- context 'when there are no feature flags' do
- let(:feature_flag) { double(:feature_flag, name: 'test') }
- let(:scope) { double(:feature_flag_scope, environment_scope: 'prd') }
-
- it_behaves_like 'not found'
- end
- end
-end
diff --git a/spec/requests/api/feature_flags_spec.rb b/spec/requests/api/feature_flags_spec.rb
index dd12648f4dd..2cd52c0a5e5 100644
--- a/spec/requests/api/feature_flags_spec.rb
+++ b/spec/requests/api/feature_flags_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe API::FeatureFlags do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/feature_flags')
- expect(json_response.map { |f| f['version'] }).to eq(%w[legacy_flag legacy_flag])
+ expect(json_response.map { |f| f['version'] }).to eq(%w[new_version_flag new_version_flag])
end
it 'does not have N+1 problem' do
@@ -145,7 +145,7 @@ RSpec.describe API::FeatureFlags do
expect(response).to match_response_schema('public_api/v4/feature_flag')
expect(json_response['name']).to eq(feature_flag.name)
expect(json_response['description']).to eq(feature_flag.description)
- expect(json_response['version']).to eq('legacy_flag')
+ expect(json_response['version']).to eq('new_version_flag')
end
it_behaves_like 'check user permission'
@@ -453,210 +453,6 @@ RSpec.describe API::FeatureFlags do
end
end
- describe 'POST /projects/:id/feature_flags/:name/enable' do
- subject do
- post api("/projects/#{project.id}/feature_flags/#{params[:name]}/enable", user),
- params: params
- end
-
- let(:params) do
- {
- name: 'awesome-feature',
- environment_scope: 'production',
- strategy: { name: 'userWithId', parameters: { userIds: 'Project:1' } }.to_json
- }
- end
-
- context 'when feature flag does not exist yet' do
- it 'creates a new feature flag with the specified scope and strategy' do
- subject
-
- feature_flag = project.operations_feature_flags.last
- scope = feature_flag.scopes.find_by_environment_scope(params[:environment_scope])
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag')
- expect(feature_flag.name).to eq(params[:name])
- expect(scope.strategies).to eq([Gitlab::Json.parse(params[:strategy])])
- expect(feature_flag.version).to eq('legacy_flag')
- end
-
- it 'returns the flag version and strategies in the json response' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag')
- expect(json_response.slice('version', 'strategies')).to eq({
- 'version' => 'legacy_flag',
- 'strategies' => []
- })
- end
-
- it_behaves_like 'check user permission'
- end
-
- context 'when feature flag exists already' do
- let!(:feature_flag) { create_flag(project, params[:name]) }
-
- context 'when feature flag scope does not exist yet' do
- it 'creates a new scope with the specified strategy' do
- subject
-
- scope = feature_flag.scopes.find_by_environment_scope(params[:environment_scope])
- expect(response).to have_gitlab_http_status(:ok)
- expect(scope.strategies).to eq([Gitlab::Json.parse(params[:strategy])])
- end
-
- it_behaves_like 'check user permission'
- end
-
- context 'when feature flag scope exists already' do
- let(:defined_strategy) { { name: 'userWithId', parameters: { userIds: 'Project:2' } } }
-
- before do
- create_scope(feature_flag, params[:environment_scope], true, [defined_strategy])
- end
-
- it 'adds an additional strategy to the scope' do
- subject
-
- scope = feature_flag.scopes.find_by_environment_scope(params[:environment_scope])
- expect(response).to have_gitlab_http_status(:ok)
- expect(scope.strategies).to eq([defined_strategy.deep_stringify_keys, Gitlab::Json.parse(params[:strategy])])
- end
-
- context 'when the specified strategy exists already' do
- let(:defined_strategy) { Gitlab::Json.parse(params[:strategy]) }
-
- it 'does not add a duplicate strategy' do
- subject
-
- scope = feature_flag.scopes.find_by_environment_scope(params[:environment_scope])
- strategy_count = scope.strategies.count { |strategy| strategy['name'] == 'userWithId' }
- expect(response).to have_gitlab_http_status(:ok)
- expect(strategy_count).to eq(1)
- end
- end
- end
- end
-
- context 'with a version 2 flag' do
- let!(:feature_flag) { create(:operations_feature_flag, :new_version_flag, project: project, name: params[:name]) }
-
- it 'does not change the flag and returns an unprocessable_entity response' do
- subject
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response).to eq({ 'message' => 'Version 2 flags not supported' })
- feature_flag.reload
- expect(feature_flag.scopes).to eq([])
- expect(feature_flag.strategies).to eq([])
- end
- end
- end
-
- describe 'POST /projects/:id/feature_flags/:name/disable' do
- subject do
- post api("/projects/#{project.id}/feature_flags/#{params[:name]}/disable", user),
- params: params
- end
-
- let(:params) do
- {
- name: 'awesome-feature',
- environment_scope: 'production',
- strategy: { name: 'userWithId', parameters: { userIds: 'Project:1' } }.to_json
- }
- end
-
- context 'when feature flag does not exist yet' do
- it_behaves_like 'not found'
- end
-
- context 'when feature flag exists already' do
- let!(:feature_flag) { create_flag(project, params[:name]) }
-
- context 'when feature flag scope does not exist yet' do
- it_behaves_like 'not found'
- end
-
- context 'when feature flag scope exists already and has the specified strategy' do
- let(:defined_strategies) do
- [
- { name: 'userWithId', parameters: { userIds: 'Project:1' } },
- { name: 'userWithId', parameters: { userIds: 'Project:2' } }
- ]
- end
-
- before do
- create_scope(feature_flag, params[:environment_scope], true, defined_strategies)
- end
-
- it 'removes the strategy from the scope' do
- subject
-
- scope = feature_flag.scopes.find_by_environment_scope(params[:environment_scope])
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag')
- expect(scope.strategies)
- .to eq([{ name: 'userWithId', parameters: { userIds: 'Project:2' } }.deep_stringify_keys])
- end
-
- it 'returns the flag version and strategies in the json response' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flag')
- expect(json_response.slice('version', 'strategies')).to eq({
- 'version' => 'legacy_flag',
- 'strategies' => []
- })
- end
-
- it_behaves_like 'check user permission'
-
- context 'when strategies become empty array after the removal' do
- let(:defined_strategies) do
- [{ name: 'userWithId', parameters: { userIds: 'Project:1' } }]
- end
-
- it 'destroys the scope' do
- subject
-
- scope = feature_flag.scopes.find_by_environment_scope(params[:environment_scope])
- expect(response).to have_gitlab_http_status(:ok)
- expect(scope).to be_nil
- end
-
- it_behaves_like 'check user permission'
- end
- end
-
- context 'when scope exists already but cannot find the corresponding strategy' do
- let(:defined_strategy) { { name: 'userWithId', parameters: { userIds: 'Project:2' } } }
-
- before do
- create_scope(feature_flag, params[:environment_scope], true, [defined_strategy])
- end
-
- it_behaves_like 'not found'
- end
- end
-
- context 'with a version 2 feature flag' do
- let!(:feature_flag) { create(:operations_feature_flag, :new_version_flag, project: project, name: params[:name]) }
-
- it 'does not change the flag and returns an unprocessable_entity response' do
- subject
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response).to eq({ 'message' => 'Version 2 flags not supported' })
- feature_flag.reload
- expect(feature_flag.scopes).to eq([])
- expect(feature_flag.strategies).to eq([])
- end
- end
- end
-
describe 'PUT /projects/:id/feature_flags/:name' do
context 'with a legacy feature flag' do
let!(:feature_flag) do
@@ -664,13 +460,13 @@ RSpec.describe API::FeatureFlags do
name: 'feature1', description: 'old description')
end
- it 'returns a 422' do
+ it 'returns a 404' do
params = { description: 'new description' }
put api("/projects/#{project.id}/feature_flags/feature1", user), params: params
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response).to eq({ 'message' => 'PUT operations are not supported for legacy feature flags' })
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response).to eq({ 'message' => '404 Not Found' })
expect(feature_flag.reload.description).to eq('old description')
end
end
@@ -984,7 +780,7 @@ RSpec.describe API::FeatureFlags do
params: params
end
- let!(:feature_flag) { create(:operations_feature_flag, project: project) }
+ let!(:feature_flag) { create(:operations_feature_flag, :legacy_flag, project: project) }
let(:params) { {} }
it 'destroys the feature flag' do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 71a4a1a2784..869df06b60c 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -558,8 +558,7 @@ RSpec.describe API::Files do
get api(url, current_user), params: params
- expect(response.headers["Cache-Control"]).to include("no-store")
- expect(response.headers["Cache-Control"]).to include("no-cache")
+ expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index a5e40eec919..378ee2f3e7c 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -18,7 +18,8 @@ RSpec.describe API::GenericPackages do
let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) }
let(:user) { personal_access_token.user }
- let(:ci_build) { create(:ci_build, :running, user: user) }
+ let(:ci_build) { create(:ci_build, :running, user: user, project: project) }
+ let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } }
def auth_header
return {} if user_role == :anonymous
diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb
index e678b6cf1c8..0143340de11 100644
--- a/spec/requests/api/go_proxy_spec.rb
+++ b/spec/requests/api/go_proxy_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe API::GoProxy do
let_it_be(:base) { "#{Settings.build_gitlab_go_url}/#{project.full_path}" }
let_it_be(:oauth) { create :oauth_access_token, scopes: 'api', resource_owner: user }
- let_it_be(:job) { create :ci_build, user: user, status: :running }
+ let_it_be(:job) { create :ci_build, user: user, status: :running, project: project }
let_it_be(:pa_token) { create :personal_access_token, user: user }
let_it_be(:modules) do
diff --git a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
index 99647d0fa3a..578a71a7272 100644
--- a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
+++ b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe 'Getting Ci Cd Setting' do
expect(settings_data['mergePipelinesEnabled']).to eql project.ci_cd_settings.merge_pipelines_enabled?
expect(settings_data['mergeTrainsEnabled']).to eql project.ci_cd_settings.merge_trains_enabled?
expect(settings_data['keepLatestArtifact']).to eql project.keep_latest_artifacts_available?
+ expect(settings_data['jobTokenScopeEnabled']).to eql project.ci_cd_settings.job_token_scope_enabled?
end
end
end
diff --git a/spec/requests/api/graphql/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb
index 452610ab18f..31cb0393d7f 100644
--- a/spec/requests/api/graphql/group/group_members_spec.rb
+++ b/spec/requests/api/graphql/group/group_members_spec.rb
@@ -14,6 +14,23 @@ RSpec.describe 'getting group members information' do
[user_1, user_2].each { |user| parent_group.add_guest(user) }
end
+ context 'when a member is invited only via email' do
+ before do
+ create(:group_member, :invited, source: parent_group)
+ end
+
+ it 'returns null in the user field' do
+ fetch_members(group: parent_group, args: { relations: [:DIRECT] })
+
+ expect(graphql_errors).to be_nil
+ expect(graphql_data_at(:group, :group_members, :edges, :node)).to contain_exactly(
+ { 'user' => { 'id' => global_id_of(user_1) } },
+ { 'user' => { 'id' => global_id_of(user_2) } },
+ 'user' => nil
+ )
+ end
+ end
+
context 'when the request is correct' do
it_behaves_like 'a working graphql query' do
before do
diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb
index 601cab6aade..2b80b5239c8 100644
--- a/spec/requests/api/graphql/group/milestones_spec.rb
+++ b/spec/requests/api/graphql/group/milestones_spec.rb
@@ -40,6 +40,13 @@ RSpec.describe 'Milestones through GroupQuery' do
expect_array_response(milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s)
end
+
+ it 'fetches milestones between timeframe start and end arguments' do
+ today = Date.today
+ fetch_milestones(user, { timeframe: { start: today.to_s, end: (today + 2.days).to_s } })
+
+ expect_array_response(milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s)
+ end
end
context 'when filtering by state' do
diff --git a/spec/requests/api/graphql/group/timelogs_spec.rb b/spec/requests/api/graphql/group/timelogs_spec.rb
index 6e21a73afa9..05b6ee3ff89 100644
--- a/spec/requests/api/graphql/group/timelogs_spec.rb
+++ b/spec/requests/api/graphql/group/timelogs_spec.rb
@@ -17,8 +17,37 @@ RSpec.describe 'Timelogs through GroupQuery' do
let(:timelogs_data) { graphql_data['group']['timelogs']['nodes'] }
- before do
- group.add_developer(user)
+ context 'when the project is private' do
+ let_it_be(:group2) { create(:group) }
+ let_it_be(:project2) { create(:project, :private, group: group2) }
+ let_it_be(:issue2) { create(:issue, project: project2) }
+ let_it_be(:timelog3) { create(:timelog, issue: issue2, spent_at: '2019-08-13 14:00:00') }
+
+ subject { post_graphql(query(full_path: group2.full_path), current_user: user) }
+
+ context 'when the user is not a member of the project' do
+ it 'returns no timelogs' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(graphql_errors).to be_nil
+ expect(timelog_array.size).to eq 0
+ end
+ end
+
+ context 'when the user is a member of the project' do
+ before do
+ project2.add_developer(user)
+ end
+
+ it 'returns timelogs' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(graphql_errors).to be_nil
+ expect(timelog_array.size).to eq 1
+ end
+ end
end
context 'when the request is correct' do
@@ -74,18 +103,6 @@ RSpec.describe 'Timelogs through GroupQuery' do
expect(timelogs_data).to be_empty
end
end
-
- context 'when user has no permission to read group timelogs' do
- it 'returns empty result' do
- guest = create(:user)
- group.add_guest(guest)
- post_graphql(query, current_user: guest)
-
- expect(response).to have_gitlab_http_status(:success)
- expect(graphql_errors).to be_nil
- expect(timelogs_data).to be_empty
- end
- end
end
end
@@ -95,7 +112,7 @@ RSpec.describe 'Timelogs through GroupQuery' do
end
end
- def query(timelog_params = params)
+ def query(timelog_params: params, full_path: group.full_path)
timelog_nodes = <<~NODE
nodes {
spentAt
@@ -114,7 +131,7 @@ RSpec.describe 'Timelogs through GroupQuery' do
graphql_query_for(
:group,
- { full_path: group.full_path },
+ { full_path: full_path },
query_graphql_field(:timelogs, timelog_params, timelog_nodes)
)
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 0dcae28ac5d..0d7571d91ca 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,8 +5,16 @@ require 'spec_helper'
RSpec.describe 'CiCdSettingsUpdate' do
include GraphqlHelpers
- let_it_be(:project) { create(:project, keep_latest_artifact: true) }
- let(:variables) { { full_path: project.full_path, keep_latest_artifact: false } }
+ let_it_be(:project) { create(:project, keep_latest_artifact: true, ci_job_token_scope_enabled: true) }
+
+ let(:variables) do
+ {
+ full_path: project.full_path,
+ keep_latest_artifact: false,
+ job_token_scope_enabled: false
+ }
+ end
+
let(:mutation) { graphql_mutation(:ci_cd_settings_update, variables) }
context 'when unauthorized' do
@@ -45,6 +53,26 @@ RSpec.describe 'CiCdSettingsUpdate' do
expect(project.keep_latest_artifact).to eq(false)
end
+ it 'updates job_token_scope_enabled' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ project.reload
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(project.ci_job_token_scope_enabled).to eq(false)
+ end
+
+ it 'does not update job_token_scope_enabled if not specified' do
+ variables.except!(:job_token_scope_enabled)
+
+ post_graphql_mutation(mutation, current_user: user)
+
+ project.reload
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(project.ci_job_token_scope_enabled).to eq(true)
+ end
+
context 'when bad arguments are provided' do
let(:variables) { { full_path: '', keep_latest_artifact: false } }
diff --git a/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb
new file mode 100644
index 00000000000..07b05ead651
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'RunnersRegistrationTokenReset' do
+ include GraphqlHelpers
+
+ let(:mutation) { graphql_mutation(:runners_registration_token_reset, input) }
+ let(:mutation_response) { graphql_mutation_response(:runners_registration_token_reset) }
+
+ subject { post_graphql_mutation(mutation, current_user: user) }
+
+ shared_examples 'unauthorized' do
+ it 'returns an error' do
+ subject
+
+ expect(graphql_errors).not_to be_empty
+ expect(graphql_errors).to include(a_hash_including('message' => "The resource that you are attempting to access does not exist or you don't have permission to perform this action"))
+ expect(mutation_response).to be_nil
+ end
+ end
+
+ shared_context 'when unauthorized' do |scope|
+ context 'when unauthorized' do
+ let_it_be(:user) { create(:user) }
+
+ context "when not a #{scope} member" do
+ it_behaves_like 'unauthorized'
+ end
+
+ context "with a non-admin #{scope} member" do
+ before do
+ target.add_developer(user)
+ end
+
+ it_behaves_like 'unauthorized'
+ end
+ end
+ end
+
+ shared_context 'when authorized' do |scope|
+ it 'resets runner registration token' do
+ expect { subject }.to change { get_token }
+ expect(response).to have_gitlab_http_status(:success)
+
+ expect(mutation_response).not_to be_nil
+ expect(mutation_response['errors']).to be_empty
+ expect(mutation_response['token']).not_to be_empty
+ expect(mutation_response['token']).to eq(get_token)
+ end
+
+ context 'when malformed id is provided' do
+ let(:input) { { type: "#{scope.upcase}_TYPE", id: 'some string' } }
+
+ it 'returns errors' do
+ expect { subject }.not_to change { get_token }
+
+ expect(graphql_errors).not_to be_empty
+ expect(mutation_response).to be_nil
+ end
+ end
+ end
+
+ context 'applied to project' do
+ let_it_be(:project) { create_default(:project) }
+
+ let(:input) { { type: 'PROJECT_TYPE', id: project.to_global_id.to_s } }
+
+ include_context 'when unauthorized', 'project' do
+ let(:target) { project }
+ end
+
+ include_context 'when authorized', 'project' do
+ let_it_be(:user) { project.owner }
+
+ def get_token
+ project.reload.runners_token
+ end
+ end
+ end
+
+ context 'applied to group' do
+ let_it_be(:group) { create_default(:group) }
+
+ let(:input) { { type: 'GROUP_TYPE', id: group.to_global_id.to_s } }
+
+ include_context 'when unauthorized', 'group' do
+ let(:target) { group }
+ end
+
+ include_context 'when authorized', 'group' do
+ let_it_be(:user) { create_default(:group_member, :maintainer, user: create(:user), group: group ).user }
+
+ def get_token
+ group.reload.runners_token
+ end
+ end
+ end
+
+ context 'applied to instance' do
+ before do
+ ApplicationSetting.create_from_defaults
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ end
+
+ let(:input) { { type: 'INSTANCE_TYPE' } }
+
+ context 'when unauthorized' do
+ let(:user) { create(:user) }
+
+ it_behaves_like 'unauthorized'
+ end
+
+ include_context 'when authorized', 'instance' do
+ let_it_be(:user) { create(:user, :admin) }
+
+ def get_token
+ ApplicationSetting.current_without_cache.runners_registration_token
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/labels/create_spec.rb b/spec/requests/api/graphql/mutations/labels/create_spec.rb
index ca3ccc8e06c..28284408306 100644
--- a/spec/requests/api/graphql/mutations/labels/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/labels/create_spec.rb
@@ -11,8 +11,7 @@ RSpec.describe Mutations::Labels::Create do
{
'title' => 'foo',
'description' => 'some description',
- 'color' => '#FF0000',
- 'removeOnClose' => true
+ 'color' => '#FF0000'
}
end
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index d944c9e9e57..214c804c519 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe 'Creating a Snippet' do
it 'passes disable_spam_action_service param to service' do
expect(::Snippets::CreateService)
.to receive(:new)
- .with(anything, anything, hash_including(disable_spam_action_service: true))
+ .with(project: anything, current_user: anything, params: hash_including(disable_spam_action_service: true))
.and_call_original
subject
@@ -190,7 +190,7 @@ RSpec.describe 'Creating a Snippet' do
it do
expect(::Snippets::CreateService).to receive(:new)
- .with(nil, user, hash_including(files: expected_value))
+ .with(project: nil, current_user: user, params: hash_including(files: expected_value))
.and_return(double(execute: creation_response))
subject
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index 28ab593526a..77efb786dcb 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -90,7 +90,7 @@ RSpec.describe 'Updating a Snippet' do
it 'passes disable_spam_action_service param to service' do
expect(::Snippets::UpdateService)
.to receive(:new)
- .with(anything, anything, hash_including(disable_spam_action_service: true))
+ .with(project: anything, current_user: anything, params: hash_including(disable_spam_action_service: true))
.and_call_original
subject
diff --git a/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb
index 705ef28ffd4..8f92105dc9c 100644
--- a/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb
@@ -22,8 +22,8 @@ RSpec.describe 'Marking all todos done' do
graphql_mutation(:todos_mark_all_done, input,
<<-QL.strip_heredoc
clientMutationId
+ todos { id }
errors
- updatedIds
QL
)
end
@@ -40,7 +40,7 @@ RSpec.describe 'Marking all todos done' do
expect(todo3.reload.state).to eq('done')
expect(other_user_todo.reload.state).to eq('pending')
- updated_todo_ids = mutation_response['updatedIds']
+ updated_todo_ids = mutation_response['todos'].map { |todo| todo['id'] }
expect(updated_todo_ids).to contain_exactly(global_id_of(todo1), global_id_of(todo3))
end
@@ -52,7 +52,7 @@ RSpec.describe 'Marking all todos done' do
expect(todo3.reload.state).to eq('pending')
expect(other_user_todo.reload.state).to eq('pending')
- updated_todo_ids = mutation_response['updatedIds']
+ updated_todo_ids = mutation_response['todos']
expect(updated_todo_ids).to be_empty
end
diff --git a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb
index 3e96d5c5058..e71a232ff7c 100644
--- a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb
+++ b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb
@@ -22,7 +22,6 @@ RSpec.describe 'Restoring many Todos' do
<<-QL.strip_heredoc
clientMutationId
errors
- updatedIds
todos {
id
state
@@ -44,7 +43,6 @@ RSpec.describe 'Restoring many Todos' do
expect(mutation_response).to include(
'errors' => be_empty,
- 'updatedIds' => match_array(input_ids),
'todos' => contain_exactly(
{ 'id' => global_id_of(todo1), 'state' => 'pending' },
{ 'id' => global_id_of(todo2), 'state' => 'pending' }
diff --git a/spec/requests/api/graphql/packages/composer_spec.rb b/spec/requests/api/graphql/packages/composer_spec.rb
index 34137a07c34..9830623ede8 100644
--- a/spec/requests/api/graphql/packages/composer_spec.rb
+++ b/spec/requests/api/graphql/packages/composer_spec.rb
@@ -3,62 +3,35 @@ require 'spec_helper'
RSpec.describe 'package details' do
include GraphqlHelpers
+ include_context 'package details setup'
- let_it_be(:project) { create(:project) }
- let_it_be(:composer_package) { create(:composer_package, project: project) }
+ let_it_be(:package) { create(:composer_package, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
# we are forced to manually create the metadatum, without using the factory to force the sha to be a string
# and avoid an error where gitaly can't find the repository
- create(:composer_metadatum, package: composer_package, target_sha: 'foo_sha', composer_json: composer_json)
+ create(:composer_metadatum, package: package, target_sha: 'foo_sha', composer_json: composer_json)
end
- let(:depth) { 3 }
- let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
let(:metadata) { query_graphql_fragment('ComposerMetadata') }
- let(:package_files) { all_graphql_fields_for('PackageFile') }
- let(:user) { project.owner }
- let(:package_global_id) { global_id_of(composer_package) }
- let(:package_details) { graphql_data_at(:package) }
- let(:metadata_response) { graphql_data_at(:package, :metadata) }
let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
- let(:query) do
- graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
- #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)}
- metadata {
- #{metadata}
- }
- packageFiles {
- nodes {
- #{package_files}
- }
- }
- FIELDS
- end
-
subject { post_graphql(query, current_user: user) }
before do
subject
end
- it_behaves_like 'a working graphql query' do
- it 'matches the JSON schema' do
- expect(package_details).to match_schema('graphql/packages/package_details')
- end
- end
+ it_behaves_like 'a package detail'
- describe 'Composer' do
- it 'has the correct metadata' do
- expect(metadata_response).to include(
- 'targetSha' => 'foo_sha',
- 'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s)
- )
- end
+ it 'has the correct metadata' do
+ expect(metadata_response).to include(
+ 'targetSha' => 'foo_sha',
+ 'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s)
+ )
+ end
- it 'does not have files' do
- expect(package_files_response).to be_empty
- end
+ it 'does not have files' do
+ expect(package_files_response).to be_empty
end
end
diff --git a/spec/requests/api/graphql/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb
index dc64c5057d5..84c5af33e5d 100644
--- a/spec/requests/api/graphql/packages/conan_spec.rb
+++ b/spec/requests/api/graphql/packages/conan_spec.rb
@@ -3,26 +3,13 @@ require 'spec_helper'
RSpec.describe 'conan package details' do
include GraphqlHelpers
+ include_context 'package details setup'
- let_it_be(:project) { create(:project) }
- let_it_be(:conan_package) { create(:conan_package, project: project) }
+ let_it_be(:package) { create(:conan_package, project: project) }
- let(:package_global_id) { global_id_of(conan_package) }
let(:metadata) { query_graphql_fragment('ConanMetadata') }
- let(:first_file) { conan_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } }
-
- let(:depth) { 3 }
- let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
- let(:package_files) { all_graphql_fields_for('PackageFile') }
let(:package_files_metadata) {query_graphql_fragment('ConanFileMetadata')}
- let(:user) { project.owner }
- let(:package_details) { graphql_data_at(:package) }
- let(:metadata_response) { graphql_data_at(:package, :metadata) }
- let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
- let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)}
- let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)}
-
let(:query) do
graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
#{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)}
@@ -46,35 +33,16 @@ RSpec.describe 'conan package details' do
subject
end
- it_behaves_like 'a working graphql query' do
- it 'matches the JSON schema' do
- expect(package_details).to match_schema('graphql/packages/package_details')
- end
- end
+ it_behaves_like 'a package detail'
+ it_behaves_like 'a package with files'
it 'has the correct metadata' do
expect(metadata_response).to include(
- 'id' => global_id_of(conan_package.conan_metadatum),
- 'recipe' => conan_package.conan_metadatum.recipe,
- 'packageChannel' => conan_package.conan_metadatum.package_channel,
- 'packageUsername' => conan_package.conan_metadatum.package_username,
- 'recipePath' => conan_package.conan_metadatum.recipe_path
- )
- end
-
- it 'has the right amount of files' do
- expect(package_files_response.length).to be(conan_package.package_files.length)
- end
-
- it 'has the basic package files data' do
- expect(first_file_response).to include(
- 'id' => global_id_of(first_file),
- 'fileName' => first_file.file_name,
- 'size' => first_file.size.to_s,
- 'downloadPath' => first_file.download_path,
- 'fileSha1' => first_file.file_sha1,
- 'fileMd5' => first_file.file_md5,
- 'fileSha256' => first_file.file_sha256
+ 'id' => global_id_of(package.conan_metadatum),
+ 'recipe' => package.conan_metadatum.recipe,
+ 'packageChannel' => package.conan_metadatum.package_channel,
+ 'packageUsername' => package.conan_metadatum.package_username,
+ 'recipePath' => package.conan_metadatum.recipe_path
)
end
diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb
index 8b6b5ea0986..d28d32b0df5 100644
--- a/spec/requests/api/graphql/packages/maven_spec.rb
+++ b/spec/requests/api/graphql/packages/maven_spec.rb
@@ -3,89 +3,51 @@ require 'spec_helper'
RSpec.describe 'maven package details' do
include GraphqlHelpers
+ include_context 'package details setup'
- let_it_be(:project) { create(:project) }
- let_it_be(:maven_package) { create(:maven_package, project: project) }
+ let_it_be(:package) { create(:maven_package, project: project) }
- let(:package_global_id) { global_id_of(maven_package) }
let(:metadata) { query_graphql_fragment('MavenMetadata') }
- let(:first_file) { maven_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } }
-
- let(:depth) { 3 }
- let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
- let(:package_files) { all_graphql_fields_for('PackageFile') }
-
- let(:user) { project.owner }
- let(:package_details) { graphql_data_at(:package) }
- let(:metadata_response) { graphql_data_at(:package, :metadata) }
- let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
- let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)}
-
- let(:query) do
- graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
- #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)}
- metadata {
- #{metadata}
- }
- packageFiles {
- nodes {
- #{package_files}
- }
- }
- FIELDS
- end
-
- subject { post_graphql(query, current_user: user) }
-
- shared_examples 'a working maven package' do
- before do
- subject
- end
-
- it_behaves_like 'a working graphql query' do
- it 'matches the JSON schema' do
- expect(package_details).to match_schema('graphql/packages/package_details')
- end
- end
+ shared_examples 'correct maven metadata' do
it 'has the correct metadata' do
expect(metadata_response).to include(
- 'id' => global_id_of(maven_package.maven_metadatum),
- 'path' => maven_package.maven_metadatum.path,
- 'appGroup' => maven_package.maven_metadatum.app_group,
- 'appVersion' => maven_package.maven_metadatum.app_version,
- 'appName' => maven_package.maven_metadatum.app_name
+ 'id' => global_id_of(package.maven_metadatum),
+ 'path' => package.maven_metadatum.path,
+ 'appGroup' => package.maven_metadatum.app_group,
+ 'appVersion' => package.maven_metadatum.app_version,
+ 'appName' => package.maven_metadatum.app_name
)
end
+ end
- it 'has the right amount of files' do
- expect(package_files_response.length).to be(maven_package.package_files.length)
- end
+ context 'a maven package with version' do
+ subject { post_graphql(query, current_user: user) }
- it 'has the basic package files data' do
- expect(first_file_response).to include(
- 'id' => global_id_of(first_file),
- 'fileName' => first_file.file_name,
- 'size' => first_file.size.to_s,
- 'downloadPath' => first_file.download_path,
- 'fileSha1' => first_file.file_sha1,
- 'fileMd5' => first_file.file_md5,
- 'fileSha256' => first_file.file_sha256
- )
+ before do
+ subject
end
- end
- context 'a maven package with version' do
- it_behaves_like "a working maven package"
+ it_behaves_like 'a package detail'
+ it_behaves_like 'correct maven metadata'
+ it_behaves_like 'a package with files'
end
context 'a versionless maven package' do
let_it_be(:maven_metadatum) { create(:maven_metadatum, app_version: nil) }
- let_it_be(:maven_package) { create(:maven_package, project: project, version: nil, maven_metadatum: maven_metadatum) }
+ let_it_be(:package) { create(:maven_package, project: project, version: nil, maven_metadatum: maven_metadatum) }
+
+ subject { post_graphql(query, current_user: user) }
+
+ before do
+ subject
+ end
- it_behaves_like "a working maven package"
+ it_behaves_like 'a package detail'
+ it_behaves_like 'correct maven metadata'
+ it_behaves_like 'a package with files'
- it "has an empty version" do
+ it 'has an empty version' do
subject
expect(metadata_response['appVersion']).to eq(nil)
diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb
index fa9d8a0e37e..1de16009684 100644
--- a/spec/requests/api/graphql/packages/nuget_spec.rb
+++ b/spec/requests/api/graphql/packages/nuget_spec.rb
@@ -3,37 +3,11 @@ require 'spec_helper'
RSpec.describe 'nuget package details' do
include GraphqlHelpers
+ include_context 'package details setup'
- let_it_be(:project) { create(:project) }
- let_it_be(:nuget_package) { create(:nuget_package, :with_metadatum, project: project) }
+ let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) }
- let(:package_global_id) { global_id_of(nuget_package) }
let(:metadata) { query_graphql_fragment('NugetMetadata') }
- let(:first_file) { nuget_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } }
-
- let(:depth) { 3 }
- let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
- let(:package_files) { all_graphql_fields_for('PackageFile') }
-
- let(:user) { project.owner }
- let(:package_details) { graphql_data_at(:package) }
- let(:metadata_response) { graphql_data_at(:package, :metadata) }
- let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
- let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)}
-
- let(:query) do
- graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
- #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)}
- metadata {
- #{metadata}
- }
- packageFiles {
- nodes {
- #{package_files}
- }
- }
- FIELDS
- end
subject { post_graphql(query, current_user: user) }
@@ -41,34 +15,15 @@ RSpec.describe 'nuget package details' do
subject
end
- it_behaves_like 'a working graphql query' do
- it 'matches the JSON schema' do
- expect(package_details).to match_schema('graphql/packages/package_details')
- end
- end
+ it_behaves_like 'a package detail'
+ it_behaves_like 'a package with files'
it 'has the correct metadata' do
expect(metadata_response).to include(
- 'id' => global_id_of(nuget_package.nuget_metadatum),
- 'licenseUrl' => nuget_package.nuget_metadatum.license_url,
- 'projectUrl' => nuget_package.nuget_metadatum.project_url,
- 'iconUrl' => nuget_package.nuget_metadatum.icon_url
- )
- end
-
- it 'has the right amount of files' do
- expect(package_files_response.length).to be(nuget_package.package_files.length)
- end
-
- it 'has the basic package files data' do
- expect(first_file_response).to include(
- 'id' => global_id_of(first_file),
- 'fileName' => first_file.file_name,
- 'size' => first_file.size.to_s,
- 'downloadPath' => first_file.download_path,
- 'fileSha1' => first_file.file_sha1,
- 'fileMd5' => first_file.file_md5,
- 'fileSha256' => first_file.file_sha256
+ 'id' => global_id_of(package.nuget_metadatum),
+ 'licenseUrl' => package.nuget_metadatum.license_url,
+ 'projectUrl' => package.nuget_metadatum.project_url,
+ 'iconUrl' => package.nuget_metadatum.icon_url
)
end
end
diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb
new file mode 100644
index 00000000000..64fe7d29a7a
--- /dev/null
+++ b/spec/requests/api/graphql/packages/pypi_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'pypi package details' do
+ include GraphqlHelpers
+ include_context 'package details setup'
+
+ let_it_be(:package) { create(:pypi_package, project: project) }
+
+ let(:metadata) { query_graphql_fragment('PypiMetadata') }
+
+ subject { post_graphql(query, current_user: user) }
+
+ before do
+ subject
+ end
+
+ it_behaves_like 'a package detail'
+ it_behaves_like 'a package with files'
+
+ it 'has the correct metadata' do
+ expect(metadata_response).to include(
+ 'id' => global_id_of(package.pypi_metadatum),
+ 'requiredPython' => package.pypi_metadatum.required_python
+ )
+ 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 4dfc242da80..af462c4a639 100644
--- a/spec/requests/api/graphql/project/base_service_spec.rb
+++ b/spec/requests/api/graphql/project/base_service_spec.rb
@@ -8,7 +8,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(:bugzilla_service) { create(:bugzilla_service, project: project) }
+ let_it_be(:bugzilla_integration) { create(:bugzilla_integration, project: project) }
let_it_be(:redmine_service) { create(:redmine_service, project: project) }
let(:query) do
diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb
index c08bb8dc0a0..466464f600c 100644
--- a/spec/requests/api/graphql/project/project_members_spec.rb
+++ b/spec/requests/api/graphql/project/project_members_spec.rb
@@ -50,6 +50,20 @@ RSpec.describe 'getting project members information' do
invited_group.add_guest(invited_user)
end
+ context 'when a member is invited only via email and current_user is a maintainer' do
+ before do
+ parent_project.add_maintainer(user)
+ create(:project_member, :invited, source: parent_project)
+ end
+
+ it 'returns null in the user field' do
+ fetch_members(project: parent_project, args: { relations: [:DIRECT] })
+
+ expect(graphql_errors).to be_nil
+ expect(graphql_data_at(:project, :project_members, :edges, :node)).to contain_exactly({ 'user' => { 'id' => global_id_of(user) } }, 'user' => nil)
+ end
+ end
+
it 'returns direct members' do
fetch_members(project: child_project, args: { relations: [:DIRECT] })
diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb
index 43732c2ed18..8ccdb955ed9 100644
--- a/spec/requests/api/graphql/project/releases_spec.rb
+++ b/spec/requests/api/graphql/project/releases_spec.rb
@@ -295,75 +295,69 @@ RSpec.describe 'Query.project(fullPath).releases()' do
end
end
- describe 'sorting behavior' do
- let_it_be(:today) { Time.now }
- let_it_be(:yesterday) { today - 1.day }
- let_it_be(:tomorrow) { today + 1.day }
+ describe 'sorting and pagination' do
+ let_it_be(:sort_project) { create(:project, :public) }
- let_it_be(:project) { create(:project, :repository, :public) }
+ let(:data_path) { [:project, :releases] }
+ let(:current_user) { developer }
- let_it_be(:release_v1) { create(:release, project: project, tag: 'v1', released_at: yesterday, created_at: tomorrow) }
- let_it_be(:release_v2) { create(:release, project: project, tag: 'v2', released_at: today, created_at: yesterday) }
- let_it_be(:release_v3) { create(:release, project: project, tag: 'v3', released_at: tomorrow, created_at: today) }
-
- let(:current_user) { developer }
-
- let(:params) { nil }
-
- let(:sorted_tags) do
- graphql_data.dig('project', 'releases', 'nodes').map { |release| release['tagName'] }
- end
-
- let(:query) do
- graphql_query_for(:project, { fullPath: project.full_path },
- %{
- releases#{params ? "(#{params})" : ""} {
- nodes {
- tagName
- }
- }
- })
- end
-
- before do
- post_query
+ def pagination_query(params)
+ graphql_query_for(
+ :project,
+ { full_path: sort_project.full_path },
+ query_graphql_field(:releases, params, "#{page_info} nodes { tagName }")
+ )
end
- context 'when no sort: parameter is provided' do
- it 'returns the results with the default sort applied (sort: RELEASED_AT_DESC)' do
- expect(sorted_tags).to eq(%w(v3 v2 v1))
- end
+ def pagination_results_data(nodes)
+ nodes.map { |release| release['tagName'] }
end
- context 'with sort: RELEASED_AT_DESC' do
- let(:params) { 'sort: RELEASED_AT_DESC' }
-
- it 'returns the releases ordered by released_at in descending order' do
- expect(sorted_tags).to eq(%w(v3 v2 v1))
+ context 'when sorting by released_at' do
+ let_it_be(:release5) { create(:release, project: sort_project, tag: 'v5.5.0', released_at: 3.days.from_now) }
+ let_it_be(:release1) { create(:release, project: sort_project, tag: 'v5.1.0', released_at: 3.days.ago) }
+ let_it_be(:release4) { create(:release, project: sort_project, tag: 'v5.4.0', released_at: 2.days.from_now) }
+ let_it_be(:release2) { create(:release, project: sort_project, tag: 'v5.2.0', released_at: 2.days.ago) }
+ let_it_be(:release3) { create(:release, project: sort_project, tag: 'v5.3.0', released_at: 1.day.ago) }
+
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :RELEASED_AT_ASC }
+ let(:first_param) { 2 }
+ let(:expected_results) { [release1.tag, release2.tag, release3.tag, release4.tag, release5.tag] }
+ end
end
- end
- context 'with sort: RELEASED_AT_ASC' do
- let(:params) { 'sort: RELEASED_AT_ASC' }
-
- it 'returns the releases ordered by released_at in ascending order' do
- expect(sorted_tags).to eq(%w(v1 v2 v3))
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :RELEASED_AT_DESC }
+ let(:first_param) { 2 }
+ let(:expected_results) { [release5.tag, release4.tag, release3.tag, release2.tag, release1.tag] }
+ end
end
end
- context 'with sort: CREATED_DESC' do
- let(:params) { 'sort: CREATED_DESC' }
-
- it 'returns the releases ordered by created_at in descending order' do
- expect(sorted_tags).to eq(%w(v1 v3 v2))
+ context 'when sorting by created_at' do
+ let_it_be(:release5) { create(:release, project: sort_project, tag: 'v5.5.0', created_at: 3.days.from_now) }
+ let_it_be(:release1) { create(:release, project: sort_project, tag: 'v5.1.0', created_at: 3.days.ago) }
+ let_it_be(:release4) { create(:release, project: sort_project, tag: 'v5.4.0', created_at: 2.days.from_now) }
+ let_it_be(:release2) { create(:release, project: sort_project, tag: 'v5.2.0', created_at: 2.days.ago) }
+ let_it_be(:release3) { create(:release, project: sort_project, tag: 'v5.3.0', created_at: 1.day.ago) }
+
+ context 'when ascending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :CREATED_ASC }
+ let(:first_param) { 2 }
+ let(:expected_results) { [release1.tag, release2.tag, release3.tag, release4.tag, release5.tag] }
+ end
end
- end
-
- context 'with sort: CREATED_ASC' do
- let(:params) { 'sort: CREATED_ASC' }
- it 'returns the releases ordered by created_at in ascending order' do
- expect(sorted_tags).to eq(%w(v2 v3 v1))
+ context 'when descending' do
+ it_behaves_like 'sorted paginated query' do
+ let(:sort_param) { :CREATED_DESC }
+ let(:first_param) { 2 }
+ let(:expected_results) { [release5.tag, release4.tag, release3.tag, release2.tag, release1.tag] }
+ end
end
end
end
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index b367bbaaf43..54375d4de1d 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe 'getting project information' do
end
it 'includes topics array' do
- project.update!(tag_list: 'topic1, topic2, topic3')
+ project.update!(topic_list: 'topic1, topic2, topic3')
post_graphql(query, current_user: current_user)
@@ -119,6 +119,29 @@ RSpec.describe 'getting project information' do
end
end
+ context 'when the user has reporter access to the project' do
+ let(:statistics_query) do
+ <<~GRAPHQL
+ {
+ project(fullPath: "#{project.full_path}") {
+ statistics { wikiSize }
+ }
+ }
+ GRAPHQL
+ end
+
+ before do
+ project.add_reporter(current_user)
+ create(:project_statistics, project: project, wiki_size: 100)
+ end
+
+ it 'allows fetching project statistics' do
+ post_graphql(statistics_query, current_user: current_user)
+
+ expect(graphql_data.dig('project', 'statistics')).to include('wikiSize' => 100.0)
+ end
+ end
+
context 'when the user does not have access to the project' do
it 'returns an empty field' do
post_graphql(query, current_user: current_user)
diff --git a/spec/requests/api/group_avatar_spec.rb b/spec/requests/api/group_avatar_spec.rb
new file mode 100644
index 00000000000..be5cfbc234c
--- /dev/null
+++ b/spec/requests/api/group_avatar_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::GroupAvatar do
+ def avatar_path(group)
+ "/groups/#{group.id}/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)
+
+ get api(avatar_path(group))
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ context 'when the group does not have avatar' do
+ it 'returns :not_found' do
+ group = create(:group, :public)
+
+ get api(avatar_path(group))
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when the group is private' do
+ let(:group) { create(:group, :private, :with_avatar) }
+
+ context 'when the user is not authenticated' do
+ it 'returns :not_found' do
+ get api(avatar_path(group))
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when the the group user is authenticated' do
+ context 'and have access to the group' do
+ it 'retrieves the avatar successfully' do
+ owner = create(:user)
+ group.add_owner(owner)
+
+ get api(avatar_path(group), owner)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'and does not have access to the group' do
+ it 'returns :not_found' do
+ get api(avatar_path(group), create(:user))
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/group_container_repositories_spec.rb b/spec/requests/api/group_container_repositories_spec.rb
index 4584ef37bd0..fdbf910e4bc 100644
--- a/spec/requests/api/group_container_repositories_spec.rb
+++ b/spec/requests/api/group_container_repositories_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe API::GroupContainerRepositories do
describe 'GET /groups/:id/registry/repositories' do
let(:url) { "/groups/#{group.id}/registry/repositories" }
+ let(:snowplow_gitlab_standard_context) { { user: api_user, namespace: group } }
subject { get api(url, api_user) }
diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb
index 8309e2ba7c1..31eef21654a 100644
--- a/spec/requests/api/group_export_spec.rb
+++ b/spec/requests/api/group_export_spec.rb
@@ -64,6 +64,23 @@ RSpec.describe API::GroupExport do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'when object is not present' do
+ let(:other_group) { create(:group, :with_export) }
+ let(:other_download_path) { "/groups/#{other_group.id}/export/download" }
+
+ before do
+ other_group.add_owner(user)
+ other_group.export_file.file.delete
+ end
+
+ it 'returns 404' do
+ get api(other_download_path, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('The group export file is not available yet')
+ end
+ end
end
context 'when export file does not exist' do
@@ -215,7 +232,7 @@ RSpec.describe API::GroupExport do
context 'when export file exists' do
it 'downloads exported group archive' do
- upload.update!(export_file: fixture_file_upload('spec/fixtures/bulk_imports/labels.ndjson.gz'))
+ upload.update!(export_file: fixture_file_upload('spec/fixtures/bulk_imports/gz/labels.ndjson.gz'))
get api(download_path, user)
diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb
index 900ffe6dfc7..c677e68b285 100644
--- a/spec/requests/api/group_labels_spec.rb
+++ b/spec/requests/api/group_labels_spec.rb
@@ -290,7 +290,7 @@ RSpec.describe API::GroupLabels do
put api("/groups/#{group.id}/labels", user), params: { name: group_label1.name }
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq('new_name, color, description, remove_on_close are missing, '\
+ expect(json_response['error']).to eq('new_name, color, description are missing, '\
'at least one parameter must be provided')
end
end
@@ -337,7 +337,7 @@ RSpec.describe API::GroupLabels do
put api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}", user)
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq('new_name, color, description, remove_on_close are missing, '\
+ expect(json_response['error']).to eq('new_name, color, description are missing, '\
'at least one parameter must be provided')
end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 1c359b6e50f..0a47b93773b 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -506,7 +506,7 @@ RSpec.describe API::Groups do
end
it "does not return a non existing group" do
- get api("/groups/1328", user1)
+ get api("/groups/#{non_existing_record_id}", user1)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -586,7 +586,7 @@ RSpec.describe API::Groups do
end
it "does not return a non existing group" do
- get api("/groups/1328", admin)
+ get api("/groups/#{non_existing_record_id}", admin)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -631,30 +631,11 @@ RSpec.describe API::Groups do
end
end
- context 'when limiting feature is enabled' do
- before do
- stub_feature_flags(limit_projects_in_groups_api: true)
- end
-
- it 'limits projects and shared_projects' do
- get api("/groups/#{group1.id}")
-
- expect(json_response['projects'].count).to eq(limit)
- expect(json_response['shared_projects'].count).to eq(limit)
- end
- end
-
- context 'when limiting feature is not enabled' do
- before do
- stub_feature_flags(limit_projects_in_groups_api: false)
- end
-
- it 'does not limit projects and shared_projects' do
- get api("/groups/#{group1.id}")
+ it 'limits projects and shared_projects' do
+ get api("/groups/#{group1.id}")
- expect(json_response['projects'].count).to eq(3)
- expect(json_response['shared_projects'].count).to eq(3)
- end
+ expect(json_response['projects'].count).to eq(limit)
+ expect(json_response['shared_projects'].count).to eq(limit)
end
end
end
@@ -748,7 +729,7 @@ RSpec.describe API::Groups do
end
it 'returns 404 for a non existing group' do
- put api('/groups/1328', user1), params: { name: new_group_name }
+ put api("/groups/#{non_existing_record_id}", user1), params: { name: new_group_name }
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -973,7 +954,7 @@ RSpec.describe API::Groups do
end
it "does not return a non existing group" do
- get api("/groups/1328/projects", user1)
+ get api("/groups/#{non_existing_record_id}/projects", user1)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -1027,7 +1008,7 @@ RSpec.describe API::Groups do
end
it "does not return a non existing group" do
- get api("/groups/1328/projects", admin)
+ get api("/groups/#{non_existing_record_id}/projects", admin)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -1686,7 +1667,7 @@ RSpec.describe API::Groups do
end
it "does not remove a non existing group" do
- delete api("/groups/1328", user1)
+ delete api("/groups/#{non_existing_record_id}", user1)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -1706,7 +1687,7 @@ RSpec.describe API::Groups do
end
it "does not remove a non existing group" do
- delete api("/groups/1328", admin)
+ delete api("/groups/#{non_existing_record_id}", admin)
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/requests/api/helm_packages_spec.rb b/spec/requests/api/helm_packages_spec.rb
new file mode 100644
index 00000000000..5871c0a5d5b
--- /dev/null
+++ b/spec/requests/api/helm_packages_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe API::HelmPackages do
+ include_context 'helm api setup'
+
+ using RSpec::Parameterized::TableSyntax
+
+ 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) }
+
+ 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 }
+
+ let(:url) { "/projects/#{project.id}/packages/helm/#{channel}/charts/#{package.name}-#{package.version}.tgz" }
+
+ subject { get api(url) }
+
+ 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
+ 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(: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]
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects helm access with unknown project id'
+ end
+end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 6bedd43e5c4..631698554f9 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -341,9 +341,9 @@ RSpec.describe API::Internal::Base do
end
describe "GET /internal/authorized_keys" do
- context "using an existing key's fingerprint" do
+ context "using an existing key" do
it "finds the key" do
- get(api('/internal/authorized_keys'), params: { fingerprint: key.fingerprint, secret_token: secret_token })
+ get(api('/internal/authorized_keys'), params: { key: key.key.split[1], secret_token: secret_token })
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(key.id)
@@ -351,58 +351,23 @@ RSpec.describe API::Internal::Base do
end
it 'exposes the comment of the key as a simple identifier of username + hostname' do
- get(api('/internal/authorized_keys'), params: { fingerprint: key.fingerprint, secret_token: secret_token })
+ get(api('/internal/authorized_keys'), params: { key: key.key.split[1], secret_token: secret_token })
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['key']).to include("#{key.user_name} (#{Gitlab.config.gitlab.host})")
end
end
- context "non existing key's fingerprint" do
- it "returns 404" do
- get(api('/internal/authorized_keys'), params: { fingerprint: "no:t-:va:li:d0", secret_token: secret_token })
+ it "returns 404 with a partial key" do
+ get(api('/internal/authorized_keys'), params: { key: key.key.split[1][0...-3], secret_token: secret_token })
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ expect(response).to have_gitlab_http_status(:not_found)
end
- context "using a partial fingerprint" do
- it "returns 404" do
- get(api('/internal/authorized_keys'), params: { fingerprint: "#{key.fingerprint[0..5]}%", secret_token: secret_token })
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context "sending the key" do
- context "using an existing key" do
- it "finds the key" do
- get(api('/internal/authorized_keys'), params: { key: key.key.split[1], secret_token: secret_token })
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['id']).to eq(key.id)
- expect(json_response['key'].split[1]).to eq(key.key.split[1])
- end
-
- it 'exposes the comment of the key as a simple identifier of username + hostname' do
- get(api('/internal/authorized_keys'), params: { fingerprint: key.fingerprint, secret_token: secret_token })
+ it "returns 404 with an not valid base64 string" do
+ get(api('/internal/authorized_keys'), params: { key: "whatever!", secret_token: secret_token })
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['key']).to include("#{key.user_name} (#{Gitlab.config.gitlab.host})")
- end
- end
-
- it "returns 404 with a partial key" do
- get(api('/internal/authorized_keys'), params: { key: key.key.split[1][0...-3], secret_token: secret_token })
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
-
- it "returns 404 with an not valid base64 string" do
- get(api('/internal/authorized_keys'), params: { key: "whatever!", secret_token: secret_token })
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb
index b0e54055854..f9f03c9e55c 100644
--- a/spec/requests/api/invitations_spec.rb
+++ b/spec/requests/api/invitations_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe API::Invitations do
context 'and new member is already a requester' do
it 'does not transform the requester into a proper member' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: access_requester.email, access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:created)
@@ -71,7 +71,7 @@ RSpec.describe API::Invitations do
it 'invites a new member' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: email, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
@@ -82,7 +82,7 @@ RSpec.describe API::Invitations do
expect do
email_list = [email, email2].join(',')
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: email_list, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
@@ -98,7 +98,7 @@ RSpec.describe API::Invitations do
project.update!(group: group)
parent.add_developer(stranger)
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: stranger.email, access_level: Member::REPORTER }
expect(response).to have_gitlab_http_status(:created)
@@ -113,7 +113,7 @@ RSpec.describe API::Invitations do
project.update!(group: group)
parent.add_developer(stranger)
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: stranger.email, access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:created)
@@ -122,7 +122,7 @@ RSpec.describe API::Invitations do
context 'access expiry date' do
subject do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: email, access_level: Member::DEVELOPER, expires_at: expires_at }
end
@@ -152,8 +152,36 @@ RSpec.describe API::Invitations do
end
end
+ context 'with invite_source considerations', :snowplow do
+ let(:params) { { email: email, access_level: Member::DEVELOPER } }
+
+ it 'tracks the invite source as api' do
+ post invitations_url(source, maintainer), params: params
+
+ expect_snowplow_event(
+ category: 'Members::InviteService',
+ action: 'create_member',
+ label: 'invitations-api',
+ property: 'net_new_user',
+ user: maintainer
+ )
+ end
+
+ it 'tracks the invite source from params' do
+ post invitations_url(source, maintainer), params: params.merge(invite_source: '_invite_source_')
+
+ expect_snowplow_event(
+ category: 'Members::InviteService',
+ action: 'create_member',
+ label: '_invite_source_',
+ property: 'net_new_user',
+ user: maintainer
+ )
+ end
+ end
+
it "returns a message if member already exists" do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: developer.email, access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:created)
@@ -161,7 +189,7 @@ RSpec.describe API::Invitations do
end
it 'returns 404 when the email is not valid' do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: '', access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:created)
@@ -169,7 +197,7 @@ RSpec.describe API::Invitations do
end
it 'returns 404 when the email list is not a valid format' do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: 'email1@example.com,not-an-email', access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -177,14 +205,14 @@ RSpec.describe API::Invitations do
end
it 'returns 400 when email is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'returns 400 when access_level is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer),
+ post invitations_url(source, maintainer),
params: { email: email }
expect(response).to have_gitlab_http_status(:bad_request)
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb
index 38c080059c4..dac721cbea0 100644
--- a/spec/requests/api/issues/put_projects_issues_spec.rb
+++ b/spec/requests/api/issues/put_projects_issues_spec.rb
@@ -402,17 +402,6 @@ RSpec.describe API::Issues do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['state']).to eq 'opened'
end
-
- it 'removes labels marked to be removed on issue closed' do
- removable_label = create(:label, project: project, remove_on_close: true)
- create(:label_link, target: issue, label: removable_label)
-
- put api_for_user, params: { state_event: 'close' }
-
- expect(issue.reload.label_ids).not_to include(removable_label.id)
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['state']).to eq 'closed'
- end
end
describe 'PUT /projects/:id/issues/:issue_iid to update updated_at param' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index f2ceedf6dbd..26377c40b73 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe API::Labels do
put_labels_api(route_type, user, spec_params)
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq('new_name, color, description, priority, remove_on_close are missing, '\
+ expect(json_response['error']).to eq('new_name, color, description, priority are missing, '\
'at least one parameter must be provided')
end
@@ -112,14 +112,6 @@ RSpec.describe API::Labels do
expect(json_response['id']).to eq(expected_response_label_id)
expect(json_response['priority']).to eq(10)
end
-
- it "returns 200 if remove_on_close is changed (#{route_type} route)" do
- put_labels_api(route_type, user, spec_params, remove_on_close: true)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['id']).to eq(expected_response_label_id)
- expect(json_response['remove_on_close']).to eq(true)
- end
end
it 'returns 200 if a priority is removed (deprecated route)' do
@@ -309,8 +301,7 @@ RSpec.describe API::Labels do
name: valid_label_title_2,
color: '#FFAABB',
description: 'test',
- priority: 2,
- remove_on_close: true
+ priority: 2
}
expect(response).to have_gitlab_http_status(:created)
@@ -318,7 +309,6 @@ RSpec.describe API::Labels do
expect(json_response['color']).to eq('#FFAABB')
expect(json_response['description']).to eq('test')
expect(json_response['priority']).to eq(2)
- expect(json_response['remove_on_close']).to eq(true)
end
it 'returns created label when only required params' do
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index 4fc5fcf8282..d9f11b19e6e 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe API::MavenPackages do
include_context 'workhorse headers'
let_it_be_with_refind(:package_settings) { create(:namespace_package_setting, :group) }
- let_it_be(:group) { package_settings.namespace }
+ let_it_be_with_refind(:group) { package_settings.namespace }
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :public, namespace: group) }
let_it_be(:package, reload: true) { create(:maven_package, project: project, name: project.full_path) }
@@ -15,12 +15,13 @@ RSpec.describe API::MavenPackages do
let_it_be(:package_file) { package.package_files.with_file_name_like('%.xml').first }
let_it_be(:jar_file) { package.package_files.with_file_name_like('%.jar').first }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
- let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) }
+ let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) }
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(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) }
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
let(:package_name) { 'com/example/my-app' }
let(:headers) { workhorse_headers }
let(:headers_with_token) { headers.merge('Private-Token' => personal_access_token.token) }
@@ -39,22 +40,73 @@ RSpec.describe API::MavenPackages do
project.add_developer(user)
end
+ shared_examples 'handling groups and subgroups for' do |shared_example_name, visibilities: %i[public]|
+ context 'within a group' do
+ visibilities.each do |visibility|
+ context "that is #{visibility}" do
+ before do
+ group.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
+ end
+
+ it_behaves_like shared_example_name
+ end
+ end
+ end
+
+ context 'within a subgroup' do
+ let_it_be_with_reload(:subgroup) { create(:group, parent: group) }
+
+ before do
+ move_project_to_namespace(subgroup)
+ end
+
+ visibilities.each do |visibility|
+ context "that is #{visibility}" do
+ before do
+ subgroup.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
+ group.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
+ end
+
+ it_behaves_like shared_example_name
+ end
+ end
+ end
+ end
+
+ shared_examples 'handling groups, subgroups and user namespaces for' do |shared_example_name, visibilities: %i[public]|
+ it_behaves_like 'handling groups and subgroups for', shared_example_name, visibilities: visibilities
+
+ context 'within a user namespace' do
+ before do
+ move_project_to_namespace(user.namespace)
+ end
+
+ visibilities.each do |visibility|
+ context "that is #{visibility}" do
+ before do
+ user.namespace.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
+ end
+
+ it_behaves_like shared_example_name
+ end
+ end
+ end
+ end
+
shared_examples 'tracking the file download event' do
context 'with jar file' do
let_it_be(:package_file) { jar_file }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
end
end
shared_examples 'rejecting the request for non existing maven path' do |expected_status: :not_found|
- before do
- if Feature.enabled?(:check_maven_path_first, default_enabled: :yaml)
- expect(::Packages::Maven::PackageFinder).not_to receive(:new)
- end
- end
-
it 'rejects the request' do
+ expect(::Packages::Maven::PackageFinder).not_to receive(:new)
+
subject
expect(response).to have_gitlab_http_status(expected_status)
@@ -166,10 +218,10 @@ RSpec.describe API::MavenPackages do
end
describe 'GET /api/v4/packages/maven/*path/:file_name' do
- shared_examples 'handling all conditions' do
- context 'a public project' do
- subject { download_file(file_name: package_file.file_name) }
+ context 'a public project' do
+ subject { download_file(file_name: package_file.file_name) }
+ shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
it 'returns the file' do
@@ -194,14 +246,18 @@ RSpec.describe API::MavenPackages do
end
end
- context 'internal project' do
- before do
- project.team.truncate
- project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
- end
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file'
+ end
- subject { download_file_with_token(file_name: package_file.file_name) }
+ context 'internal project' do
+ before do
+ project.team.truncate
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ end
+ subject { download_file_with_token(file_name: package_file.file_name) }
+
+ shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
it 'returns the file' do
@@ -228,13 +284,17 @@ RSpec.describe API::MavenPackages do
end
end
- context 'private project' do
- subject { download_file_with_token(file_name: package_file.file_name) }
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: %i[public internal]
+ end
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ context 'private project' do
+ subject { download_file_with_token(file_name: package_file.file_name) }
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
it 'returns the file' do
@@ -245,11 +305,13 @@ RSpec.describe API::MavenPackages do
end
it 'denies download when not enough permissions' do
- project.add_guest(user)
+ unless project.root_namespace == user.namespace
+ project.add_guest(user)
- subject
+ subject
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
it 'denies download when no private token' do
@@ -286,33 +348,19 @@ RSpec.describe API::MavenPackages do
end
end
- context 'project name is different from a package name' do
- before do
- maven_metadatum.update!(path: "wrong_name/#{package.version}")
- end
-
- it 'rejects request' do
- download_file(file_name: package_file.file_name)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: %i[public internal private]
end
- context 'with check_maven_path_first enabled' do
+ context 'project name is different from a package name' do
before do
- stub_feature_flags(check_maven_path_first: true)
+ maven_metadatum.update!(path: "wrong_name/#{package.version}")
end
- it_behaves_like 'handling all conditions'
- end
+ it 'rejects request' do
+ download_file(file_name: package_file.file_name)
- context 'with check_maven_path_first disabled' do
- before do
- stub_feature_flags(check_maven_path_first: false)
+ expect(response).to have_gitlab_http_status(:forbidden)
end
-
- it_behaves_like 'handling all conditions'
end
def download_file(file_name:, params: {}, request_headers: headers, path: maven_metadatum.path)
@@ -328,14 +376,16 @@ RSpec.describe API::MavenPackages do
let(:path) { package.maven_metadatum.path }
let(:url) { "/packages/maven/#{path}/#{package_file.file_name}" }
- it_behaves_like 'processing HEAD requests', instance_level: true
+ shared_examples 'heading a file' do
+ it_behaves_like 'processing HEAD requests', instance_level: true
+ end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)
end
- it_behaves_like 'processing HEAD requests', instance_level: true
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'heading a file'
end
context 'with check_maven_path_first disabled' do
@@ -343,7 +393,7 @@ RSpec.describe API::MavenPackages do
stub_feature_flags(check_maven_path_first: false)
end
- it_behaves_like 'processing HEAD requests', instance_level: true
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'heading a file'
end
end
@@ -353,10 +403,10 @@ RSpec.describe API::MavenPackages do
group.add_developer(user)
end
- shared_examples 'handling all conditions' do
- context 'a public project' do
- subject { download_file(file_name: package_file.file_name) }
+ context 'a public project' do
+ subject { download_file(file_name: package_file.file_name) }
+ shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
it 'returns the file' do
@@ -381,14 +431,18 @@ RSpec.describe API::MavenPackages do
end
end
- context 'internal project' do
- before do
- group.group_member(user).destroy!
- project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
- end
+ it_behaves_like 'handling groups and subgroups for', 'getting a file for a group'
+ end
+
+ context 'internal project' do
+ before do
+ group.group_member(user).destroy!
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ end
- subject { download_file_with_token(file_name: package_file.file_name) }
+ subject { download_file_with_token(file_name: package_file.file_name) }
+ shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
it 'returns the file' do
@@ -415,13 +469,17 @@ RSpec.describe API::MavenPackages do
end
end
- context 'private project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: %i[internal public]
+ end
- subject { download_file_with_token(file_name: package_file.file_name) }
+ context 'private project' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ subject { download_file_with_token(file_name: package_file.file_name) }
+ shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
it 'returns the file' do
@@ -480,101 +538,87 @@ RSpec.describe API::MavenPackages do
it_behaves_like 'rejecting the request for non existing maven path'
end
end
+ end
- context 'with a reporter from a subgroup accessing the root group' do
- let_it_be(:root_group) { create(:group, :private) }
- let_it_be(:group) { create(:group, :private, parent: root_group) }
+ it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: %i[private internal public]
- subject { download_file_with_token(file_name: package_file.file_name, request_headers: headers_with_token, group_id: root_group.id) }
+ context 'with a reporter from a subgroup accessing the root group' do
+ let_it_be(:root_group) { create(:group, :private) }
+ let_it_be(:group) { create(:group, :private, parent: root_group) }
- before do
- project.update!(namespace: group)
- group.add_reporter(user)
- end
+ subject { download_file_with_token(file_name: package_file.file_name, request_headers: headers_with_token, group_id: root_group.id) }
- it 'returns the file' do
- subject
+ before do
+ project.update!(namespace: group)
+ group.add_reporter(user)
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/octet-stream')
- end
+ it 'returns the file' do
+ subject
- context 'with a non existing maven path' do
- subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3', request_headers: headers_with_token, group_id: root_group.id) }
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type).to eq('application/octet-stream')
+ end
- it_behaves_like 'rejecting the request for non existing maven path'
- end
+ context 'with a non existing maven path' do
+ subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3', request_headers: headers_with_token, group_id: root_group.id) }
+
+ it_behaves_like 'rejecting the request for non existing maven path'
end
end
+ end
- context 'maven metadata file' do
- let_it_be(:sub_group1) { create(:group, parent: group) }
- let_it_be(:sub_group2) { create(:group, parent: group) }
- let_it_be(:project1) { create(:project, :private, group: sub_group1) }
- let_it_be(:project2) { create(:project, :private, group: sub_group2) }
- let_it_be(:project3) { create(:project, :private, group: sub_group1) }
- let_it_be(:package_name) { 'foo' }
- let_it_be(:package1) { create(:maven_package, project: project1, name: package_name, version: nil) }
- let_it_be(:package_file1) { create(:package_file, :xml, package: package1, file_name: 'maven-metadata.xml') }
- let_it_be(:package2) { create(:maven_package, project: project2, name: package_name, version: nil) }
- let_it_be(:package_file2) { create(:package_file, :xml, package: package2, file_name: 'maven-metadata.xml') }
- let_it_be(:package3) { create(:maven_package, project: project3, name: package_name, version: nil) }
- let_it_be(:package_file3) { create(:package_file, :xml, package: package3, file_name: 'maven-metadata.xml') }
+ context 'maven metadata file' do
+ let_it_be(:sub_group1) { create(:group, parent: group) }
+ let_it_be(:sub_group2) { create(:group, parent: group) }
+ let_it_be(:project1) { create(:project, :private, group: sub_group1) }
+ let_it_be(:project2) { create(:project, :private, group: sub_group2) }
+ let_it_be(:project3) { create(:project, :private, group: sub_group1) }
+ let_it_be(:package_name) { 'foo' }
+ let_it_be(:package1) { create(:maven_package, project: project1, name: package_name, version: nil) }
+ let_it_be(:package_file1) { create(:package_file, :xml, package: package1, file_name: 'maven-metadata.xml') }
+ let_it_be(:package2) { create(:maven_package, project: project2, name: package_name, version: nil) }
+ let_it_be(:package_file2) { create(:package_file, :xml, package: package2, file_name: 'maven-metadata.xml') }
+ let_it_be(:package3) { create(:maven_package, project: project3, name: package_name, version: nil) }
+ let_it_be(:package_file3) { create(:package_file, :xml, package: package3, file_name: 'maven-metadata.xml') }
- let(:maven_metadatum) { package3.maven_metadatum }
+ let(:maven_metadatum) { package3.maven_metadatum }
- subject { download_file_with_token(file_name: package_file3.file_name) }
+ subject { download_file_with_token(file_name: package_file3.file_name) }
- before do
- sub_group1.add_developer(user)
- sub_group2.add_developer(user)
- # the package with the most recently published file should be returned
- create(:package_file, :xml, package: package2)
- end
+ before do
+ sub_group1.add_developer(user)
+ sub_group2.add_developer(user)
+ # the package with the most recently published file should be returned
+ create(:package_file, :xml, package: package2)
+ end
- context 'in multiple versionless packages' do
- it 'downloads the file' do
- expect(::Packages::PackageFileFinder)
- .to receive(:new).with(package2, 'maven-metadata.xml').and_call_original
+ context 'in multiple versionless packages' do
+ it 'downloads the file' do
+ expect(::Packages::PackageFileFinder)
+ .to receive(:new).with(package2, 'maven-metadata.xml').and_call_original
- subject
- end
+ subject
end
+ end
- context 'in multiple snapshot packages' do
- before do
- version = '1.0.0-SNAPSHOT'
- [package1, package2, package3].each do |pkg|
- pkg.update!(version: version)
-
- pkg.maven_metadatum.update!(path: "#{pkg.name}/#{pkg.version}")
- end
- end
-
- it 'downloads the file' do
- expect(::Packages::PackageFileFinder)
- .to receive(:new).with(package3, 'maven-metadata.xml').and_call_original
+ context 'in multiple snapshot packages' do
+ before do
+ version = '1.0.0-SNAPSHOT'
+ [package1, package2, package3].each do |pkg|
+ pkg.update!(version: version)
- subject
+ pkg.maven_metadatum.update!(path: "#{pkg.name}/#{pkg.version}")
end
end
- end
- end
- context 'with check_maven_path_first enabled' do
- before do
- stub_feature_flags(check_maven_path_first: true)
- end
-
- it_behaves_like 'handling all conditions'
- end
+ it 'downloads the file' do
+ expect(::Packages::PackageFileFinder)
+ .to receive(:new).with(package3, 'maven-metadata.xml').and_call_original
- context 'with check_maven_path_first disabled' do
- before do
- stub_feature_flags(check_maven_path_first: false)
+ subject
+ end
end
-
- it_behaves_like 'handling all conditions'
end
def download_file(file_name:, params: {}, request_headers: headers, path: maven_metadatum.path, group_id: group.id)
@@ -595,7 +639,7 @@ RSpec.describe API::MavenPackages do
stub_feature_flags(check_maven_path_first: true)
end
- it_behaves_like 'processing HEAD requests'
+ it_behaves_like 'handling groups and subgroups for', 'processing HEAD requests'
end
context 'with check_maven_path_first disabled' do
@@ -603,95 +647,77 @@ RSpec.describe API::MavenPackages do
stub_feature_flags(check_maven_path_first: false)
end
- it_behaves_like 'processing HEAD requests'
+ it_behaves_like 'handling groups and subgroups for', 'processing HEAD requests'
end
end
describe 'GET /api/v4/projects/:id/packages/maven/*path/:file_name' do
- shared_examples 'handling all conditions' do
- context 'a public project' do
- subject { download_file(file_name: package_file.file_name) }
-
- it_behaves_like 'tracking the file download event'
-
- it 'returns the file' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/octet-stream')
- end
+ context 'a public project' do
+ subject { download_file(file_name: package_file.file_name) }
- it 'returns sha1 of the file' do
- download_file(file_name: package_file.file_name + '.sha1')
+ it_behaves_like 'tracking the file download event'
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('text/plain')
- expect(response.body).to eq(package_file.file_sha1)
- end
-
- context 'with a non existing maven path' do
- subject { download_file(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
+ it 'returns the file' do
+ subject
- it_behaves_like 'rejecting the request for non existing maven path'
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type).to eq('application/octet-stream')
end
- context 'private project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ it 'returns sha1 of the file' do
+ download_file(file_name: package_file.file_name + '.sha1')
- subject { download_file_with_token(file_name: package_file.file_name) }
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type).to eq('text/plain')
+ expect(response.body).to eq(package_file.file_sha1)
+ end
- it_behaves_like 'tracking the file download event'
+ context 'with a non existing maven path' do
+ subject { download_file(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it 'returns the file' do
- subject
+ it_behaves_like 'rejecting the request for non existing maven path'
+ end
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/octet-stream')
- end
+ context 'private project' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
- it 'denies download when not enough permissions' do
- project.add_guest(user)
+ subject { download_file_with_token(file_name: package_file.file_name) }
- subject
+ it_behaves_like 'tracking the file download event'
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it 'returns the file' do
+ subject
- it 'denies download when no private token' do
- download_file(file_name: package_file.file_name)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type).to eq('application/octet-stream')
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it 'denies download when not enough permissions' do
+ project.add_guest(user)
- it_behaves_like 'downloads with a job token'
+ subject
- it_behaves_like 'downloads with a deploy token'
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
- context 'with a non existing maven path' do
- subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
+ it 'denies download when no private token' do
+ download_file(file_name: package_file.file_name)
- it_behaves_like 'rejecting the request for non existing maven path'
- end
+ expect(response).to have_gitlab_http_status(:not_found)
end
- end
- context 'with check_maven_path_first enabled' do
- before do
- stub_feature_flags(check_maven_path_first: true)
- end
+ it_behaves_like 'downloads with a job token'
- it_behaves_like 'handling all conditions'
- end
+ it_behaves_like 'downloads with a deploy token'
- context 'with check_maven_path_first disabled' do
- before do
- stub_feature_flags(check_maven_path_first: false)
- end
+ context 'with a non existing maven path' do
+ subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'handling all conditions'
+ it_behaves_like 'rejecting the request for non existing maven path'
+ end
end
def download_file(file_name:, params: {}, request_headers: headers, path: maven_metadatum.path)
@@ -1020,4 +1046,10 @@ RSpec.describe API::MavenPackages do
upload_file(params: params, request_headers: request_headers, file_extension: file_extension)
end
end
+
+ def move_project_to_namespace(namespace)
+ project.update!(namespace: namespace)
+ package.update!(name: project.full_path)
+ maven_metadatum.update!(path: "#{package.name}/#{package.version}")
+ end
end
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index d488aee0c10..cac1b95e854 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -255,11 +255,41 @@ RSpec.describe API::Members do
expect(json_response['access_level']).to eq(Member::DEVELOPER)
end
- describe 'executes the Members::CreateService for multiple user_ids' do
+ context 'with invite_source considerations', :snowplow do
+ let(:params) { { user_id: stranger.id, access_level: Member::DEVELOPER } }
+
+ it 'tracks the invite source as api' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ params: params
+
+ expect_snowplow_event(
+ category: 'Members::CreateService',
+ action: 'create_member',
+ label: 'members-api',
+ property: 'existing_user',
+ user: maintainer
+ )
+ end
+
+ it 'tracks the invite source from params' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ params: params.merge(invite_source: '_invite_source_')
+
+ expect_snowplow_event(
+ category: 'Members::CreateService',
+ action: 'create_member',
+ label: '_invite_source_',
+ property: 'existing_user',
+ user: maintainer
+ )
+ end
+ end
+
+ context 'when executing the Members::CreateService for multiple user_ids' do
+ let(:user_ids) { [stranger.id, access_requester.id].join(',') }
+
it 'returns success when it successfully create all members' do
expect do
- user_ids = [stranger.id, access_requester.id].join(',')
-
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
params: { user_id: user_ids, access_level: Member::DEVELOPER }
@@ -270,8 +300,6 @@ RSpec.describe API::Members do
it 'returns the error message if there was an error adding members to group' do
error_message = 'Unable to find User ID'
- user_ids = [stranger.id, access_requester.id].join(',')
-
allow_next_instance_of(::Members::CreateService) do |service|
expect(service).to receive(:execute).and_return({ status: :error, message: error_message })
end
@@ -283,6 +311,36 @@ RSpec.describe API::Members do
expect(json_response['status']).to eq('error')
expect(json_response['message']).to eq(error_message)
end
+
+ context 'with invite_source considerations', :snowplow do
+ let(:params) { { user_id: user_ids, access_level: Member::DEVELOPER } }
+
+ it 'tracks the invite source as api' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ params: params
+
+ expect_snowplow_event(
+ category: 'Members::CreateService',
+ action: 'create_member',
+ label: 'members-api',
+ property: 'existing_user',
+ user: maintainer
+ )
+ end
+
+ it 'tracks the invite source from params' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ params: params.merge(invite_source: '_invite_source_')
+
+ expect_snowplow_event(
+ category: 'Members::CreateService',
+ action: 'create_member',
+ label: '_invite_source_',
+ property: 'existing_user',
+ user: maintainer
+ )
+ end
+ end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index a13db1bb414..038c3bc552a 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1186,7 +1186,8 @@ RSpec.describe API::MergeRequests do
expect(json_response['downvotes']).to eq(1)
expect(json_response['source_project_id']).to eq(merge_request.source_project.id)
expect(json_response['target_project_id']).to eq(merge_request.target_project.id)
- expect(json_response['work_in_progress']).to be_falsy
+ 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_status']).to eq('can_be_merged')
expect(json_response['should_close_merge_request']).to be_falsy
@@ -1329,29 +1330,30 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:not_found)
end
- context 'Work in Progress' do
- let!(:merge_request_wip) do
+ context 'Draft' do
+ let!(:merge_request_draft) do
create(:merge_request,
author: user,
assignees: [user],
source_project: project,
target_project: project,
- title: "WIP: Test",
+ title: "Draft: Test",
created_at: base_time + 1.second
)
end
it "returns merge request" do
- get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.iid}", user)
+ get api("/projects/#{project.id}/merge_requests/#{merge_request_draft.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['draft']).to eq(true)
expect(json_response['work_in_progress']).to eq(true)
end
end
context 'when a merge request has more than the changes limit' do
it "returns a string indicating that more changes were made" do
- allow(Commit).to receive(:diff_hard_limit_files).and_return(5)
+ allow(Commit).to receive(:diff_max_files).and_return(5)
merge_request_overflow = create(:merge_request, :simple,
author: user,
@@ -2174,6 +2176,12 @@ RSpec.describe API::MergeRequests do
a_hash_including('name' => user2.name)
)
end
+
+ it 'creates appropriate system notes', :sidekiq_inline do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(merge_request.notes.system.last.note).to include("assigned to #{user2.to_reference}")
+ end
end
context 'when assignee_id=user2.id' do
@@ -2193,6 +2201,27 @@ RSpec.describe API::MergeRequests do
end
end
+ context 'when assignee_id=0' do
+ let(:params) do
+ {
+ assignee_id: 0
+ }
+ end
+
+ it 'clears the assignees' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['assignees']).to be_empty
+ end
+
+ it 'creates appropriate system notes', :sidekiq_inline do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(merge_request.notes.system.last.note).to include("unassigned #{user.to_reference}")
+ end
+ end
+
context 'when only assignee_ids are provided, and the list is empty' do
let(:params) do
{
@@ -2495,8 +2524,8 @@ RSpec.describe API::MergeRequests do
expect(json_response['message']).to eq('405 Method Not Allowed')
end
- it "returns 405 if merge_request is a work in progress" do
- merge_request.update_attribute(:title, "WIP: #{merge_request.title}")
+ it "returns 405 if merge_request is a draft" do
+ merge_request.update_attribute(:title, "Draft: #{merge_request.title}")
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
expect(response).to have_gitlab_http_status(:method_not_allowed)
expect(json_response['message']).to eq('405 Method Not Allowed')
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 10271719a15..ab74da4bda4 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -71,12 +71,14 @@ RSpec.describe API::NpmProjectPackages do
end
context 'a public project' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
context 'with a job token for a different user' do
let_it_be(:other_user) { create(:user) }
- let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) }
+ let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user, project: project) }
let(:headers) { build_token_auth_header(other_job.token) }
@@ -161,6 +163,7 @@ RSpec.describe API::NpmProjectPackages do
context 'valid package record' do
let(:params) { upload_params(package_name: package_name) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
shared_examples 'handling upload with different authentications' do
context 'with access token' do
diff --git a/spec/requests/api/nuget_group_packages_spec.rb b/spec/requests/api/nuget_group_packages_spec.rb
index aefbc89dc3b..1b71f0f9de1 100644
--- a/spec/requests/api/nuget_group_packages_spec.rb
+++ b/spec/requests/api/nuget_group_packages_spec.rb
@@ -46,6 +46,7 @@ RSpec.describe API::NugetGroupPackages do
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: subgroup) }
let(:target) { subgroup }
+ let(:snowplow_gitlab_standard_context) { { namespace: subgroup } }
it_behaves_like 'handling all endpoints'
@@ -57,6 +58,7 @@ RSpec.describe API::NugetGroupPackages do
context 'a group' do
let(:target) { group }
+ let(:snowplow_gitlab_standard_context) { { namespace: group } }
it_behaves_like 'handling all endpoints'
diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb
index 54fe0b985df..572736cfc86 100644
--- a/spec/requests/api/nuget_project_packages_spec.rb
+++ b/spec/requests/api/nuget_project_packages_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe API::NugetProjectPackages do
describe 'GET /api/v4/projects/:id/packages/nuget' do
it_behaves_like 'handling nuget service requests' do
let(:url) { "/projects/#{target.id}/packages/nuget/index.json" }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
end
end
@@ -34,6 +35,7 @@ RSpec.describe API::NugetProjectPackages do
describe 'GET /api/v4/projects/:id/packages/nuget/query' do
it_behaves_like 'handling nuget search requests' do
let(:url) { "/projects/#{target.id}/packages/nuget/query?#{query_parameters.to_query}" }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
end
end
@@ -121,6 +123,7 @@ RSpec.describe API::NugetProjectPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
subject { get api(url), headers: headers }
@@ -189,7 +192,7 @@ RSpec.describe API::NugetProjectPackages do
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) }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
end
it_behaves_like 'rejects nuget access with unknown target id'
@@ -244,6 +247,7 @@ RSpec.describe API::NugetProjectPackages 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))
@@ -256,7 +260,7 @@ RSpec.describe API::NugetProjectPackages do
it_behaves_like 'deploy token for package uploads'
it_behaves_like 'job token for package uploads' do
- let_it_be(:job) { create(:ci_build, :running, user: user) }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
end
it_behaves_like 'rejects nuget access with unknown target id'
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index d28442bd692..2932447f663 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -69,6 +69,7 @@ itself: # project
- shared_with_groups
- ssh_url_to_repo
- tag_list
+ - topics
- web_url
build_auto_devops: # auto_devops
@@ -86,13 +87,13 @@ ci_cd_settings:
- id
- project_id
- group_runners_enabled
- - keep_latest_artifact
- merge_pipelines_enabled
- merge_trains_enabled
- auto_rollback_enabled
remapped_attributes:
default_git_depth: ci_default_git_depth
forward_deployment_enabled: ci_forward_deployment_enabled
+ job_token_scope_enabled: ci_job_token_scope_enabled
build_import_state: # import_state
unexposed_attributes:
@@ -139,7 +140,6 @@ project_setting:
- project_id
- push_rule_id
- show_default_award_emojis
- - squash_option
- updated_at
- cve_id_request_enabled
- mr_default_target_self
diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb
index f3da99573fe..695d2c3fe2c 100644
--- a/spec/requests/api/project_container_repositories_spec.rb
+++ b/spec/requests/api/project_container_repositories_spec.rb
@@ -32,6 +32,8 @@ RSpec.describe API::ProjectContainerRepositories do
let(:method) { :get }
let(:params) { {} }
+ let(:snowplow_gitlab_standard_context) { { user: api_user, project: project, namespace: project.namespace } }
+
before_all do
project.add_maintainer(maintainer)
project.add_developer(developer)
@@ -405,7 +407,7 @@ RSpec.describe API::ProjectContainerRepositories do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect_snowplow_event(category: described_class.name, action: 'delete_tag')
+ expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, user: api_user, namespace: project.namespace)
end
end
@@ -421,7 +423,7 @@ RSpec.describe API::ProjectContainerRepositories do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect_snowplow_event(category: described_class.name, action: 'delete_tag')
+ expect_snowplow_event(category: described_class.name, action: 'delete_tag', project: project, user: api_user, namespace: project.namespace)
end
end
end
diff --git a/spec/requests/api/project_debian_distributions_spec.rb b/spec/requests/api/project_debian_distributions_spec.rb
new file mode 100644
index 00000000000..de7362758f7
--- /dev/null
+++ b/spec/requests/api/project_debian_distributions_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe API::ProjectDebianDistributions do
+ include HttpBasicAuthHelpers
+ include WorkhorseHelpers
+
+ include_context 'Debian repository shared context', :project, true do
+ describe 'POST projects/:id/debian_distributions' do
+ let(:method) { :post }
+ let(:url) { "/projects/#{container.id}/debian_distributions" }
+ let(:api_params) { { 'codename': 'my-codename' } }
+
+ it_behaves_like 'Debian repository write endpoint', 'POST distribution request', :created, /^{.*"codename":"my-codename",.*"components":\["main"\],.*"architectures":\["all","amd64"\]/, authenticate_non_public: false
+
+ context 'with invalid parameters' do
+ let(:api_params) { { codename: distribution.codename } }
+
+ it_behaves_like 'Debian repository write endpoint', 'GET request', :bad_request, /^{"message":{"codename":\["has already been taken"\]}}$/, authenticate_non_public: false
+ end
+ end
+
+ describe 'GET projects/:id/debian_distributions' do
+ let(:url) { "/projects/#{container.id}/debian_distributions" }
+
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^\[{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/, authenticate_non_public: false
+ end
+
+ describe 'GET projects/:id/debian_distributions/:codename' do
+ let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
+
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/, authenticate_non_public: false
+ end
+
+ describe 'PUT projects/:id/debian_distributions/:codename' do
+ let(:method) { :put }
+ let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
+ let(:api_params) { { suite: 'my-suite' } }
+
+ it_behaves_like 'Debian repository write endpoint', 'PUT distribution request', :success, /^{.*"codename":"existing-codename",.*"suite":"my-suite",/, authenticate_non_public: false
+
+ context 'with invalid parameters' do
+ let(:api_params) { { suite: distribution.codename } }
+
+ it_behaves_like 'Debian repository write endpoint', 'GET request', :bad_request, /^{"message":{"suite":\["has already been taken as Codename"\]}}$/, authenticate_non_public: false
+ end
+ end
+
+ describe 'DELETE projects/:id/debian_distributions/:codename' do
+ let(:method) { :delete }
+ let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
+
+ it_behaves_like 'Debian repository maintainer write endpoint', 'DELETE distribution request', :success, /^{\"message\":\"202 Accepted\"}$/, authenticate_non_public: false
+
+ context 'when destroy fails' do
+ before do
+ allow_next_found_instance_of(::Packages::Debian::ProjectDistribution) do |distribution|
+ expect(distribution).to receive(:destroy).and_return(false)
+ end
+ end
+
+ it_behaves_like 'Debian repository maintainer write endpoint', 'GET request', :bad_request, /^{"message":"Failed to delete distribution"}$/, authenticate_non_public: false
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index ac24aeee52c..06f4475ef79 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -196,6 +196,19 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache do
end
end
+ context 'when export object is not present' do
+ before do
+ project_after_export.export_file.file.delete
+ end
+
+ it 'returns 404' do
+ get api(download_path_export_action, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('The project export file is not available yet')
+ end
+ end
+
context 'when upload complete' do
before do
project_after_export.remove_exports
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index fb1aa65c08d..5886f293f41 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -40,10 +40,36 @@ RSpec.describe API::ProjectPackages do
context 'with terraform module package' do
let_it_be(:terraform_module_package) { create(:terraform_module_package, project: project) }
- it 'filters out terraform module packages when no package_type filter is set' do
- subject
+ context 'when no package_type filter is set' do
+ let(:params) { {} }
+
+ it 'filters out terraform module packages' do
+ subject
+
+ expect(json_response).not_to include(a_hash_including('package_type' => 'terraform_module'))
+ end
+
+ it 'returns packages with the package registry web_path' do
+ subject
+
+ expect(json_response).to include(a_hash_including('_links' => a_hash_including('web_path' => include('packages'))))
+ end
+ end
+
+ context 'when package_type filter is set to terraform_module' do
+ let(:params) { { package_type: :terraform_module } }
- expect(json_response).not_to include(a_hash_including('package_type' => 'terraform_module'))
+ it 'returns the terraform module package' do
+ subject
+
+ expect(json_response).to include(a_hash_including('package_type' => 'terraform_module'))
+ end
+
+ it 'returns the terraform module package with the infrastructure registry web_path' do
+ subject
+
+ expect(json_response).to include(a_hash_including('_links' => a_hash_including('web_path' => include('infrastructure_registry'))))
+ end
end
end
diff --git a/spec/requests/api/project_repository_storage_moves_spec.rb b/spec/requests/api/project_repository_storage_moves_spec.rb
index b40645ba2de..5b272121233 100644
--- a/spec/requests/api/project_repository_storage_moves_spec.rb
+++ b/spec/requests/api/project_repository_storage_moves_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe API::ProjectRepositoryStorageMoves do
it_behaves_like 'repository_storage_moves API', 'projects' do
- let_it_be(:container) { create(:project, :repository).tap { |project| project.track_project_repository } }
+ let_it_be(:container) { create(:project, :repository) }
let_it_be(:storage_move) { create(:project_repository_storage_move, :scheduled, container: container) }
let(:repository_storage_move_factory) { :project_repository_storage_move }
let(:bulk_worker_klass) { Projects::ScheduleBulkRepositoryShardMovesWorker }
diff --git a/spec/requests/api/project_statistics_spec.rb b/spec/requests/api/project_statistics_spec.rb
index 5f0cac403aa..d314af0746a 100644
--- a/spec/requests/api/project_statistics_spec.rb
+++ b/spec/requests/api/project_statistics_spec.rb
@@ -3,11 +3,11 @@
require 'spec_helper'
RSpec.describe API::ProjectStatistics do
- let_it_be(:developer) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
let_it_be(:public_project) { create(:project, :public) }
before do
- public_project.add_developer(developer)
+ public_project.add_reporter(reporter)
end
describe 'GET /projects/:id/statistics' do
@@ -19,7 +19,7 @@ RSpec.describe API::ProjectStatistics do
let_it_be(:fetch_statistics_other_project) { create(:project_daily_statistic, project: create(:project), fetch_count: 29, date: 29.days.ago) }
it 'returns the fetch statistics of the last 30 days' do
- get api("/projects/#{public_project.id}/statistics", developer)
+ get api("/projects/#{public_project.id}/statistics", reporter)
expect(response).to have_gitlab_http_status(:ok)
fetches = json_response['fetches']
@@ -32,7 +32,7 @@ RSpec.describe API::ProjectStatistics do
it 'excludes the fetch statistics older than 30 days' do
create(:project_daily_statistic, fetch_count: 31, project: public_project, date: 30.days.ago)
- get api("/projects/#{public_project.id}/statistics", developer)
+ get api("/projects/#{public_project.id}/statistics", reporter)
expect(response).to have_gitlab_http_status(:ok)
fetches = json_response['fetches']
@@ -41,7 +41,7 @@ RSpec.describe API::ProjectStatistics do
expect(fetches['days'].last).to eq({ 'count' => fetch_statistics1.fetch_count, 'date' => fetch_statistics1.date.to_s })
end
- it 'responds with 403 when the user is not a developer of the repository' do
+ it 'responds with 403 when the user is not a reporter of the repository' do
guest = create(:user)
public_project.add_guest(guest)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 7f804186bc7..e7e26c34a83 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe API::Projects do
let_it_be(:user2) { create(:user) }
let_it_be(:user3) { create(:user) }
let_it_be(:admin) { create(:admin) }
- let_it_be(:project, reload: true) { create(:project, :repository, namespace: user.namespace) }
+ let_it_be(:project, reload: true) { create(:project, :repository, create_branch: 'something_else', namespace: user.namespace) }
let_it_be(:project2, reload: true) { create(:project, namespace: user.namespace) }
let_it_be(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let_it_be(:user4) { create(:user, username: 'user.with.dot') }
@@ -109,6 +109,43 @@ RSpec.describe API::Projects do
end
end
+ shared_examples_for 'create project with default branch parameter' do
+ let(:params) { { name: 'Foo Project', initialize_with_readme: true, default_branch: default_branch } }
+ let(:default_branch) { 'main' }
+
+ it 'creates project with provided default branch name' do
+ expect { request }.to change { Project.count }.by(1)
+ expect(response).to have_gitlab_http_status(:created)
+
+ project = Project.find(json_response['id'])
+ expect(project.default_branch).to eq(default_branch)
+ end
+
+ context 'when branch name is empty' do
+ let(:default_branch) { '' }
+
+ it 'creates project with a default project branch name' do
+ expect { request }.to change { Project.count }.by(1)
+ expect(response).to have_gitlab_http_status(:created)
+
+ project = Project.find(json_response['id'])
+ expect(project.default_branch).to eq('master')
+ end
+ end
+
+ context 'when initialize with readme is not set' do
+ let(:params) { super().merge(initialize_with_readme: nil) }
+
+ it 'creates project with a default project branch name' do
+ expect { request }.to change { Project.count }.by(1)
+ expect(response).to have_gitlab_http_status(:created)
+
+ project = Project.find(json_response['id'])
+ expect(project.default_branch).to be_nil
+ end
+ end
+ end
+
describe 'GET /projects' do
shared_examples_for 'projects response' do
it 'returns an array of projects' do
@@ -184,13 +221,40 @@ RSpec.describe API::Projects do
end
end
- it 'includes the project labels as the tag_list' do
+ it 'includes correct value of container_registry_enabled', :aggregate_failures do
+ project.update_column(:container_registry_enabled, true)
+ project.project_feature.update!(container_registry_access_level: ProjectFeature::DISABLED)
+
+ 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(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)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.first.keys).to include('tag_list')
+ expect(json_response.first.keys).to include('tag_list') # deprecated in favor of 'topics'
+ expect(json_response.first.keys).to include('topics')
end
it 'includes open_issues_count' do
@@ -223,9 +287,9 @@ RSpec.describe API::Projects do
expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count')
end
- context 'filter by topic (column tag_list)' do
+ context 'filter by topic (column topic_list)' do
before do
- project.update!(tag_list: %w(ruby javascript))
+ project.update!(topic_list: %w(ruby javascript))
end
it 'returns no projects' do
@@ -742,10 +806,6 @@ RSpec.describe API::Projects do
it 'includes a pagination header with link to the next page' do
get api('/projects', current_user), params: params
- expect(response.header).to include('Links')
- expect(response.header['Links']).to include('pagination=keyset')
- expect(response.header['Links']).to include("id_after=#{first_project_id}")
-
expect(response.header).to include('Link')
expect(response.header['Link']).to include('pagination=keyset')
expect(response.header['Link']).to include("id_after=#{first_project_id}")
@@ -762,10 +822,6 @@ RSpec.describe API::Projects do
it 'still includes a link if the end has reached and there is no more data after this page' do
get api('/projects', current_user), params: params.merge(id_after: project2.id)
- expect(response.header).to include('Links')
- expect(response.header['Links']).to include('pagination=keyset')
- expect(response.header['Links']).to include("id_after=#{project3.id}")
-
expect(response.header).to include('Link')
expect(response.header['Link']).to include('pagination=keyset')
expect(response.header['Link']).to include("id_after=#{project3.id}")
@@ -774,7 +830,6 @@ RSpec.describe API::Projects do
it 'does not include a next link when the page does not have any records' do
get api('/projects', current_user), params: params.merge(id_after: Project.maximum(:id))
- expect(response.header).not_to include('Links')
expect(response.header).not_to include('Link')
end
@@ -798,10 +853,6 @@ RSpec.describe API::Projects do
it 'includes a pagination header with link to the next page' do
get api('/projects', current_user), params: params
- expect(response.header).to include('Links')
- expect(response.header['Links']).to include('pagination=keyset')
- expect(response.header['Links']).to include("id_before=#{last_project_id}")
-
expect(response.header).to include('Link')
expect(response.header['Link']).to include('pagination=keyset')
expect(response.header['Link']).to include("id_before=#{last_project_id}")
@@ -828,11 +879,6 @@ RSpec.describe API::Projects do
requests += 1
get api(url, current_user), params: params
- links = response.header['Links']
- url = links&.match(/<[^>]+(\/projects\?[^>]+)>; rel="next"/) do |match|
- match[1]
- end
-
link = response.header['Link']
url = link&.match(/<[^>]+(\/projects\?[^>]+)>; rel="next"/) do |match|
match[1]
@@ -938,6 +984,10 @@ RSpec.describe API::Projects do
expect(project.path).to eq('path-project-Foo')
end
+ it_behaves_like 'create project with default branch parameter' do
+ let(:request) { post api('/projects', user), params: params }
+ end
+
it 'creates last project before reaching project limit' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
post api('/projects', user2), params: { name: 'foo' }
@@ -1050,12 +1100,20 @@ RSpec.describe API::Projects do
expect(json_response['readme_url']).to eql("#{Gitlab.config.gitlab.url}/#{json_response['namespace']['full_path']}/somewhere/-/blob/master/README.md")
end
- it 'sets tag list to a project' do
+ it 'sets tag list to a project (deprecated)' do
project = attributes_for(:project, tag_list: %w[tagFirst tagSecond])
post api('/projects', user), params: project
- expect(json_response['tag_list']).to eq(%w[tagFirst tagSecond])
+ expect(json_response['topics']).to eq(%w[tagFirst tagSecond])
+ end
+
+ it 'sets topics to a project' do
+ project = attributes_for(:project, topics: %w[topic1 topics2])
+
+ post api('/projects', user), params: project
+
+ expect(json_response['topics']).to eq(%w[topic1 topics2])
end
it 'uploads avatar for project a project' do
@@ -1410,6 +1468,10 @@ RSpec.describe API::Projects do
expect(project.path).to eq('path-project-Foo')
end
+ it_behaves_like 'create project with default branch parameter' do
+ let(:request) { post api("/projects/user/#{user.id}", admin), params: params }
+ end
+
it 'responds with 400 on failure and not project' do
expect { post api("/projects/user/#{user.id}", admin) }
.not_to change { Project.count }
@@ -1910,7 +1972,8 @@ RSpec.describe API::Projects do
expect(json_response['id']).to eq(project.id)
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
- expect(json_response['tag_list']).to be_an Array
+ expect(json_response['tag_list']).to be_an Array # deprecated in favor of 'topics'
+ expect(json_response['topics']).to be_an Array
expect(json_response['archived']).to be_falsey
expect(json_response['visibility']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
@@ -1987,7 +2050,8 @@ RSpec.describe API::Projects do
expect(json_response['id']).to eq(project.id)
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
- expect(json_response['tag_list']).to be_an Array
+ expect(json_response['tag_list']).to be_an Array # deprecated in favor of 'topics'
+ expect(json_response['topics']).to be_an Array
expect(json_response['archived']).to be_falsey
expect(json_response['visibility']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
@@ -2043,8 +2107,10 @@ RSpec.describe API::Projects do
expect(json_response['ci_default_git_depth']).to eq(project.ci_default_git_depth)
expect(json_response['ci_forward_deployment_enabled']).to eq(project.ci_forward_deployment_enabled)
expect(json_response['merge_method']).to eq(project.merge_method.to_s)
+ expect(json_response['squash_option']).to eq(project.squash_option.to_s)
expect(json_response['readme_url']).to eq(project.readme_url)
expect(json_response).to have_key 'packages_enabled'
+ expect(json_response['keep_latest_artifact']).to be_present
end
it 'returns a group link with expiration date' do
@@ -2755,7 +2821,7 @@ RSpec.describe API::Projects do
expect(project.project_group_links).to be_empty
end
- it 'updates project authorization' do
+ it 'updates project authorization', :sidekiq_inline do
expect do
delete api("/projects/#{project.id}/share/#{group.id}", user)
end.to(
@@ -2902,6 +2968,18 @@ RSpec.describe API::Projects do
end
end
+ it 'updates default_branch' do
+ project_param = { default_branch: 'something_else' }
+
+ put api("/projects/#{project.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
it 'updates jobs_enabled' do
project_param = { jobs_enabled: true }
@@ -3027,6 +3105,26 @@ RSpec.describe API::Projects do
expect(json_response['auto_devops_enabled']).to eq(false)
end
+
+ it 'updates topics using tag_list (deprecated)' do
+ project_param = { tag_list: 'topic1' }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(json_response['topics']).to eq(%w[topic1])
+ end
+
+ it 'updates topics' do
+ project_param = { topics: 'topic2' }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(json_response['topics']).to eq(%w[topic2])
+ end
end
context 'when authenticated as project maintainer' do
@@ -3203,6 +3301,24 @@ RSpec.describe API::Projects do
expect { subject }.to change { project.reload.service_desk_enabled }.to(true)
end
end
+
+ context 'when updating keep latest artifact' do
+ subject { put(api("/projects/#{project.id}", user), params: { keep_latest_artifact: true }) }
+
+ before do
+ project.update!(keep_latest_artifact: false)
+ end
+
+ it 'returns 200' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'enables keep_latest_artifact' do
+ expect { subject }.to change { project.reload.keep_latest_artifact }.to(true)
+ end
+ end
end
describe 'POST /projects/:id/archive' do
@@ -3882,6 +3998,48 @@ RSpec.describe API::Projects do
end
end
+ describe 'GET /projects/:id/storage' do
+ context 'when unauthenticated' do
+ it 'does not return project storage data' do
+ get api("/projects/#{project.id}/storage")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ it 'returns project storage data when user is admin' do
+ get api("/projects/#{project.id}/storage", create(:admin))
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['project_id']).to eq(project.id)
+ expect(json_response['disk_path']).to eq(project.repository.disk_path)
+ expect(json_response['created_at']).to be_present
+ expect(json_response['repository_storage']).to eq(project.repository_storage)
+ end
+
+ it 'does not return project storage data when user is not admin' do
+ get api("/projects/#{project.id}/storage", user3)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'responds with a 401 for unauthenticated users trying to access a non-existent project id' do
+ expect(Project.find_by(id: non_existing_record_id)).to be_nil
+
+ get api("/projects/#{non_existing_record_id}/storage")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'responds with a 403 for non-admin users trying to access a non-existent project id' do
+ expect(Project.find_by(id: non_existing_record_id)).to be_nil
+
+ get api("/projects/#{non_existing_record_id}/storage", user3)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
it_behaves_like 'custom attributes endpoints', 'projects' do
let(:attributable) { project }
let(:other_attributable) { project2 }
diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb
index 718004a0087..86925e6a0ba 100644
--- a/spec/requests/api/pypi_packages_spec.rb
+++ b/spec/requests/api/pypi_packages_spec.rb
@@ -8,69 +8,59 @@ RSpec.describe API::PypiPackages do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
- let_it_be(:project, reload: true) { create(:project, :public) }
+ let_it_be_with_reload(:group) { create(:group) }
+ let_it_be_with_reload(:project) { create(:project, :public, group: group) }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
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) }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ let(:headers) { {} }
- describe 'GET /api/v4/projects/:id/packages/pypi/simple/:package_name' do
+ context 'simple API endpoint' do
let_it_be(:package) { create(:pypi_package, project: project) }
- let(:url) { "/projects/#{project.id}/packages/pypi/simple/#{package.name}" }
- subject { get api(url) }
+ subject { get api(url), headers: headers }
- context 'with valid project' do
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'PyPI package versions' | :success
- 'PUBLIC' | :guest | true | true | 'PyPI package versions' | :success
- 'PUBLIC' | :developer | true | false | 'PyPI package versions' | :success
- 'PUBLIC' | :guest | true | false | 'PyPI package versions' | :success
- 'PUBLIC' | :developer | false | true | 'PyPI package versions' | :success
- 'PUBLIC' | :guest | false | true | 'PyPI package versions' | :success
- 'PUBLIC' | :developer | false | false | 'PyPI package versions' | :success
- 'PUBLIC' | :guest | false | false | 'PyPI package versions' | :success
- 'PUBLIC' | :anonymous | false | true | 'PyPI package versions' | :success
- 'PRIVATE' | :developer | true | true | 'PyPI package versions' | :success
- 'PRIVATE' | :guest | true | true | 'process PyPI api request' | :forbidden
- 'PRIVATE' | :developer | true | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'process PyPI api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process PyPI api request' | :not_found
- 'PRIVATE' | :developer | false | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'process PyPI api request' | :unauthorized
- end
+ describe 'GET /api/v4/groups/:id/-/packages/pypi/simple/:package_name' do
+ let(:url) { "/groups/#{group.id}/-/packages/pypi/simple/#{package.name}" }
+ let(:snowplow_gitlab_standard_context) { {} }
- with_them do
- let(:token) { user_token ? personal_access_token.token : 'wrong' }
- let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ it_behaves_like 'pypi simple API endpoint'
+ it_behaves_like 'rejects PyPI access with unknown group id'
- subject { get api(url), headers: headers }
+ context 'deploy tokens' do
+ let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: group) }
before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+ group.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
end
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ it_behaves_like 'deploy token for package GET requests'
end
- end
- context 'with a normalized package name' do
- let_it_be(:package) { create(:pypi_package, project: project, name: 'my.package') }
- let(:url) { "/projects/#{project.id}/packages/pypi/simple/my-package" }
- let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
+ context 'job token' do
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+ group.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+ group.add_developer(user)
+ end
- subject { get api(url), headers: headers }
+ it_behaves_like 'job token for package GET requests'
+ end
- it_behaves_like 'PyPI package versions', :developer, :success
+ it_behaves_like 'a pypi user namespace endpoint'
end
- it_behaves_like 'deploy token for package GET requests'
-
- it_behaves_like 'job token for package GET requests'
+ describe 'GET /api/v4/projects/:id/packages/pypi/simple/:package_name' do
+ let(:url) { "/projects/#{project.id}/packages/pypi/simple/#{package.name}" }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
- it_behaves_like 'rejects PyPI access with unknown project id'
+ it_behaves_like 'pypi simple API endpoint'
+ it_behaves_like 'rejects PyPI access with unknown project id'
+ it_behaves_like 'deploy token for package GET requests'
+ it_behaves_like 'job token for package GET requests'
+ end
end
describe 'POST /api/v4/projects/:id/packages/pypi/authorize' do
@@ -82,25 +72,25 @@ RSpec.describe API::PypiPackages do
subject { post api(url), headers: headers }
context 'with valid project' do
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process PyPI api request' | :success
- 'PUBLIC' | :guest | true | true | 'process PyPI api request' | :forbidden
- 'PUBLIC' | :developer | true | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'process PyPI api request' | :forbidden
- 'PUBLIC' | :guest | false | true | 'process PyPI api request' | :forbidden
- 'PUBLIC' | :developer | false | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process PyPI api request' | :success
- 'PRIVATE' | :guest | true | true | 'process PyPI api request' | :forbidden
- 'PRIVATE' | :developer | true | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'process PyPI api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process PyPI api request' | :not_found
- 'PRIVATE' | :developer | false | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'process PyPI api request' | :unauthorized
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ :public | :developer | true | true | 'process PyPI api request' | :success
+ :public | :guest | true | true | 'process PyPI api request' | :forbidden
+ :public | :developer | true | false | 'process PyPI api request' | :unauthorized
+ :public | :guest | true | false | 'process PyPI api request' | :unauthorized
+ :public | :developer | false | true | 'process PyPI api request' | :forbidden
+ :public | :guest | false | true | 'process PyPI api request' | :forbidden
+ :public | :developer | false | false | 'process PyPI api request' | :unauthorized
+ :public | :guest | false | false | 'process PyPI api request' | :unauthorized
+ :public | :anonymous | false | true | 'process PyPI api request' | :unauthorized
+ :private | :developer | true | true | 'process PyPI api request' | :success
+ :private | :guest | true | true | 'process PyPI api request' | :forbidden
+ :private | :developer | true | false | 'process PyPI api request' | :unauthorized
+ :private | :guest | true | false | 'process PyPI api request' | :unauthorized
+ :private | :developer | false | true | 'process PyPI api request' | :not_found
+ :private | :guest | false | true | 'process PyPI api request' | :not_found
+ :private | :developer | false | false | 'process PyPI api request' | :unauthorized
+ :private | :guest | false | false | 'process PyPI api request' | :unauthorized
+ :private | :anonymous | false | true | 'process PyPI api request' | :unauthorized
end
with_them do
@@ -109,7 +99,7 @@ RSpec.describe API::PypiPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ 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], params[:member]
@@ -133,6 +123,7 @@ RSpec.describe API::PypiPackages do
let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '123' } }
let(:params) { base_params.merge(content: temp_file(file_name)) }
let(:send_rewritten_field) { true }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
subject do
workhorse_finalize(
@@ -146,25 +137,25 @@ RSpec.describe API::PypiPackages do
end
context 'with valid project' do
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'PyPI package creation' | :created
- 'PUBLIC' | :guest | true | true | 'process PyPI api request' | :forbidden
- 'PUBLIC' | :developer | true | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'process PyPI api request' | :forbidden
- 'PUBLIC' | :guest | false | true | 'process PyPI api request' | :forbidden
- 'PUBLIC' | :developer | false | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'process PyPI api request' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process PyPI api request' | :created
- 'PRIVATE' | :guest | true | true | 'process PyPI api request' | :forbidden
- 'PRIVATE' | :developer | true | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'process PyPI api request' | :not_found
- 'PRIVATE' | :guest | false | true | 'process PyPI api request' | :not_found
- 'PRIVATE' | :developer | false | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'process PyPI api request' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'process PyPI api request' | :unauthorized
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ :public | :developer | true | true | 'PyPI package creation' | :created
+ :public | :guest | true | true | 'process PyPI api request' | :forbidden
+ :public | :developer | true | false | 'process PyPI api request' | :unauthorized
+ :public | :guest | true | false | 'process PyPI api request' | :unauthorized
+ :public | :developer | false | true | 'process PyPI api request' | :forbidden
+ :public | :guest | false | true | 'process PyPI api request' | :forbidden
+ :public | :developer | false | false | 'process PyPI api request' | :unauthorized
+ :public | :guest | false | false | 'process PyPI api request' | :unauthorized
+ :public | :anonymous | false | true | 'process PyPI api request' | :unauthorized
+ :private | :developer | true | true | 'process PyPI api request' | :created
+ :private | :guest | true | true | 'process PyPI api request' | :forbidden
+ :private | :developer | true | false | 'process PyPI api request' | :unauthorized
+ :private | :guest | true | false | 'process PyPI api request' | :unauthorized
+ :private | :developer | false | true | 'process PyPI api request' | :not_found
+ :private | :guest | false | true | 'process PyPI api request' | :not_found
+ :private | :developer | false | false | 'process PyPI api request' | :unauthorized
+ :private | :guest | false | false | 'process PyPI api request' | :unauthorized
+ :private | :anonymous | false | true | 'process PyPI api request' | :unauthorized
end
with_them do
@@ -173,7 +164,7 @@ RSpec.describe API::PypiPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ 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], params[:member]
@@ -187,7 +178,7 @@ RSpec.describe API::PypiPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
end
it_behaves_like 'process PyPI api request', :developer, :bad_request, true
@@ -225,84 +216,27 @@ RSpec.describe API::PypiPackages do
end
end
- describe 'GET /api/v4/projects/:id/packages/pypi/files/:sha256/*file_identifier' do
+ context 'file download endpoint' do
let_it_be(:package_name) { 'Dummy-Package' }
let_it_be(:package) { create(:pypi_package, project: project, name: package_name, version: '1.0.0') }
- let(:url) { "/projects/#{project.id}/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" }
-
- subject { get api(url) }
-
- context 'with valid project' do
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'PyPI package download' | :success
- 'PUBLIC' | :guest | true | true | 'PyPI package download' | :success
- 'PUBLIC' | :developer | true | false | 'PyPI package download' | :success
- 'PUBLIC' | :guest | true | false | 'PyPI package download' | :success
- 'PUBLIC' | :developer | false | true | 'PyPI package download' | :success
- 'PUBLIC' | :guest | false | true | 'PyPI package download' | :success
- 'PUBLIC' | :developer | false | false | 'PyPI package download' | :success
- 'PUBLIC' | :guest | false | false | 'PyPI package download' | :success
- 'PUBLIC' | :anonymous | false | true | 'PyPI package download' | :success
- 'PRIVATE' | :developer | true | true | 'PyPI package download' | :success
- 'PRIVATE' | :guest | true | true | 'PyPI package download' | :success
- 'PRIVATE' | :developer | true | false | 'PyPI package download' | :success
- 'PRIVATE' | :guest | true | false | 'PyPI package download' | :success
- 'PRIVATE' | :developer | false | true | 'PyPI package download' | :success
- 'PRIVATE' | :guest | false | true | 'PyPI package download' | :success
- 'PRIVATE' | :developer | false | false | 'PyPI package download' | :success
- 'PRIVATE' | :guest | false | false | 'PyPI package download' | :success
- 'PRIVATE' | :anonymous | false | true | 'PyPI package download' | :success
- end
-
- with_them do
- let(:token) { user_token ? personal_access_token.token : 'wrong' }
- let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
-
- subject { get api(url), headers: headers }
+ subject { get api(url), headers: headers }
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- context 'with deploy token headers' do
- let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token) }
-
- context 'valid token' do
- it_behaves_like 'returning response status', :success
- end
-
- context 'invalid token' do
- let(:headers) { basic_auth_header('foo', 'bar') }
+ describe 'GET /api/v4/groups/:id/-/packages/pypi/files/:sha256/*file_identifier' do
+ let(:url) { "/groups/#{group.id}/-/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" }
+ let(:snowplow_gitlab_standard_context) { {} }
- it_behaves_like 'returning response status', :success
- end
+ it_behaves_like 'pypi file download endpoint'
+ it_behaves_like 'rejects PyPI access with unknown group id'
+ it_behaves_like 'a pypi user namespace endpoint'
end
- context 'with job token headers' do
- let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token) }
-
- context 'valid token' do
- it_behaves_like 'returning response status', :success
- end
-
- context 'invalid token' do
- let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar') }
-
- it_behaves_like 'returning response status', :success
- end
-
- context 'invalid user' do
- let(:headers) { basic_auth_header('foo', job.token) }
+ describe 'GET /api/v4/projects/:id/packages/pypi/files/:sha256/*file_identifier' do
+ let(:url) { "/projects/#{project.id}/packages/pypi/files/#{package.package_files.first.file_sha256}/#{package_name}-1.0.0.tar.gz" }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
- it_behaves_like 'returning response status', :success
- end
+ it_behaves_like 'pypi file download endpoint'
+ it_behaves_like 'rejects PyPI access with unknown project id'
end
-
- it_behaves_like 'rejects PyPI access with unknown project id'
end
end
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index dad3e34404b..81a4fcdbcac 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -775,7 +775,7 @@ RSpec.describe API::Releases do
end
context 'when using JOB-TOKEN auth' do
- let(:job) { create(:ci_build, user: maintainer) }
+ let(:job) { create(:ci_build, user: maintainer, project: project) }
let(:params) do
{
name: 'Another release',
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index a12b4dc9848..1b96efeca22 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -178,10 +178,12 @@ RSpec.describe API::Repositories do
expect(headers['Content-Disposition']).to eq 'inline'
end
- it_behaves_like 'uncached response' do
- before do
- get api(route, current_user)
- end
+ it 'defines an uncached header response' do
+ get api(route, current_user)
+
+ expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
+ expect(response.headers["Pragma"]).to eq("no-cache")
+ expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
context 'when sha does not exist' do
diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb
index d6ad8186063..7d863b55bbe 100644
--- a/spec/requests/api/rubygem_packages_spec.rb
+++ b/spec/requests/api/rubygem_packages_spec.rb
@@ -10,10 +10,11 @@ RSpec.describe API::RubygemPackages do
let_it_be_with_reload(:project) { create(:project) }
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:user) { personal_access_token.user }
- let_it_be(:job) { create(:ci_build, :running, user: user) }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
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
{
@@ -162,6 +163,7 @@ RSpec.describe API::RubygemPackages do
with_them do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
@@ -304,6 +306,16 @@ RSpec.describe API::RubygemPackages do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:user_headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user } }
+ let(:snowplow_user) do
+ if token_type == :deploy_token
+ deploy_token
+ elsif token_type == :job_token
+ job.user
+ else
+ user
+ end
+ end
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 1f859622760..8701efcd65f 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe API::Services do
context 'project with services' do
let!(:active_service) { create(:emails_on_push_service, project: project, active: true) }
- let!(:service) { create(:custom_issue_tracker_service, project: project, active: false) }
+ let!(:service) { create(:custom_issue_tracker_integration, project: project, active: false) }
it "returns a list of all active services" do
get api("/projects/#{project.id}/services", user)
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 66c0dcaa36c..4a4aeaea714 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -113,6 +113,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
terms: 'Hello world!',
performance_bar_allowed_group_path: group.full_path,
diff_max_patch_bytes: 300_000,
+ diff_max_files: 2000,
+ diff_max_lines: 50000,
default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE,
local_markdown_version: 3,
allow_local_requests_from_web_hooks_and_services: true,
@@ -159,6 +161,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['terms']).to eq('Hello world!')
expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
expect(json_response['diff_max_patch_bytes']).to eq(300_000)
+ expect(json_response['diff_max_files']).to eq(2000)
+ expect(json_response['diff_max_lines']).to eq(50000)
expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(json_response['local_markdown_version']).to eq(3)
expect(json_response['allow_local_requests_from_web_hooks_and_services']).to eq(true)
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 3c698cf577e..1aa1ad87be9 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -358,17 +358,6 @@ RSpec.describe API::Tags do
expect(json_response['message']).to eq('Target foo is invalid')
end
- context 'lightweight tags with release notes' do
- it 'creates a new tag' do
- post api(route, current_user), params: { tag_name: tag_name, ref: 'master', release_description: 'Wow' }
-
- expect(response).to have_gitlab_http_status(:created)
- expect(response).to match_response_schema('public_api/v4/tag')
- expect(json_response['name']).to eq(tag_name)
- expect(json_response['release']['description']).to eq('Wow')
- end
- end
-
context 'annotated tag' do
it 'creates a new annotated tag' do
# Identity must be set in .gitconfig to create annotated tag.
@@ -440,122 +429,4 @@ RSpec.describe API::Tags do
end
end
end
-
- describe 'POST /projects/:id/repository/tags/:tag_name/release' do
- let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/release" }
- let(:description) { 'Awesome release!' }
-
- shared_examples_for 'repository new release' do
- it 'creates description for existing git tag' do
- post api(route, user), params: { description: description }
-
- expect(response).to have_gitlab_http_status(:created)
- expect(response).to match_response_schema('public_api/v4/release/tag_release')
- expect(json_response['tag_name']).to eq(tag_name)
- expect(json_response['description']).to eq(description)
- end
-
- context 'when tag does not exist' do
- let(:tag_name) { 'unknown' }
-
- it_behaves_like '404 response' do
- let(:request) { post api(route, current_user), params: { description: description } }
- let(:message) { '404 Tag Not Found' }
- end
- end
-
- context 'when repository is disabled' do
- include_context 'disabled repository'
-
- it_behaves_like '403 response' do
- let(:request) { post api(route, current_user), params: { description: description } }
- end
- end
- end
-
- context 'when authenticated', 'as a maintainer' do
- let(:current_user) { user }
-
- it_behaves_like 'repository new release'
-
- context 'requesting with the escaped project full path' do
- let(:project_id) { CGI.escape(project.full_path) }
-
- it_behaves_like 'repository new release'
- end
-
- context 'on tag with existing release' do
- let!(:release) { create(:release, :legacy, project: project, tag: tag_name, description: description) }
-
- it 'returns 409 if there is already a release' do
- post api(route, user), params: { description: description }
-
- expect(response).to have_gitlab_http_status(:conflict)
- expect(json_response['message']).to eq('Release already exists')
- end
- end
- end
- end
-
- describe 'PUT id/repository/tags/:tag_name/release' do
- let(:route) { "/projects/#{project_id}/repository/tags/#{tag_name}/release" }
- let(:description) { 'Awesome release!' }
- let(:new_description) { 'The best release!' }
-
- shared_examples_for 'repository update release' do
- context 'on tag with existing release' do
- let!(:release) do
- create(:release,
- :legacy,
- project: project,
- tag: tag_name,
- description: description)
- end
-
- it 'updates the release description' do
- put api(route, current_user), params: { description: new_description }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['tag_name']).to eq(tag_name)
- expect(json_response['description']).to eq(new_description)
- end
- end
-
- context 'when tag does not exist' do
- let(:tag_name) { 'unknown' }
-
- it_behaves_like '403 response' do
- let(:request) { put api(route, current_user), params: { description: new_description } }
- let(:message) { '403 Forbidden' }
- end
- end
-
- context 'when repository is disabled' do
- include_context 'disabled repository'
-
- it_behaves_like '403 response' do
- let(:request) { put api(route, current_user), params: { description: new_description } }
- end
- end
- end
-
- context 'when authenticated', 'as a maintainer' do
- let(:current_user) { user }
-
- it_behaves_like 'repository update release'
-
- context 'requesting with the escaped project full path' do
- let(:project_id) { CGI.escape(project.full_path) }
-
- it_behaves_like 'repository update release'
- end
-
- context 'when release does not exist' do
- it_behaves_like '403 response' do
- let(:request) { put api(route, current_user), params: { description: new_description } }
- let(:message) { '403 Forbidden' }
- end
- end
- end
- end
end
diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb
index d318b22cf27..b04f5ad9a94 100644
--- a/spec/requests/api/terraform/modules/v1/packages_spec.rb
+++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe API::Terraform::Modules::V1::Packages do
let_it_be(:package) { create(:terraform_module_package, project: project) }
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:user) { personal_access_token.user }
- let_it_be(:job) { create(:ci_build, :running, user: user) }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
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) }
@@ -188,6 +188,7 @@ RSpec.describe API::Terraform::Modules::V1::Packages do
with_them do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:url) { api("/packages/terraform/modules/v1/#{group.path}/#{package.name}/#{package.version}/file?token=#{token}") }
+ let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } }
before do
group.update!(visibility: visibility.to_s)
@@ -330,6 +331,16 @@ RSpec.describe API::Terraform::Modules::V1::Packages do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:user_headers) { user_role == :anonymous ? {} : { token_header => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: snowplow_user } }
+ let(:snowplow_user) do
+ if token_type == :deploy_token
+ deploy_token
+ elsif token_type == :job_token
+ job.user
+ else
+ user
+ end
+ end
before do
project.update!(visibility: visibility.to_s)
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index 2cb3c8e9ab5..5d2635126e8 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -25,10 +25,6 @@ RSpec.describe API::Terraform::State do
context 'without authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
- before do
- stub_feature_flags(usage_data_p_terraform_state_api_unique_users: false)
- end
-
it 'does not track unique event' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
@@ -156,15 +152,6 @@ RSpec.describe API::Terraform::State do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to be_empty
end
-
- context 'on Unicorn', :unicorn do
- it 'updates the state' do
- expect { request }.to change { Terraform::State.count }.by(0)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(Gitlab::Json.parse(response.body)).to be_empty
- end
- end
end
context 'without body' do
@@ -200,15 +187,6 @@ RSpec.describe API::Terraform::State do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to be_empty
end
-
- context 'on Unicorn', :unicorn do
- it 'creates a new state' do
- expect { request }.to change { Terraform::State.count }.by(1)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(Gitlab::Json.parse(response.body)).to be_empty
- end
- end
end
context 'without body' do
diff --git a/spec/requests/api/unleash_spec.rb b/spec/requests/api/unleash_spec.rb
index 0b70d62b093..9989f8d28bd 100644
--- a/spec/requests/api/unleash_spec.rb
+++ b/spec/requests/api/unleash_spec.rb
@@ -176,34 +176,9 @@ RSpec.describe API::Unleash do
it_behaves_like 'authenticated request'
context 'with version 1 (legacy) feature flags' do
- let(:feature_flag) { create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 1) }
+ let(:feature_flag) { create(:operations_feature_flag, :legacy_flag, project: project, name: 'feature1', active: true, version: 1) }
- it_behaves_like 'support multiple environments'
-
- context 'with a list of feature flags' do
- let(:headers) { { "UNLEASH-INSTANCEID" => client.token, "UNLEASH-APPNAME" => "production" } }
- let!(:enabled_feature_flag) { create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 1) }
- let!(:disabled_feature_flag) { create(:operations_feature_flag, project: project, name: 'feature2', active: false, version: 1) }
-
- it 'responds with a list of features' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['version']).to eq(1)
- expect(json_response['features']).not_to be_empty
- expect(json_response['features'].map { |f| f['name'] }.sort).to eq(%w[feature1 feature2])
- expect(json_response['features'].sort_by {|f| f['name'] }.map { |f| f['enabled'] }).to eq([true, false])
- end
-
- it 'matches json schema' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('unleash/unleash')
- end
- end
-
- it 'returns a feature flag strategy' do
+ it 'does not return a legacy feature flag' do
create(:operations_feature_flag_scope,
feature_flag: feature_flag,
environment_scope: 'sandbox',
@@ -215,81 +190,7 @@ RSpec.describe API::Unleash do
get api(features_url), headers: headers
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['features'].first['enabled']).to eq(true)
- strategies = json_response['features'].first['strategies']
- expect(strategies).to eq([{
- "name" => "gradualRolloutUserId",
- "parameters" => {
- "percentage" => "50",
- "groupId" => "default"
- }
- }])
- end
-
- it 'returns a default strategy for a scope' do
- create(:operations_feature_flag_scope, feature_flag: feature_flag, environment_scope: 'sandbox', active: true)
- headers = { "UNLEASH-INSTANCEID" => client.token, "UNLEASH-APPNAME" => "sandbox" }
-
- get api(features_url), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['features'].first['enabled']).to eq(true)
- strategies = json_response['features'].first['strategies']
- expect(strategies).to eq([{ "name" => "default", "parameters" => {} }])
- end
-
- it 'returns multiple strategies for a feature flag' do
- create(:operations_feature_flag_scope,
- feature_flag: feature_flag,
- environment_scope: 'staging',
- active: true,
- strategies: [{ name: "userWithId", parameters: { userIds: "max,fred" } },
- { name: "gradualRolloutUserId",
- parameters: { groupId: "default", percentage: "50" } }])
- headers = { "UNLEASH-INSTANCEID" => client.token, "UNLEASH-APPNAME" => "staging" }
-
- get api(features_url), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['features'].first['enabled']).to eq(true)
- strategies = json_response['features'].first['strategies'].sort_by { |s| s['name'] }
- expect(strategies).to eq([{
- "name" => "gradualRolloutUserId",
- "parameters" => {
- "percentage" => "50",
- "groupId" => "default"
- }
- }, {
- "name" => "userWithId",
- "parameters" => {
- "userIds" => "max,fred"
- }
- }])
- end
-
- it 'returns a disabled feature when the flag is disabled' do
- flag = create(:operations_feature_flag, project: project, name: 'test_feature', active: false, version: 1)
- create(:operations_feature_flag_scope, feature_flag: flag, environment_scope: 'production', active: true)
- headers = { "UNLEASH-INSTANCEID" => client.token, "UNLEASH-APPNAME" => "production" }
-
- get api(features_url), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['features'].first['enabled']).to eq(false)
- end
-
- context "with an inactive scope" do
- let!(:scope) { create(:operations_feature_flag_scope, feature_flag: feature_flag, environment_scope: 'production', active: false, strategies: [{ name: "default", parameters: {} }]) }
- let(:headers) { { "UNLEASH-INSTANCEID" => client.token, "UNLEASH-APPNAME" => "production" } }
-
- it 'returns a disabled feature' do
- get api(features_url), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- feature_json = json_response['features'].first
- expect(feature_json['enabled']).to eq(false)
- expect(feature_json['strategies']).to eq([{ 'name' => 'default', 'parameters' => {} }])
- end
+ expect(json_response['features']).to be_empty
end
end
@@ -534,63 +435,6 @@ RSpec.describe API::Unleash do
}])
end
end
-
- context 'when mixing version 1 and version 2 feature flags' do
- it 'returns both types of flags when both match' do
- feature_flag_a = create(:operations_feature_flag, project: project,
- name: 'feature_a', active: true, version: 2)
- strategy = create(:operations_strategy, feature_flag: feature_flag_a,
- name: 'userWithId', parameters: { userIds: 'user8' })
- create(:operations_scope, strategy: strategy, environment_scope: 'staging')
- feature_flag_b = create(:operations_feature_flag, project: project,
- name: 'feature_b', active: true, version: 1)
- create(:operations_feature_flag_scope, feature_flag: feature_flag_b,
- active: true, strategies: [{ name: 'default', parameters: {} }], environment_scope: 'staging')
-
- get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'staging' }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['features'].sort_by {|f| f['name']}).to eq([{
- 'name' => 'feature_a',
- 'enabled' => true,
- 'strategies' => [{
- 'name' => 'userWithId',
- 'parameters' => { 'userIds' => 'user8' }
- }]
- }, {
- 'name' => 'feature_b',
- 'enabled' => true,
- 'strategies' => [{
- 'name' => 'default',
- 'parameters' => {}
- }]
- }])
- end
-
- it 'returns legacy flags when only legacy flags match' do
- feature_flag_a = create(:operations_feature_flag, project: project,
- name: 'feature_a', active: true, version: 2)
- strategy = create(:operations_strategy, feature_flag: feature_flag_a,
- name: 'userWithId', parameters: { userIds: 'user8' })
- create(:operations_scope, strategy: strategy, environment_scope: 'production')
- feature_flag_b = create(:operations_feature_flag, project: project,
- name: 'feature_b', active: true, version: 1)
- create(:operations_feature_flag_scope, feature_flag: feature_flag_b,
- active: true, strategies: [{ name: 'default', parameters: {} }], environment_scope: 'staging')
-
- get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'staging' }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['features']).to eq([{
- 'name' => 'feature_b',
- 'enabled' => true,
- 'strategies' => [{
- 'name' => 'default',
- 'parameters' => {}
- }]
- }])
- end
- end
end
end
diff --git a/spec/requests/api/users_preferences_spec.rb b/spec/requests/api/users_preferences_spec.rb
index db03786ed2a..97e37263ee6 100644
--- a/spec/requests/api/users_preferences_spec.rb
+++ b/spec/requests/api/users_preferences_spec.rb
@@ -8,11 +8,19 @@ RSpec.describe API::Users do
describe 'PUT /user/preferences/' do
context "with correct attributes and a logged in user" do
it 'returns a success status and the value has been changed' do
- put api("/user/preferences", user), params: { view_diffs_file_by_file: true }
+ put api("/user/preferences", user), params: {
+ view_diffs_file_by_file: true,
+ show_whitespace_in_diffs: true
+ }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['view_diffs_file_by_file']).to eq(true)
- expect(user.reload.view_diffs_file_by_file).to be_truthy
+ expect(json_response['show_whitespace_in_diffs']).to eq(true)
+
+ user.reload
+
+ expect(user.view_diffs_file_by_file).to be_truthy
+ expect(user.show_whitespace_in_diffs).to be_truthy
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 71fdd986f20..a9231b65c8f 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -2077,6 +2077,29 @@ RSpec.describe API::Users do
it_behaves_like 'get user info', 'v4'
end
+ describe "GET /user/preferences" do
+ context "when unauthenticated" do
+ it "returns authentication error" do
+ get api("/user/preferences")
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context "when authenticated" do
+ it "returns user preferences" do
+ user.user_preference.view_diffs_file_by_file = false
+ user.user_preference.show_whitespace_in_diffs = true
+ user.save!
+
+ get api("/user/preferences", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response["view_diffs_file_by_file"]).to eq(user.user_preference.view_diffs_file_by_file)
+ expect(json_response["show_whitespace_in_diffs"]).to eq(user.user_preference.show_whitespace_in_diffs)
+ end
+ end
+ end
+
describe "GET /user/keys" do
context "when unauthenticated" do
it "returns authentication error" do
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index d35aab40ca9..64fde3db19f 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -16,8 +16,19 @@ RSpec.describe API::Wikis do
include WorkhorseHelpers
include AfterNextHelpers
- let(:user) { create(:user) }
- let(:group) { create(:group).tap { |g| g.add_owner(user) } }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group).tap { |g| g.add_owner(user) } }
+ let_it_be(:group_project) { create(:project, :wiki_repo, namespace: group) }
+
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:project_wiki_disabled) do
+ create(:project, :wiki_repo, :wiki_disabled).tap do |project|
+ project.add_developer(developer)
+ project.add_maintainer(maintainer)
+ end
+ end
+
let(:project_wiki) { create(:project_wiki, project: project, user: user) }
let(:payload) { { content: 'content', format: 'rdoc', title: 'title' } }
let(:expected_keys_with_content) { %w(content format slug title) }
@@ -32,7 +43,7 @@ RSpec.describe API::Wikis do
let(:url) { "/projects/#{project.id}/wikis" }
context 'when wiki is disabled' do
- let(:project) { create(:project, :wiki_repo, :wiki_disabled) }
+ let(:project) { project_wiki_disabled }
context 'when user is guest' do
before do
@@ -44,9 +55,7 @@ RSpec.describe API::Wikis do
context 'when user is developer' do
before do
- project.add_developer(user)
-
- get api(url, user)
+ get api(url, developer)
end
include_examples 'wiki API 403 Forbidden'
@@ -54,9 +63,7 @@ RSpec.describe API::Wikis do
context 'when user is maintainer' do
before do
- project.add_maintainer(user)
-
- get api(url, user)
+ get api(url, maintainer)
end
include_examples 'wiki API 403 Forbidden'
@@ -125,7 +132,7 @@ RSpec.describe API::Wikis do
let(:url) { "/projects/#{project.id}/wikis/#{page.slug}" }
context 'when wiki is disabled' do
- let(:project) { create(:project, :wiki_repo, :wiki_disabled) }
+ let(:project) { project_wiki_disabled }
context 'when user is guest' do
before do
@@ -137,9 +144,7 @@ RSpec.describe API::Wikis do
context 'when user is developer' do
before do
- project.add_developer(user)
-
- get api(url, user)
+ get api(url, developer)
end
include_examples 'wiki API 403 Forbidden'
@@ -147,9 +152,7 @@ RSpec.describe API::Wikis do
context 'when user is maintainer' do
before do
- project.add_maintainer(user)
-
- get api(url, user)
+ get api(url, maintainer)
end
include_examples 'wiki API 403 Forbidden'
@@ -249,7 +252,7 @@ RSpec.describe API::Wikis do
let(:url) { "/projects/#{project.id}/wikis" }
context 'when wiki is disabled' do
- let(:project) { create(:project, :wiki_disabled, :wiki_repo) }
+ let(:project) { project_wiki_disabled }
context 'when user is guest' do
before do
@@ -261,8 +264,7 @@ RSpec.describe API::Wikis do
context 'when user is developer' do
before do
- project.add_developer(user)
- post(api(url, user), params: payload)
+ post(api(url, developer), params: payload)
end
include_examples 'wiki API 403 Forbidden'
@@ -270,8 +272,7 @@ RSpec.describe API::Wikis do
context 'when user is maintainer' do
before do
- project.add_maintainer(user)
- post(api(url, user), params: payload)
+ post(api(url, maintainer), params: payload)
end
include_examples 'wiki API 403 Forbidden'
@@ -469,7 +470,7 @@ RSpec.describe API::Wikis do
end
context 'when wiki belongs to a group project' do
- let(:project) { create(:project, :wiki_repo, namespace: group) }
+ let(:project) { group_project }
include_examples 'wikis API updates wiki page'
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 279c65fc2f4..7cf46f6adc6 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -883,10 +883,10 @@ RSpec.describe 'Git HTTP requests' do
context 'when admin mode is enabled', :enable_admin_mode do
it_behaves_like 'can download code only'
- it 'downloads from other project get status 403' do
+ it 'downloads from other project get status 404' do
clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
diff --git a/spec/requests/groups/email_campaigns_controller_spec.rb b/spec/requests/groups/email_campaigns_controller_spec.rb
index 48297ec4cb6..4d630ef6710 100644
--- a/spec/requests/groups/email_campaigns_controller_spec.rb
+++ b/spec/requests/groups/email_campaigns_controller_spec.rb
@@ -9,10 +9,11 @@ RSpec.describe Groups::EmailCampaignsController do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
+
let(:track) { 'create' }
let(:series) { '0' }
let(:schema) { described_class::EMAIL_CAMPAIGNS_SCHEMA_URL }
- let(:subject_line_text) { Gitlab::Email::Message::InProductMarketing.for(track.to_sym).new(group: group, series: series.to_i).subject_line }
+ let(:subject_line_text) { Gitlab::Email::Message::InProductMarketing.for(track.to_sym).new(group: group, user: user, series: series.to_i).subject_line }
let(:data) do
{
namespace_id: group.id,
@@ -51,7 +52,9 @@ RSpec.describe Groups::EmailCampaignsController do
context: [{
schema: described_class::EMAIL_CAMPAIGNS_SCHEMA_URL,
data: { namespace_id: group.id, series: series.to_i, subject_line: subject_line_text, track: track.to_s }
- }]
+ }],
+ user: user,
+ namespace: group
)
end
@@ -91,7 +94,7 @@ RSpec.describe Groups::EmailCampaignsController do
describe 'track parameter' do
context 'when valid' do
- where(track: Namespaces::InProductMarketingEmailsService::TRACKS.keys)
+ where(track: Namespaces::InProductMarketingEmailsService::TRACKS.keys.without(:experience))
with_them do
it_behaves_like 'track and redirect'
@@ -109,7 +112,7 @@ RSpec.describe Groups::EmailCampaignsController do
describe 'series parameter' do
context 'when valid' do
- where(series: (0..Namespaces::InProductMarketingEmailsService::INTERVAL_DAYS.length - 1).to_a)
+ where(series: (0..Namespaces::InProductMarketingEmailsService::TRACKS[:create][:interval_days].length - 1).to_a)
with_them do
it_behaves_like 'track and redirect'
@@ -117,7 +120,7 @@ RSpec.describe Groups::EmailCampaignsController do
end
context 'when invalid' do
- where(series: [-1, nil, Namespaces::InProductMarketingEmailsService::INTERVAL_DAYS.length])
+ where(series: [-1, nil, Namespaces::InProductMarketingEmailsService::TRACKS[:create][:interval_days].length])
with_them do
it_behaves_like 'no track and 404'
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 0e3a0252638..fda8b2ecec6 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -569,7 +569,7 @@ RSpec.describe 'Git LFS API and storage' do
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
# I'm not sure what this tests that is different from the previous test
- it_behaves_like 'LFS http 403 response'
+ it_behaves_like 'LFS http 404 response'
end
end
@@ -1043,7 +1043,7 @@ RSpec.describe 'Git LFS API and storage' do
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
# I'm not sure what this tests that is different from the previous test
- it_behaves_like 'LFS http 403 response'
+ it_behaves_like 'LFS http 404 response'
end
end
diff --git a/spec/requests/oauth/tokens_controller_spec.rb b/spec/requests/oauth/tokens_controller_spec.rb
index c3cdae2cd21..1967d0ba8b1 100644
--- a/spec/requests/oauth/tokens_controller_spec.rb
+++ b/spec/requests/oauth/tokens_controller_spec.rb
@@ -3,12 +3,71 @@
require 'spec_helper'
RSpec.describe Oauth::TokensController do
- it 'allows cross-origin POST requests' do
- post '/oauth/token', headers: { 'Origin' => 'http://notgitlab.com' }
+ let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
+ let(:other_headers) { {} }
+ let(:headers) { cors_request_headers.merge(other_headers)}
- expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
- expect(response.headers['Access-Control-Allow-Methods']).to eq 'POST'
- expect(response.headers['Access-Control-Allow-Headers']).to be_nil
- expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
+ shared_examples 'cross-origin POST request' do
+ it 'allows cross-origin requests' do
+ expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'POST'
+ expect(response.headers['Access-Control-Allow-Headers']).to be_nil
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
+ end
+ end
+
+ shared_examples 'CORS preflight OPTIONS request' do
+ it 'returns 200' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'allows cross-origin requests' do
+ expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'POST'
+ expect(response.headers['Access-Control-Allow-Headers']).to eq 'Authorization'
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
+ end
+ end
+
+ describe 'POST /oauth/token' do
+ before do
+ post '/oauth/token', headers: headers
+ end
+
+ it_behaves_like 'cross-origin POST request'
+ end
+
+ describe 'OPTIONS /oauth/token' do
+ let(:other_headers) { { 'Access-Control-Request-Headers' => 'Authorization', 'Access-Control-Request-Method' => 'POST' } }
+
+ before do
+ options '/oauth/token', headers: headers
+ end
+
+ it_behaves_like 'CORS preflight OPTIONS request'
+ end
+
+ describe 'POST /oauth/revoke' do
+ let(:other_headers) { { 'Content-Type' => 'application/x-www-form-urlencoded' } }
+
+ before do
+ post '/oauth/revoke', headers: headers, params: { token: '12345' }
+ end
+
+ it 'returns 200' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it_behaves_like 'cross-origin POST request'
+ end
+
+ describe 'OPTIONS /oauth/revoke' do
+ let(:other_headers) { { 'Access-Control-Request-Headers' => 'Authorization', 'Access-Control-Request-Method' => 'POST' } }
+
+ before do
+ options '/oauth/revoke', headers: headers
+ end
+
+ it_behaves_like 'CORS preflight OPTIONS request'
end
end
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 7b682d76150..5bf786f2290 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -41,6 +41,8 @@ RSpec.describe 'OpenID Connect requests' do
}
end
+ let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } }
+
def request_access_token!
login_as user
@@ -81,6 +83,24 @@ RSpec.describe 'OpenID Connect requests' do
end
end
+ shared_examples 'cross-origin GET request' do
+ it 'allows cross-origin request' do
+ expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, HEAD'
+ expect(response.headers['Access-Control-Allow-Headers']).to be_nil
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
+ end
+ end
+
+ shared_examples 'cross-origin GET and POST request' do
+ it 'allows cross-origin request' do
+ expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
+ expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, HEAD, POST'
+ expect(response.headers['Access-Control-Allow-Headers']).to be_nil
+ expect(response.headers['Access-Control-Allow-Credentials']).to be_nil
+ end
+ end
+
context 'Application with OpenID scope' do
let(:application) { create :oauth_application, scopes: 'openid' }
@@ -180,6 +200,51 @@ RSpec.describe 'OpenID Connect requests' do
expect(response).to redirect_to('/users/sign_in')
end
end
+
+ context 'OpenID Discovery keys' do
+ context 'with a cross-origin request' do
+ before do
+ get '/oauth/discovery/keys', headers: cors_request_headers
+ end
+
+ it 'returns data' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it_behaves_like 'cross-origin GET request'
+ end
+
+ context 'with a cross-origin preflight OPTIONS request' do
+ before do
+ options '/oauth/discovery/keys', headers: cors_request_headers
+ end
+
+ it_behaves_like 'cross-origin GET request'
+ end
+ end
+
+ context 'OpenID WebFinger endpoint' do
+ context 'with a cross-origin request' do
+ before do
+ get '/.well-known/webfinger', headers: cors_request_headers, params: { resource: 'user@example.com' }
+ end
+
+ it 'returns data' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['subject']).to eq('user@example.com')
+ end
+
+ it_behaves_like 'cross-origin GET request'
+ end
+ end
+
+ context 'with a cross-origin preflight OPTIONS request' do
+ before do
+ options '/.well-known/webfinger', headers: cors_request_headers, params: { resource: 'user@example.com' }
+ end
+
+ it_behaves_like 'cross-origin GET request'
+ end
end
context 'OpenID configuration information' do
@@ -191,6 +256,27 @@ RSpec.describe 'OpenID Connect requests' do
expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys')
expect(json_response['scopes_supported']).to eq(%w[api read_user read_api read_repository write_repository sudo openid profile email])
end
+
+ context 'with a cross-origin request' do
+ before do
+ get '/.well-known/openid-configuration', headers: cors_request_headers
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['issuer']).to eq('http://localhost')
+ expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys')
+ expect(json_response['scopes_supported']).to eq(%w[api read_user read_api read_repository write_repository sudo openid profile email])
+ end
+
+ it_behaves_like 'cross-origin GET request'
+ end
+
+ context 'with a cross-origin preflight OPTIONS request' do
+ before do
+ options '/.well-known/openid-configuration', headers: cors_request_headers
+ end
+
+ it_behaves_like 'cross-origin GET request'
+ end
end
context 'Application with OpenID and email scopes' do
@@ -218,6 +304,30 @@ RSpec.describe 'OpenID Connect requests' do
it 'has true in email_verified claim' do
expect(json_response['email_verified']).to eq(true)
end
+
+ context 'with a cross-origin request' do
+ before do
+ get '/oauth/userinfo', headers: cors_request_headers
+ end
+
+ it_behaves_like 'cross-origin GET and POST request'
+ end
+
+ context 'with a cross-origin POST request' do
+ before do
+ post '/oauth/userinfo', headers: cors_request_headers
+ end
+
+ it_behaves_like 'cross-origin GET and POST request'
+ end
+
+ context 'with a cross-origin preflight OPTIONS request' do
+ before do
+ options '/oauth/userinfo', headers: cors_request_headers
+ end
+
+ it_behaves_like 'cross-origin GET and POST request'
+ end
end
context 'ID token payload' do
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index f092cbf26a4..5a38f92221f 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -675,48 +675,6 @@ RSpec.describe UsersController do
end
end
- describe 'GET #suggests' do
- context 'when user exists' do
- it 'returns JSON indicating the user exists and a suggestion' do
- get user_suggests_url user.username
-
- expected_json = { exists: true, suggests: ["#{user.username}1"] }.to_json
- expect(response.body).to eq(expected_json)
- end
-
- context 'when the casing is different' do
- let(:user) { create(:user, username: 'CamelCaseUser') }
-
- it 'returns JSON indicating the user exists and a suggestion' do
- get user_suggests_url user.username.downcase
-
- expected_json = { exists: true, suggests: ["#{user.username.downcase}1"] }.to_json
- expect(response.body).to eq(expected_json)
- end
- end
- end
-
- context 'when the user does not exist' do
- it 'returns JSON indicating the user does not exist' do
- get user_suggests_url 'foo'
-
- expected_json = { exists: false, suggests: [] }.to_json
- expect(response.body).to eq(expected_json)
- end
-
- context 'when a user changed their username' do
- let(:redirect_route) { user.namespace.redirect_routes.create!(path: 'old-username') }
-
- it 'returns JSON indicating a user by that username does not exist' do
- get user_suggests_url 'old-username'
-
- expected_json = { exists: false, suggests: [] }.to_json
- expect(response.body).to eq(expected_json)
- end
- end
- end
- end
-
describe '#ensure_canonical_path' do
before do
sign_in(user)