Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-03-18 23:02:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-18 23:02:30 +0300
commit41fe97390ceddf945f3d967b8fdb3de4c66b7dea (patch)
tree9c8d89a8624828992f06d892cd2f43818ff5dcc8 /spec/requests/api
parent0804d2dc31052fb45a1efecedc8e06ce9bc32862 (diff)
Add latest changes from gitlab-org/gitlab@14-9-stable-eev14.9.0-rc42
Diffstat (limited to 'spec/requests/api')
-rw-r--r--spec/requests/api/admin/instance_clusters_spec.rb20
-rw-r--r--spec/requests/api/broadcast_messages_spec.rb23
-rw-r--r--spec/requests/api/ci/jobs_spec.rb51
-rw-r--r--spec/requests/api/ci/pipelines_spec.rb17
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb6
-rw-r--r--spec/requests/api/ci/runner/jobs_trace_spec.rb8
-rw-r--r--spec/requests/api/ci/runner/runners_post_spec.rb8
-rw-r--r--spec/requests/api/ci/runner/runners_reset_spec.rb65
-rw-r--r--spec/requests/api/ci/runners_spec.rb8
-rw-r--r--spec/requests/api/ci/secure_files_spec.rb187
-rw-r--r--spec/requests/api/commits_spec.rb9
-rw-r--r--spec/requests/api/container_repositories_spec.rb64
-rw-r--r--spec/requests/api/deploy_tokens_spec.rb106
-rw-r--r--spec/requests/api/error_tracking/collector_spec.rb15
-rw-r--r--spec/requests/api/error_tracking/project_settings_spec.rb59
-rw-r--r--spec/requests/api/generic_packages_spec.rb11
-rw-r--r--spec/requests/api/graphql/ci/pipelines_spec.rb33
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb33
-rw-r--r--spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb57
-rw-r--r--spec/requests/api/graphql/container_repository/container_repository_details_spec.rb80
-rw-r--r--spec/requests/api/graphql/group/group_members_spec.rb46
-rw-r--r--spec/requests/api/graphql/group/issues_spec.rb25
-rw-r--r--spec/requests/api/graphql/group/merge_requests_spec.rb21
-rw-r--r--spec/requests/api/graphql/group/work_item_types_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb14
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb33
-rw-r--r--spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb87
-rw-r--r--spec/requests/api/graphql/namespace_query_spec.rb3
-rw-r--r--spec/requests/api/graphql/project/jira_service_spec.rb9
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb18
-rw-r--r--spec/requests/api/graphql/project/work_item_types_spec.rb4
-rw-r--r--spec/requests/api/graphql/query_spec.rb24
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb75
-rw-r--r--spec/requests/api/group_clusters_spec.rb20
-rw-r--r--spec/requests/api/group_labels_spec.rb10
-rw-r--r--spec/requests/api/integrations_spec.rb10
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb1
-rw-r--r--spec/requests/api/internal/mail_room_spec.rb16
-rw-r--r--spec/requests/api/invitations_spec.rb21
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb22
-rw-r--r--spec/requests/api/labels_spec.rb22
-rw-r--r--spec/requests/api/members_spec.rb6
-rw-r--r--spec/requests/api/notes_spec.rb89
-rw-r--r--spec/requests/api/project_attributes.yml2
-rw-r--r--spec/requests/api/project_clusters_spec.rb20
-rw-r--r--spec/requests/api/project_import_spec.rb87
-rw-r--r--spec/requests/api/project_snippets_spec.rb4
-rw-r--r--spec/requests/api/projects_spec.rb34
-rw-r--r--spec/requests/api/pypi_packages_spec.rb8
-rw-r--r--spec/requests/api/releases_spec.rb6
-rw-r--r--spec/requests/api/repositories_spec.rb7
-rw-r--r--spec/requests/api/search_spec.rb11
-rw-r--r--spec/requests/api/snippets_spec.rb4
-rw-r--r--spec/requests/api/system_hooks_spec.rb49
-rw-r--r--spec/requests/api/terraform/state_spec.rb4
-rw-r--r--spec/requests/api/topics_spec.rb52
-rw-r--r--spec/requests/api/user_counts_spec.rb15
-rw-r--r--spec/requests/api/users_spec.rb58
-rw-r--r--spec/requests/api/wikis_spec.rb54
60 files changed, 1578 insertions, 279 deletions
diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb
index ab3b6b718e1..7b3224f58c5 100644
--- a/spec/requests/api/admin/instance_clusters_spec.rb
+++ b/spec/requests/api/admin/instance_clusters_spec.rb
@@ -21,6 +21,10 @@ RSpec.describe ::API::Admin::InstanceClusters do
create_list(:cluster, 3, :provided_by_gcp, :instance, :production_environment)
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api("/admin/clusters", admin_user) }
+ end
+
context "when authenticated as a non-admin user" do
it 'returns 403' do
get api('/admin/clusters', regular_user)
@@ -62,6 +66,10 @@ RSpec.describe ::API::Admin::InstanceClusters do
let(:cluster_id) { cluster.id }
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api("/admin/clusters/#{cluster_id}", admin_user) }
+ end
+
context "when authenticated as admin" do
before do
get api("/admin/clusters/#{cluster_id}", admin_user)
@@ -188,6 +196,10 @@ RSpec.describe ::API::Admin::InstanceClusters do
}
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { post api('/admin/clusters/add', admin_user), params: cluster_params }
+ end
+
context 'authorized user' do
before do
post api('/admin/clusters/add', admin_user), params: cluster_params
@@ -317,6 +329,10 @@ RSpec.describe ::API::Admin::InstanceClusters do
create(:cluster, :instance, :provided_by_gcp, domain: 'old-domain.com')
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { put api("/admin/clusters/#{cluster.id}", admin_user), params: update_params }
+ end
+
context 'authorized user' do
before do
put api("/admin/clusters/#{cluster.id}", admin_user), params: update_params
@@ -448,6 +464,10 @@ RSpec.describe ::API::Admin::InstanceClusters do
create(:cluster, :instance, :provided_by_gcp)
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { delete api("/admin/clusters/#{cluster.id}", admin_user), params: cluster_params }
+ end
+
context 'authorized user' do
before do
delete api("/admin/clusters/#{cluster.id}", admin_user), params: cluster_params
diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb
index b023ec398a2..76412c80f4c 100644
--- a/spec/requests/api/broadcast_messages_spec.rb
+++ b/spec/requests/api/broadcast_messages_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe API::BroadcastMessages do
expect(response).to include_pagination_headers
expect(json_response).to be_kind_of(Array)
expect(json_response.first.keys)
- .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
+ .to match_array(%w(id message starts_at ends_at color font active target_access_levels target_path broadcast_type dismissable))
end
end
@@ -28,7 +28,7 @@ RSpec.describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq message.id
expect(json_response.keys)
- .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
+ .to match_array(%w(id message starts_at ends_at color font active target_access_levels target_path broadcast_type dismissable))
end
end
@@ -77,6 +77,16 @@ RSpec.describe API::BroadcastMessages do
expect(json_response['font']).to eq attrs[:font]
end
+ it 'accepts target access levels' do
+ target_access_levels = [Gitlab::Access::GUEST, Gitlab::Access::DEVELOPER]
+ attrs = attributes_for(:broadcast_message, target_access_levels: target_access_levels)
+
+ post api('/broadcast_messages', admin), params: attrs
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['target_access_levels']).to eq attrs[:target_access_levels]
+ end
+
it 'accepts a target path' do
attrs = attributes_for(:broadcast_message, target_path: "*/welcome")
@@ -171,6 +181,15 @@ RSpec.describe API::BroadcastMessages do
expect { message.reload }.to change { message.message }.to('new message')
end
+ it 'accepts a new target_access_levels' do
+ attrs = { target_access_levels: [Gitlab::Access::MAINTAINER] }
+
+ put api("/broadcast_messages/#{message.id}", admin), params: attrs
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['target_access_levels']).to eq attrs[:target_access_levels]
+ end
+
it 'accepts a new target_path' do
attrs = { target_path: '*/welcome' }
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 7c85cbc31a5..f6dae7e8e23 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -707,12 +707,14 @@ RSpec.describe API::Ci::Jobs do
end
describe 'POST /projects/:id/jobs/:job_id/play' do
+ let(:params) { {} }
+
before do
- post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user), params: params
end
context 'on a playable job' do
- let_it_be(:job) { create(:ci_bridge, :playable, pipeline: pipeline, downstream: project) }
+ let_it_be(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
before do
project.add_developer(user)
@@ -720,6 +722,8 @@ RSpec.describe API::Ci::Jobs do
context 'when user is authorized to trigger a manual action' do
context 'that is a bridge' do
+ let_it_be(:job) { create(:ci_bridge, :playable, pipeline: pipeline, downstream: project) }
+
it 'plays the job' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['user']['id']).to eq(user.id)
@@ -729,8 +733,6 @@ RSpec.describe API::Ci::Jobs do
end
context 'that is a build' do
- let_it_be(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
-
it 'plays the job' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['user']['id']).to eq(user.id)
@@ -738,6 +740,47 @@ RSpec.describe API::Ci::Jobs do
expect(job.reload).to be_pending
end
end
+
+ context 'when the user provides valid custom variables' do
+ let(:params) { { job_variables_attributes: [{ key: 'TEST_VAR', value: 'test' }] } }
+
+ it 'applies the variables to the job' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(job.reload).to be_pending
+ expect(job.job_variables.map(&:key)).to contain_exactly('TEST_VAR')
+ expect(job.job_variables.map(&:value)).to contain_exactly('test')
+ end
+ end
+
+ context 'when the user provides a variable without a key' do
+ let(:params) { { job_variables_attributes: [{ value: 'test' }] } }
+
+ it 'reports that the key is missing' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('job_variables_attributes[0][key] is missing')
+ expect(job.reload).to be_manual
+ end
+ end
+
+ context 'when the user provides a variable without a value' do
+ let(:params) { { job_variables_attributes: [{ key: 'TEST_VAR' }] } }
+
+ it 'reports that the value is missing' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('job_variables_attributes[0][value] is missing')
+ expect(job.reload).to be_manual
+ end
+ end
+
+ context 'when the user provides both valid and invalid variables' do
+ let(:params) { { job_variables_attributes: [{ key: 'TEST_VAR', value: 'test' }, { value: 'test2' }] } }
+
+ it 'reports the invalid variables and does not run the job' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('job_variables_attributes[1][key] is missing')
+ expect(job.reload).to be_manual
+ end
+ end
end
context 'when user is not authorized to trigger a manual action' do
diff --git a/spec/requests/api/ci/pipelines_spec.rb b/spec/requests/api/ci/pipelines_spec.rb
index 1b87a5e24f5..12faeec94da 100644
--- a/spec/requests/api/ci/pipelines_spec.rb
+++ b/spec/requests/api/ci/pipelines_spec.rb
@@ -1075,6 +1075,23 @@ RSpec.describe API::Ci::Pipelines do
expect(json_response['id']).to be nil
end
end
+
+ context 'handles errors' do
+ before do
+ service_response = ServiceResponse.error(http_status: 403, message: 'hello world')
+ allow_next_instance_of(::Ci::RetryPipelineService) do |service|
+ allow(service).to receive(:check_access).and_return(service_response)
+ end
+ end
+
+ it 'returns error' do
+ post api("/projects/#{project.id}/pipelines/#{pipeline.id}/retry", user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq 'hello world'
+ expect(json_response['id']).to be nil
+ end
+ end
end
describe 'POST /projects/:id/pipelines/:pipeline_id/cancel' do
diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
index 68f7581bf06..d317386dc73 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -156,7 +156,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
'sha' => job.sha,
'before_sha' => job.before_sha,
'ref_type' => 'branch',
- 'refspecs' => ["+#{pipeline.sha}:refs/pipelines/#{pipeline.id}",
+ 'refspecs' => ["+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
"+refs/heads/#{job.ref}:refs/remotes/origin/#{job.ref}"],
'depth' => project.ci_default_git_depth }
end
@@ -291,7 +291,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['git_info']['refspecs'])
- .to contain_exactly("+#{pipeline.sha}:refs/pipelines/#{pipeline.id}",
+ .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
'+refs/tags/*:refs/tags/*',
'+refs/heads/*:refs/remotes/origin/*')
end
@@ -359,7 +359,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['git_info']['refspecs'])
- .to contain_exactly("+#{pipeline.sha}:refs/pipelines/#{pipeline.id}",
+ .to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
'+refs/tags/*:refs/tags/*',
'+refs/heads/*:refs/remotes/origin/*')
end
diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb
index 2760e306693..d6928969beb 100644
--- a/spec/requests/api/ci/runner/jobs_trace_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do
let(:headers) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } }
let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) }
- let(:update_interval) { 10.seconds.to_i }
+ let(:update_interval) { 10.seconds }
before do
initial_patch_the_trace
@@ -81,7 +81,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do
end
context 'when job was not updated recently' do
- let(:update_interval) { 15.minutes.to_i }
+ let(:update_interval) { 16.minutes }
it { expect { patch_the_trace }.to change { job.updated_at } }
@@ -293,10 +293,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do
end
end
- Timecop.travel(job.updated_at + update_interval) do
+ travel_to(job.updated_at + update_interval) do
patch api("/jobs/#{job_id}/trace"), params: content, headers: request_headers
- job.reload
end
+ job.reload
end
def initial_patch_the_trace
diff --git a/spec/requests/api/ci/runner/runners_post_spec.rb b/spec/requests/api/ci/runner/runners_post_spec.rb
index 5eb5d3977a3..1d553751eea 100644
--- a/spec/requests/api/ci/runner/runners_post_spec.rb
+++ b/spec/requests/api/ci/runner/runners_post_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when invalid token is provided' do
it 'returns 403 error' do
- allow_next_instance_of(::Ci::RegisterRunnerService) do |service|
+ allow_next_instance_of(::Ci::Runners::RegisterRunnerService) do |service|
allow(service).to receive(:execute).and_return(nil)
end
@@ -43,7 +43,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let_it_be(:new_runner) { create(:ci_runner) }
before do
- allow_next_instance_of(::Ci::RegisterRunnerService) do |service|
+ allow_next_instance_of(::Ci::Runners::RegisterRunnerService) do |service|
expected_params = {
description: 'server.hostname',
maintenance_note: 'Some maintainer notes',
@@ -108,7 +108,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let(:new_runner) { create(:ci_runner) }
it 'converts to maintenance_note param' do
- allow_next_instance_of(::Ci::RegisterRunnerService) do |service|
+ allow_next_instance_of(::Ci::Runners::RegisterRunnerService) do |service|
expect(service).to receive(:execute)
.once
.with('valid token', a_hash_including('maintenance_note' => 'Some maintainer notes')
@@ -133,7 +133,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let_it_be(:new_runner) { create(:ci_runner) }
it 'uses active value in registration' do
- expect_next_instance_of(::Ci::RegisterRunnerService) do |service|
+ expect_next_instance_of(::Ci::Runners::RegisterRunnerService) do |service|
expected_params = { active: false }.stringify_keys
expect(service).to receive(:execute)
diff --git a/spec/requests/api/ci/runner/runners_reset_spec.rb b/spec/requests/api/ci/runner/runners_reset_spec.rb
new file mode 100644
index 00000000000..8a61012ead1
--- /dev/null
+++ b/spec/requests/api/ci/runner/runners_reset_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
+ include StubGitlabCalls
+ include RedisHelpers
+ include WorkhorseHelpers
+
+ before do
+ stub_feature_flags(ci_enable_live_trace: true)
+ stub_feature_flags(runner_registration_control: false)
+ stub_gitlab_calls
+ stub_application_setting(valid_runner_registrars: ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES)
+ end
+
+ let_it_be(:group_settings) { create(:namespace_settings, runner_token_expiration_interval: 5.days.to_i) }
+ let_it_be(:group) { create(:group, namespace_settings: group_settings) }
+ let_it_be(:instance_runner, reload: true) { create(:ci_runner, :instance) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], token_expires_at: 1.day.from_now) }
+
+ describe 'POST /runners/reset_authentication_token', :freeze_time do
+ context 'current token provided' do
+ it "resets authentication token when token doesn't have an expiration" do
+ expect do
+ post api("/runners/reset_authentication_token"), params: { token: instance_runner.reload.token }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response).to eq({ 'token' => instance_runner.reload.token, 'token_expires_at' => nil })
+ expect(instance_runner.reload.token_expires_at).to be_nil
+ end.to change { instance_runner.reload.token }
+ end
+
+ it 'resets authentication token when token is not expired' do
+ expect do
+ post api("/runners/reset_authentication_token"), params: { token: group_runner.reload.token }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response).to eq({ 'token' => group_runner.reload.token, 'token_expires_at' => group_runner.reload.token_expires_at.iso8601(3) })
+ expect(group_runner.reload.token_expires_at).to eq(5.days.from_now)
+ end.to change { group_runner.reload.token }
+ end
+
+ it 'does not reset authentication token when token is expired' do
+ expect do
+ instance_runner.update!(token_expires_at: 1.day.ago)
+ post api("/runners/reset_authentication_token"), params: { token: instance_runner.reload.token }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ instance_runner.update!(token_expires_at: nil)
+ end.not_to change { instance_runner.reload.token }
+ end
+ end
+
+ context 'wrong current token provided' do
+ it 'does not reset authentication token' do
+ expect do
+ post api("/runners/reset_authentication_token"), params: { token: 'garbage' }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end.not_to change { instance_runner.reload.token }
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 336ce70d8d2..a1fda68b77b 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -530,7 +530,7 @@ RSpec.describe API::Ci::Runners do
context 'admin user' do
context 'when runner is shared' do
it 'deletes runner' do
- expect_next_instance_of(Ci::UnregisterRunnerService, shared_runner) do |service|
+ expect_next_instance_of(Ci::Runners::UnregisterRunnerService, shared_runner, admin) do |service|
expect(service).to receive(:execute).once.and_call_original
end
@@ -548,7 +548,7 @@ RSpec.describe API::Ci::Runners do
context 'when runner is not shared' do
it 'deletes used project runner' do
- expect_next_instance_of(Ci::UnregisterRunnerService, project_runner) do |service|
+ expect_next_instance_of(Ci::Runners::UnregisterRunnerService, project_runner, admin) do |service|
expect(service).to receive(:execute).once.and_call_original
end
@@ -561,7 +561,7 @@ RSpec.describe API::Ci::Runners do
end
it 'returns 404 if runner does not exist' do
- allow_next_instance_of(Ci::UnregisterRunnerService) do |service|
+ allow_next_instance_of(Ci::Runners::UnregisterRunnerService) do |service|
expect(service).not_to receive(:execute)
end
@@ -646,7 +646,7 @@ RSpec.describe API::Ci::Runners do
context 'unauthorized user' do
it 'does not delete project runner' do
- allow_next_instance_of(Ci::UnregisterRunnerService) do |service|
+ allow_next_instance_of(Ci::Runners::UnregisterRunnerService) do |service|
expect(service).not_to receive(:execute)
end
diff --git a/spec/requests/api/ci/secure_files_spec.rb b/spec/requests/api/ci/secure_files_spec.rb
index 5cf6999f60a..aa479cb8713 100644
--- a/spec/requests/api/ci/secure_files_spec.rb
+++ b/spec/requests/api/ci/secure_files_spec.rb
@@ -8,49 +8,72 @@ RSpec.describe API::Ci::SecureFiles do
stub_feature_flags(ci_secure_files: true)
end
- let_it_be(:user) { create(:user) }
- let_it_be(:user2) { create(:user) }
- let_it_be(:project) { create(:project, creator_id: user.id) }
- let_it_be(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
- let_it_be(:developer) { create(:project_member, :developer, user: user2, project: project) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:anonymous) { create(:user) }
+ let_it_be(:project) { create(:project, creator_id: maintainer.id) }
let_it_be(:secure_file) { create(:ci_secure_file, project: project) }
+ before_all do
+ project.add_maintainer(maintainer)
+ project.add_developer(developer)
+ project.add_guest(guest)
+ end
+
describe 'GET /projects/:id/secure_files' do
context 'feature flag' do
it 'returns a 503 when the feature flag is disabled' do
stub_feature_flags(ci_secure_files: false)
- get api("/projects/#{project.id}/secure_files", user)
+ get api("/projects/#{project.id}/secure_files", maintainer)
expect(response).to have_gitlab_http_status(:service_unavailable)
end
it 'returns a 200 when the feature flag is enabled' do
- get api("/projects/#{project.id}/secure_files", user)
+ get api("/projects/#{project.id}/secure_files", maintainer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to be_a(Array)
+ end
+ end
+
+ context 'authenticated user with admin permissions' do
+ it 'returns project secure files' do
+ get api("/projects/#{project.id}/secure_files", maintainer)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a(Array)
end
end
- context 'authorized user with proper permissions' do
+ context 'authenticated user with read permissions' do
it 'returns project secure files' do
- get api("/projects/#{project.id}/secure_files", user)
+ get api("/projects/#{project.id}/secure_files", developer)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a(Array)
end
end
- context 'authorized user with invalid permissions' do
+ context 'authenticated user with guest permissions' do
it 'does not return project secure files' do
- get api("/projects/#{project.id}/secure_files", user2)
+ get api("/projects/#{project.id}/secure_files", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
- context 'unauthorized user' do
+ context 'authenticated user with no permissions' do
+ it 'does not return project secure files' do
+ get api("/projects/#{project.id}/secure_files", anonymous)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
it 'does not return project secure files' do
get api("/projects/#{project.id}/secure_files")
@@ -60,9 +83,9 @@ RSpec.describe API::Ci::SecureFiles do
end
describe 'GET /projects/:id/secure_files/:secure_file_id' do
- context 'authorized user with proper permissions' do
+ context 'authenticated user with admin permissions' do
it 'returns project secure file details' do
- get api("/projects/#{project.id}/secure_files/#{secure_file.id}", user)
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}", maintainer)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(secure_file.name)
@@ -70,21 +93,31 @@ RSpec.describe API::Ci::SecureFiles do
end
it 'responds with 404 Not Found if requesting non-existing secure file' do
- get api("/projects/#{project.id}/secure_files/99999", user)
+ get api("/projects/#{project.id}/secure_files/#{non_existing_record_id}", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'authorized user with invalid permissions' do
+ context 'authenticated user with read permissions' do
+ it 'returns project secure file details' do
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}", developer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['name']).to eq(secure_file.name)
+ expect(json_response['permissions']).to eq(secure_file.permissions)
+ end
+ end
+
+ context 'authenticated user with no permissions' do
it 'does not return project secure file details' do
- get api("/projects/#{project.id}/secure_files/#{secure_file.id}", user2)
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}", anonymous)
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'unauthorized user' do
+ context 'unauthenticated user' do
it 'does not return project secure file details' do
get api("/projects/#{project.id}/secure_files/#{secure_file.id}")
@@ -94,34 +127,47 @@ RSpec.describe API::Ci::SecureFiles do
end
describe 'GET /projects/:id/secure_files/:secure_file_id/download' do
- context 'authorized user with proper permissions' do
+ context 'authenticated user with admin permissions' do
it 'returns a secure file' do
sample_file = fixture_file('ci_secure_files/upload-keystore.jks')
secure_file.file = CarrierWaveStringFile.new(sample_file)
secure_file.save!
- get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", user)
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", maintainer)
expect(response).to have_gitlab_http_status(:ok)
expect(Base64.encode64(response.body)).to eq(Base64.encode64(sample_file))
end
it 'responds with 404 Not Found if requesting non-existing secure file' do
- get api("/projects/#{project.id}/secure_files/99999/download", user)
+ get api("/projects/#{project.id}/secure_files/#{non_existing_record_id}/download", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'authorized user with invalid permissions' do
+ context 'authenticated user with read permissions' do
+ it 'returns a secure file' do
+ sample_file = fixture_file('ci_secure_files/upload-keystore.jks')
+ secure_file.file = CarrierWaveStringFile.new(sample_file)
+ secure_file.save!
+
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", developer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(Base64.encode64(response.body)).to eq(Base64.encode64(sample_file))
+ end
+ end
+
+ context 'authenticated user with no permissions' do
it 'does not return project secure file details' do
- get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", user2)
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", anonymous)
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'unauthorized user' do
+ context 'unauthenticated user' do
it 'does not return project secure file details' do
get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download")
@@ -131,7 +177,7 @@ RSpec.describe API::Ci::SecureFiles do
end
describe 'POST /projects/:id/secure_files' do
- context 'authorized user with proper permissions' do
+ context 'authenticated user with admin permissions' do
it 'creates a secure file' do
params = {
file: fixture_file_upload('spec/fixtures/ci_secure_files/upload-keystore.jks'),
@@ -140,7 +186,7 @@ RSpec.describe API::Ci::SecureFiles do
}
expect do
- post api("/projects/#{project.id}/secure_files", user), params: params
+ post api("/projects/#{project.id}/secure_files", maintainer), params: params
end.to change {project.secure_files.count}.by(1)
expect(response).to have_gitlab_http_status(:created)
@@ -154,6 +200,7 @@ RSpec.describe API::Ci::SecureFiles do
Digest::SHA256.hexdigest(fixture_file('ci_secure_files/upload-keystore.jks'))
)
expect(json_response['id']).to eq(secure_file.id)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(secure_file.created_at)
end
it 'creates a secure file with read_only permissions by default' do
@@ -163,7 +210,7 @@ RSpec.describe API::Ci::SecureFiles do
}
expect do
- post api("/projects/#{project.id}/secure_files", user), params: params
+ post api("/projects/#{project.id}/secure_files", maintainer), params: params
end.to change {project.secure_files.count}.by(1)
expect(json_response['permissions']).to eq('read_only')
@@ -176,11 +223,11 @@ RSpec.describe API::Ci::SecureFiles do
permissions: 'read_write'
}
- post api("/projects/#{project.id}/secure_files", user), params: post_params
+ post api("/projects/#{project.id}/secure_files", maintainer), params: post_params
secure_file_id = json_response['id']
- get api("/projects/#{project.id}/secure_files/#{secure_file_id}/download", user)
+ get api("/projects/#{project.id}/secure_files/#{secure_file_id}/download", maintainer)
expect(Base64.encode64(response.body)).to eq(Base64.encode64(fixture_file_upload('spec/fixtures/ci_secure_files/upload-keystore.jks').read))
end
@@ -188,7 +235,9 @@ RSpec.describe API::Ci::SecureFiles do
it 'returns an error when the file checksum fails to validate' do
secure_file.update!(checksum: 'foo')
- get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", user)
+ expect do
+ get api("/projects/#{project.id}/secure_files/#{secure_file.id}/download", maintainer)
+ end.not_to change { project.secure_files.count }
expect(response.code).to eq("500")
end
@@ -198,7 +247,9 @@ RSpec.describe API::Ci::SecureFiles do
name: 'upload-keystore.jks'
}
- post api("/projects/#{project.id}/secure_files", user), params: post_params
+ expect do
+ post api("/projects/#{project.id}/secure_files", maintainer), params: post_params
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('file is missing')
@@ -209,7 +260,9 @@ RSpec.describe API::Ci::SecureFiles do
file: fixture_file_upload('spec/fixtures/ci_secure_files/upload-keystore.jks')
}
- post api("/projects/#{project.id}/secure_files", user), params: post_params
+ expect do
+ post api("/projects/#{project.id}/secure_files", maintainer), params: post_params
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('name is missing')
@@ -222,7 +275,9 @@ RSpec.describe API::Ci::SecureFiles do
permissions: 'foo'
}
- post api("/projects/#{project.id}/secure_files", user), params: post_params
+ expect do
+ post api("/projects/#{project.id}/secure_files", maintainer), params: post_params
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('permissions does not have a valid value')
@@ -240,7 +295,9 @@ RSpec.describe API::Ci::SecureFiles do
name: 'upload-keystore.jks'
}
- post api("/projects/#{project.id}/secure_files", user), params: post_params
+ expect do
+ post api("/projects/#{project.id}/secure_files", maintainer), params: post_params
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -255,23 +312,39 @@ RSpec.describe API::Ci::SecureFiles do
name: 'upload-keystore.jks'
}
- post api("/projects/#{project.id}/secure_files", user), params: post_params
+ expect do
+ post api("/projects/#{project.id}/secure_files", maintainer), params: post_params
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:payload_too_large)
end
end
- context 'authorized user with invalid permissions' do
+ context 'authenticated user with read permissions' do
it 'does not create a secure file' do
- post api("/projects/#{project.id}/secure_files", user2)
+ expect do
+ post api("/projects/#{project.id}/secure_files", developer)
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
- context 'unauthorized user' do
+ context 'authenticated user with no permissions' do
it 'does not create a secure file' do
- post api("/projects/#{project.id}/secure_files")
+ expect do
+ post api("/projects/#{project.id}/secure_files", anonymous)
+ end.not_to change { project.secure_files.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not create a secure file' do
+ expect do
+ post api("/projects/#{project.id}/secure_files")
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -279,33 +352,49 @@ RSpec.describe API::Ci::SecureFiles do
end
describe 'DELETE /projects/:id/secure_files/:secure_file_id' do
- context 'authorized user with proper permissions' do
+ context 'authenticated user with admin permissions' do
it 'deletes the secure file' do
expect do
- delete api("/projects/#{project.id}/secure_files/#{secure_file.id}", user)
+ delete api("/projects/#{project.id}/secure_files/#{secure_file.id}", maintainer)
expect(response).to have_gitlab_http_status(:no_content)
- end.to change {project.secure_files.count}.by(-1)
+ end.to change { project.secure_files.count }
end
it 'responds with 404 Not Found if requesting non-existing secure_file' do
- delete api("/projects/#{project.id}/secure_files/99999", user)
+ expect do
+ delete api("/projects/#{project.id}/secure_files/#{non_existing_record_id}", maintainer)
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'authorized user with invalid permissions' do
+ context 'authenticated user with read permissions' do
it 'does not delete the secure_file' do
- delete api("/projects/#{project.id}/secure_files/#{secure_file.id}", user2)
+ expect do
+ delete api("/projects/#{project.id}/secure_files/#{secure_file.id}", developer)
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
- context 'unauthorized user' do
+ context 'authenticated user with no permissions' do
it 'does not delete the secure_file' do
- delete api("/projects/#{project.id}/secure_files/#{secure_file.id}")
+ expect do
+ delete api("/projects/#{project.id}/secure_files/#{secure_file.id}", anonymous)
+ end.not_to change { project.secure_files.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'does not delete the secure_file' do
+ expect do
+ delete api("/projects/#{project.id}/secure_files/#{secure_file.id}")
+ end.not_to change { project.secure_files.count }
expect(response).to have_gitlab_http_status(:unauthorized)
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 156a4cf5ff3..67c2ec91540 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -127,6 +127,15 @@ RSpec.describe API::Commits do
it_behaves_like 'project commits'
end
+ context 'when repository does not exist' do
+ let(:project) { create(:project, creator: user, path: 'my.project') }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Repository Not Found' }
+ end
+ end
+
context "path optional parameter" do
it "returns project commits matching provided path parameter" do
path = 'files/ruby/popen.rb'
diff --git a/spec/requests/api/container_repositories_spec.rb b/spec/requests/api/container_repositories_spec.rb
index 9809702467d..90f0243dbfc 100644
--- a/spec/requests/api/container_repositories_spec.rb
+++ b/spec/requests/api/container_repositories_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe API::ContainerRepositories do
+ include_context 'container registry client stubs'
+
let_it_be(:project) { create(:project, :private) }
let_it_be(:reporter) { create(:user) }
let_it_be(:guest) { create(:user) }
@@ -103,6 +105,68 @@ RSpec.describe API::ContainerRepositories do
expect(json_response['tags_count']).to eq(2)
end
end
+
+ context 'with size param' do
+ let(:url) { "/registry/repositories/#{repository.id}?size=true" }
+ let(:on_com) { true }
+ let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT + 3.months }
+
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(on_com)
+ repository.update_column(:created_at, created_at)
+ end
+
+ it 'returns a repository and its size' do
+ stub_container_registry_gitlab_api_support(supported: true) do |client|
+ stub_container_registry_gitlab_api_repository_details(client, path: repository.path, size_bytes: 12345)
+ end
+
+ subject
+
+ expect(json_response['size']).to eq(12345)
+ end
+
+ context 'with a network error' do
+ it 'returns an error message' do
+ stub_container_registry_gitlab_api_network_error
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:service_unavailable)
+ expect(json_response['message']).to include('We are having trouble connecting to the Container Registry')
+ end
+ end
+
+ context 'with not supporting the gitlab api' do
+ it 'returns nil' do
+ stub_container_registry_gitlab_api_support(supported: false)
+
+ subject
+
+ expect(json_response['size']).to eq(nil)
+ end
+ end
+
+ context 'not on .com' do
+ let(:on_com) { false }
+
+ it 'returns nil' do
+ subject
+
+ expect(json_response['size']).to eq(nil)
+ end
+ end
+
+ context 'with an older container repository' do
+ let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT - 3.months }
+
+ it 'returns nil' do
+ subject
+
+ expect(json_response['size']).to eq(nil)
+ end
+ end
+ end
end
context 'with invalid repository id' do
diff --git a/spec/requests/api/deploy_tokens_spec.rb b/spec/requests/api/deploy_tokens_spec.rb
index 38380fa4460..b5f8da1f327 100644
--- a/spec/requests/api/deploy_tokens_spec.rb
+++ b/spec/requests/api/deploy_tokens_spec.rb
@@ -130,6 +130,55 @@ RSpec.describe API::DeployTokens do
end
end
+ describe 'GET /projects/:id/deploy_tokens/:token_id' do
+ subject do
+ get api("/projects/#{project.id}/deploy_tokens/#{deploy_token.id}", user)
+ response
+ end
+
+ context 'when unauthenticated' do
+ let(:user) { nil }
+
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ context 'when authenticated as non-admin user' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
+ end
+
+ context 'when authenticated as maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it { is_expected.to have_gitlab_http_status(:ok) }
+
+ it 'returns specific deploy token for the project' do
+ subject
+
+ expect(response).to match_response_schema('public_api/v4/deploy_token')
+ end
+
+ context 'invalid request' do
+ it 'returns not found with invalid project id' do
+ get api("/projects/bad_id/deploy_tokens/#{deploy_token.id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns not found with invalid token id' do
+ get api("/projects/#{project.id}/deploy_tokens/#{non_existing_record_id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
describe 'GET /groups/:id/deploy_tokens' do
subject do
get api("/groups/#{group.id}/deploy_tokens", user)
@@ -188,6 +237,55 @@ RSpec.describe API::DeployTokens do
end
end
+ describe 'GET /groups/:id/deploy_tokens/:token_id' do
+ subject do
+ get api("/groups/#{group.id}/deploy_tokens/#{group_deploy_token.id}", user)
+ response
+ end
+
+ context 'when unauthenticated' do
+ let(:user) { nil }
+
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
+ end
+
+ context 'when authenticated as non-admin user' do
+ before do
+ group.add_developer(user)
+ end
+
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
+ end
+
+ context 'when authenticated as maintainer' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it { is_expected.to have_gitlab_http_status(:ok) }
+
+ it 'returns specific deploy token for the group' do
+ subject
+
+ expect(response).to match_response_schema('public_api/v4/deploy_token')
+ end
+
+ context 'invalid request' do
+ it 'returns not found with invalid group id' do
+ get api("/groups/bad_id/deploy_tokens/#{group_deploy_token.id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns not found with invalid token id' do
+ get api("/groups/#{group.id}/deploy_tokens/#{non_existing_record_id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
describe 'DELETE /projects/:id/deploy_tokens/:token_id' do
subject do
delete api("/projects/#{project.id}/deploy_tokens/#{deploy_token.id}", user)
@@ -232,10 +330,10 @@ RSpec.describe API::DeployTokens do
it 'returns bad_request with invalid token id' do
expect(::Projects::DeployTokens::DestroyService).to receive(:new)
- .with(project, user, token_id: 999)
+ .with(project, user, token_id: non_existing_record_id)
.and_raise(ActiveRecord::RecordNotFound)
- delete api("/projects/#{project.id}/deploy_tokens/999", user)
+ delete api("/projects/#{project.id}/deploy_tokens/#{non_existing_record_id}", user)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -395,10 +493,10 @@ RSpec.describe API::DeployTokens do
it 'returns not found with invalid deploy token id' do
expect(::Groups::DeployTokens::DestroyService).to receive(:new)
- .with(group, user, token_id: 999)
+ .with(group, user, token_id: non_existing_record_id)
.and_raise(ActiveRecord::RecordNotFound)
- delete api("/groups/#{group.id}/deploy_tokens/999", user)
+ delete api("/groups/#{group.id}/deploy_tokens/#{non_existing_record_id}", user)
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/requests/api/error_tracking/collector_spec.rb b/spec/requests/api/error_tracking/collector_spec.rb
index 573da862b57..fa0b238dcad 100644
--- a/spec/requests/api/error_tracking/collector_spec.rb
+++ b/spec/requests/api/error_tracking/collector_spec.rb
@@ -26,7 +26,6 @@ RSpec.describe API::ErrorTracking::Collector do
RSpec.shared_examples 'successful request' do
it 'writes to the database and returns OK', :aggregate_failures do
expect { subject }.to change { ErrorTracking::ErrorEvent.count }.by(1)
-
expect(response).to have_gitlab_http_status(:ok)
end
end
@@ -42,6 +41,14 @@ RSpec.describe API::ErrorTracking::Collector do
it_behaves_like 'successful request'
+ context 'intergrated error tracking feature flag is disabled' do
+ before do
+ stub_feature_flags(integrated_error_tracking: false)
+ end
+
+ it_behaves_like 'not found'
+ end
+
context 'error tracking feature is disabled' do
before do
setting.update!(enabled: false)
@@ -171,6 +178,12 @@ RSpec.describe API::ErrorTracking::Collector do
it_behaves_like 'successful request'
end
+ context 'when JSON key transaction is empty string' do
+ let_it_be(:raw_event) { fixture_file('error_tracking/php_empty_transaction.json') }
+
+ it_behaves_like 'successful request'
+ end
+
context 'sentry_key as param and empty headers' do
let(:url) { "/error_tracking/collector/api/#{project.id}/store?sentry_key=#{sentry_key}" }
let(:headers) { {} }
diff --git a/spec/requests/api/error_tracking/project_settings_spec.rb b/spec/requests/api/error_tracking/project_settings_spec.rb
index 161e4f01ea5..c0c0680ef31 100644
--- a/spec/requests/api/error_tracking/project_settings_spec.rb
+++ b/spec/requests/api/error_tracking/project_settings_spec.rb
@@ -23,6 +23,21 @@ RSpec.describe API::ErrorTracking::ProjectSettings do
end
end
+ shared_examples 'returns project settings with false for integrated' do
+ specify do
+ make_request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq(
+ 'active' => setting.reload.enabled,
+ 'project_name' => setting.project_name,
+ 'sentry_external_url' => setting.sentry_external_url,
+ 'api_url' => setting.api_url,
+ 'integrated' => false
+ )
+ end
+ end
+
shared_examples 'returns 404' do
it 'returns no project settings' do
make_request
@@ -46,7 +61,17 @@ RSpec.describe API::ErrorTracking::ProjectSettings do
end
context 'patch settings' do
- it_behaves_like 'returns project settings'
+ context 'integrated_error_tracking feature enabled' do
+ it_behaves_like 'returns project settings'
+ end
+
+ context 'integrated_error_tracking feature disabled' do
+ before do
+ stub_feature_flags(integrated_error_tracking: false)
+ end
+
+ it_behaves_like 'returns project settings with false for integrated'
+ end
it 'updates enabled flag' do
expect(setting).to be_enabled
@@ -84,13 +109,19 @@ RSpec.describe API::ErrorTracking::ProjectSettings do
context 'with integrated param' do
let(:params) { { active: true, integrated: true } }
- it 'updates the integrated flag' do
- expect(setting.integrated).to be_falsey
+ context 'integrated_error_tracking feature enabled' do
+ before do
+ stub_feature_flags(integrated_error_tracking: true)
+ end
- make_request
+ it 'updates the integrated flag' do
+ expect(setting.integrated).to be_falsey
+
+ make_request
- expect(json_response).to include('integrated' => true)
- expect(setting.reload.integrated).to be_truthy
+ expect(json_response).to include('integrated' => true)
+ expect(setting.reload.integrated).to be_truthy
+ end
end
end
end
@@ -170,7 +201,21 @@ RSpec.describe API::ErrorTracking::ProjectSettings do
end
context 'get settings' do
- it_behaves_like 'returns project settings'
+ context 'integrated_error_tracking feature enabled' do
+ before do
+ stub_feature_flags(integrated_error_tracking: true)
+ end
+
+ it_behaves_like 'returns project settings'
+ end
+
+ context 'integrated_error_tracking feature disabled' do
+ before do
+ stub_feature_flags(integrated_error_tracking: false)
+ end
+
+ it_behaves_like 'returns project settings with false for integrated'
+ end
end
end
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index e1d8a9f0229..3a5c6103781 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -170,17 +170,6 @@ RSpec.describe API::GenericPackages do
end
end
- context 'generic_packages feature flag is disabled' do
- it 'responds with 404 Not Found' do
- stub_feature_flags(generic_packages: false)
- project.add_developer(user)
-
- authorize_upload_file(workhorse_headers.merge(personal_access_token_header))
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
def authorize_upload_file(request_headers, package_name: 'mypackage', file_name: 'myfile.tar.gz')
url = "/projects/#{project.id}/packages/generic/#{package_name}/0.0.1/#{file_name}/authorize"
diff --git a/spec/requests/api/graphql/ci/pipelines_spec.rb b/spec/requests/api/graphql/ci/pipelines_spec.rb
index 5ae68be46a2..741af676b6d 100644
--- a/spec/requests/api/graphql/ci/pipelines_spec.rb
+++ b/spec/requests/api/graphql/ci/pipelines_spec.rb
@@ -528,4 +528,37 @@ RSpec.describe 'Query.project(fullPath).pipelines' do
end.not_to exceed_query_limit(control_count)
end
end
+
+ describe 'filtering' do
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ pipelines(updatedAfter: "#{updated_after_arg}", updatedBefore: "#{updated_before_arg}") {
+ nodes {
+ id
+ }}}}
+ )
+ end
+
+ context 'when filtered by updated_at' do
+ let_it_be(:oldish_pipeline) { create(:ci_empty_pipeline, project: project, updated_at: 3.days.ago) }
+ let_it_be(:older_pipeline) { create(:ci_empty_pipeline, project: project, updated_at: 10.days.ago) }
+
+ let(:updated_after_arg) { 5.days.ago }
+ let(:updated_before_arg) { 1.day.ago }
+
+ before do
+ post_graphql(query, current_user: user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'accepts filter params' do
+ pipeline_ids = graphql_data.dig('project', 'pipelines', 'nodes').map { |pipeline| pipeline.fetch('id') }
+
+ expect(pipeline_ids).to match_array(oldish_pipeline.to_global_id.to_s)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index fa16b9e1ddd..b99a3d14fb9 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -196,39 +196,6 @@ RSpec.describe 'Query.runner(id)' do
it_behaves_like 'runner details fetch', :inactive_instance_runner
end
- describe 'for runner inside group request' do
- let(:query) do
- %(
- query {
- group(fullPath: "#{group.full_path}") {
- runners {
- edges {
- webUrl
- node {
- id
- }
- }
- }
- }
- }
- )
- end
-
- it 'retrieves webUrl field with expected value' do
- post_graphql(query, current_user: user)
-
- runner_data = graphql_data_at(:group, :runners, :edges)
- expect(runner_data).to match_array [
- a_hash_including(
- 'webUrl' => "http://localhost/groups/#{group.full_path}/-/runners/#{active_group_runner.id}",
- 'node' => {
- 'id' => active_group_runner.to_global_id.to_s
- }
- )
- ]
- end
- end
-
describe 'for group runner request' do
let(:query) do
%(
diff --git a/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb b/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb
new file mode 100644
index 00000000000..767e958ea82
--- /dev/null
+++ b/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'RunnerWebUrlEdge' do
+ include GraphqlHelpers
+
+ describe 'inside a Query.group' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+
+ let(:edges_graphql_data) { graphql_data.dig('group', 'runners', 'edges') }
+
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ group(fullPath: $path) {
+ runners {
+ edges {
+ editUrl
+ webUrl
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ before do
+ post_graphql(query, current_user: user, variables: { path: group.full_path })
+ end
+
+ context 'with an authorized user' do
+ let(:user) { create_default(:user, :admin) }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns correct URLs' do
+ expect(edges_graphql_data).to match_array [
+ {
+ 'editUrl' => Gitlab::Routing.url_helpers.edit_group_runner_url(group, group_runner),
+ 'webUrl' => Gitlab::Routing.url_helpers.group_runner_url(group, group_runner)
+ }
+ ]
+ end
+ end
+
+ context 'with an unauthorized user' do
+ let(:user) { create(:user) }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns no edges' do
+ expect(edges_graphql_data).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
index 35a70a180a2..922a9ab277e 100644
--- a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
+++ b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
@@ -3,17 +3,19 @@ require 'spec_helper'
RSpec.describe 'container repository details' do
include_context 'container registry tags'
+ include_context 'container registry client stubs'
+
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
let_it_be_with_reload(:project) { create(:project) }
- let_it_be(:container_repository) { create(:container_repository, project: project) }
+ let_it_be_with_reload(:container_repository) { create(:container_repository, project: project) }
let(:query) do
graphql_query_for(
'containerRepository',
{ id: container_repository_global_id },
- all_graphql_fields_for('ContainerRepositoryDetails', excluded: ['pipeline'])
+ all_graphql_fields_for('ContainerRepositoryDetails', excluded: %w[pipeline size])
)
end
@@ -220,6 +222,80 @@ RSpec.describe 'container repository details' do
end
end
+ context 'size field' do
+ let(:size_response) { container_repository_details_response.dig('size') }
+ let(:on_com) { true }
+ let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT + 3.months }
+ let(:variables) do
+ { id: container_repository_global_id }
+ end
+
+ let(:query) do
+ <<~GQL
+ query($id: ID!) {
+ containerRepository(id: $id) {
+ size
+ }
+ }
+ GQL
+ end
+
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(on_com)
+ container_repository.update_column(:created_at, created_at)
+ end
+
+ it 'returns the size' do
+ stub_container_registry_gitlab_api_support(supported: true) do |client|
+ stub_container_registry_gitlab_api_repository_details(client, path: container_repository.path, size_bytes: 12345)
+ end
+
+ subject
+
+ expect(size_response).to eq(12345)
+ end
+
+ context 'with a network error' do
+ it 'returns an error' do
+ stub_container_registry_gitlab_api_network_error
+
+ subject
+
+ expect_graphql_errors_to_include("Can't connect to the Container Registry. If this error persists, please review the troubleshooting documentation.")
+ end
+ end
+
+ context 'with not supporting the gitlab api' do
+ it 'returns nil' do
+ stub_container_registry_gitlab_api_support(supported: false)
+
+ subject
+
+ expect(size_response).to eq(nil)
+ end
+ end
+
+ context 'not on .com' do
+ let(:on_com) { false }
+
+ it 'returns nil' do
+ subject
+
+ expect(size_response).to eq(nil)
+ end
+ end
+
+ context 'with an older container repository' do
+ let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT - 3.months }
+
+ it 'returns nil' do
+ subject
+
+ expect(size_response).to eq(nil)
+ end
+ end
+ end
+
context 'with tags with a manifest containing nil fields' do
let(:tags_response) { container_repository_details_response.dig('tags', 'nodes') }
let(:errors) { container_repository_details_response.dig('errors') }
diff --git a/spec/requests/api/graphql/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb
index 06afb5b9a49..78852622835 100644
--- a/spec/requests/api/graphql/group/group_members_spec.rb
+++ b/spec/requests/api/graphql/group/group_members_spec.rb
@@ -53,6 +53,30 @@ RSpec.describe 'getting group members information' do
end
end
+ context "when requesting member's notification email" do
+ context 'when current_user is admin' do
+ let_it_be(:admin_user) { create(:user, :admin) }
+
+ it 'returns notification email' do
+ fetch_members_notification_email(current_user: admin_user)
+ notification_emails = graphql_data_at(:group, :group_members, :edges, :node, :notification_email)
+
+ expect(notification_emails).to all be_present
+ expect(graphql_errors).to be_nil
+ end
+ end
+
+ context 'when current_user is not admin' do
+ it 'returns an error' do
+ fetch_members_notification_email
+
+ expect(graphql_errors.first)
+ .to include('path' => ['group', 'groupMembers', 'edges', 0, 'node', 'notificationEmail'],
+ 'message' => a_string_including("you don't have permission to perform this action"))
+ end
+ end
+ end
+
context 'member relations' do
let_it_be(:child_group) { create(:group, :public, parent: parent_group) }
let_it_be(:grandchild_group) { create(:group, :public, parent: child_group) }
@@ -117,6 +141,10 @@ RSpec.describe 'getting group members information' do
post_graphql(members_query(group.full_path, args), current_user: current_user)
end
+ def fetch_members_notification_email(group: parent_group, current_user: user)
+ post_graphql(member_notification_email_query(group.full_path), current_user: current_user)
+ end
+
def members_query(group_path, args = {})
members_node = <<~NODE
edges {
@@ -134,6 +162,24 @@ RSpec.describe 'getting group members information' do
)
end
+ def member_notification_email_query(group_path)
+ members_node = <<~NODE
+ edges {
+ node {
+ user {
+ id
+ }
+ notificationEmail
+ }
+ }
+ NODE
+
+ graphql_query_for("group",
+ { full_path: group_path },
+ [query_graphql_field("groupMembers", {}, members_node)]
+ )
+ end
+
def expect_array_response(*items)
expect(response).to have_gitlab_http_status(:success)
member_gids = graphql_data_at(:group, :group_members, :edges, :node, :user, :id)
diff --git a/spec/requests/api/graphql/group/issues_spec.rb b/spec/requests/api/graphql/group/issues_spec.rb
index 332bf242e9c..26338f46611 100644
--- a/spec/requests/api/graphql/group/issues_spec.rb
+++ b/spec/requests/api/graphql/group/issues_spec.rb
@@ -44,6 +44,31 @@ RSpec.describe 'getting an issue list for a group' do
end
end
+ context 'when there are archived projects' do
+ let_it_be(:archived_project) { create(:project, :archived, group: group1) }
+ let_it_be(:archived_issue) { create(:issue, project: archived_project) }
+
+ before_all do
+ group1.add_developer(current_user)
+ end
+
+ it 'excludes issues from archived projects by default' do
+ post_graphql(query, current_user: current_user)
+
+ expect(issues_ids).to contain_exactly(issue1_gid, issue2_gid)
+ end
+
+ context 'when include_archived is true' do
+ let(:issue_filter_params) { { include_archived: true } }
+
+ it 'includes issues from archived projects' do
+ post_graphql(query, current_user: current_user)
+
+ expect(issues_ids).to contain_exactly(issue1_gid, issue2_gid, archived_issue.to_global_id.to_s)
+ end
+ end
+ end
+
context 'when there is a confidential issue' do
let_it_be(:confidential_issue1) { create(:issue, :confidential, project: project1) }
let_it_be(:confidential_issue2) { create(:issue, :confidential, project: project2) }
diff --git a/spec/requests/api/graphql/group/merge_requests_spec.rb b/spec/requests/api/graphql/group/merge_requests_spec.rb
index e9a5e558b1d..c0faff11c8d 100644
--- a/spec/requests/api/graphql/group/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/group/merge_requests_spec.rb
@@ -16,6 +16,9 @@ RSpec.describe 'Query.group.mergeRequests' do
let_it_be(:project_x) { create(:project, :repository) }
let_it_be(:user) { create(:user, developer_projects: [project_x]) }
+ let_it_be(:archived_project) { create(:project, :archived, :repository, group: group) }
+ let_it_be(:archived_mr) { create(:merge_request, source_project: archived_project) }
+
let_it_be(:mr_attrs) do
{ target_branch: 'master' }
end
@@ -119,4 +122,22 @@ RSpec.describe 'Query.group.mergeRequests' do
expect(mrs_data).to match_array(expected_mrs(mrs_a + mrs_b + mrs_c))
end
end
+
+ describe 'passing include_archived: true' do
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ group(fullPath: $path) {
+ mergeRequests(includeArchived: true) { nodes { id } }
+ }
+ }
+ GQL
+ end
+
+ it 'can find all merge requests in the group, including from archived projects' do
+ post_graphql(query, current_user: user, variables: { path: group.full_path })
+
+ expect(mrs_data).to match_array(expected_mrs(mrs_a + mrs_b + [archived_mr]))
+ end
+ end
end
diff --git a/spec/requests/api/graphql/group/work_item_types_spec.rb b/spec/requests/api/graphql/group/work_item_types_spec.rb
index 0667e09d1e9..a33e3ae5427 100644
--- a/spec/requests/api/graphql/group/work_item_types_spec.rb
+++ b/spec/requests/api/graphql/group/work_item_types_spec.rb
@@ -64,8 +64,8 @@ RSpec.describe 'getting a list of work item types for a group' do
post_graphql(query, current_user: current_user)
end
- it 'makes the workItemTypes field unavailable' do
- expect(graphql_errors).to contain_exactly(hash_including("message" => "Field 'workItemTypes' doesn't exist on type 'Group'"))
+ it 'returns null' do
+ expect(graphql_data.dig('group', 'workItemTypes')).to be_nil
end
end
end
diff --git a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
index 79d687a2bdb..02b79dac489 100644
--- a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
@@ -9,12 +9,10 @@ RSpec.describe 'Setting issues crm contacts' do
let_it_be(:group) { create(:group, :crm_enabled) }
let_it_be(:subgroup) { create(:group, :crm_enabled, parent: group) }
let_it_be(:project) { create(:project, group: subgroup) }
- let_it_be(:group_contacts) { create_list(:contact, 4, group: group) }
- let_it_be(:subgroup_contacts) { create_list(:contact, 4, group: subgroup) }
+ let_it_be(:contacts) { create_list(:contact, 4, group: group) }
let(:issue) { create(:issue, project: project) }
let(:operation_mode) { Types::MutationOperationModeEnum.default_mode }
- let(:contacts) { subgroup_contacts }
let(:initial_contacts) { contacts[0..1] }
let(:mutation_contacts) { contacts[1..2] }
let(:contact_ids) { contact_global_ids(mutation_contacts) }
@@ -116,15 +114,7 @@ RSpec.describe 'Setting issues crm contacts' do
end
end
- context 'with issue group contacts' do
- let(:contacts) { subgroup_contacts }
-
- it_behaves_like 'successful mutation'
- end
-
- context 'with issue ancestor group contacts' do
- it_behaves_like 'successful mutation'
- end
+ it_behaves_like 'successful mutation'
context 'when the contact does not exist' do
let(:contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] }
diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
index 87c752393ea..2bc671e4ca5 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -8,13 +8,16 @@ RSpec.describe 'Adding a Note' do
let_it_be(:current_user) { create(:user) }
let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:discussion) { nil }
+ let(:head_sha) { nil }
+ let(:body) { 'Body text' }
let(:mutation) do
variables = {
noteable_id: GitlabSchema.id_from_object(noteable).to_s,
discussion_id: (GitlabSchema.id_from_object(discussion).to_s if discussion),
- body: 'Body text',
+ merge_request_diff_head_sha: head_sha.presence,
+ body: body,
confidential: true
}
@@ -54,7 +57,7 @@ RSpec.describe 'Adding a Note' do
let(:discussion) { create(:discussion_note).to_discussion }
it_behaves_like 'a mutation that returns top-level errors',
- errors: ["The discussion does not exist or you don't have permission to perform this action"]
+ errors: ["The discussion does not exist or you don't have permission to perform this action"]
end
context 'when the user has permission to create notes on the discussion' do
@@ -75,5 +78,29 @@ RSpec.describe 'Adding a Note' do
end
end
end
+
+ context 'when body only contains quick actions' do
+ let(:head_sha) { noteable.diff_head_sha }
+ let(:body) { '/merge' }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ # NOTE: Known issue https://gitlab.com/gitlab-org/gitlab/-/issues/346557
+ it 'returns a nil note and info about the command in errors' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to include(
+ 'errors' => [/Merged this merge request/],
+ 'note' => nil
+ )
+ end
+
+ it 'starts the merge process' do
+ expect { post_graphql_mutation(mutation, current_user: current_user) }
+ .to change { noteable.reload.merge_jid.present? }.from(false).to(true)
+ end
+ end
end
end
diff --git a/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb
new file mode 100644
index 00000000000..8d33f8e1806
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "Create a work item from a task in a work item's description" do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
+ let_it_be(:work_item, refind: true) { create(:work_item, project: project, description: '- [ ] A task in a list', lock_version: 3) }
+
+ let(:lock_version) { work_item.lock_version }
+ let(:input) do
+ {
+ 'id' => work_item.to_global_id.to_s,
+ 'workItemData' => {
+ 'title' => 'A task in a list',
+ 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_global_id.to_s,
+ 'lineNumberStart' => 1,
+ 'lineNumberEnd' => 1,
+ 'lockVersion' => lock_version
+ }
+ }
+ end
+
+ let(:mutation) { graphql_mutation(:workItemCreateFromTask, input) }
+ let(:mutation_response) { graphql_mutation_response(:work_item_create_from_task) }
+
+ context 'the user is not allowed to update a work item' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user has permissions to create a work item' do
+ let(:current_user) { developer }
+
+ it 'creates the work item' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change(WorkItem, :count).by(1)
+
+ created_work_item = WorkItem.last
+ work_item.reload
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(work_item.description).to eq("- [ ] #{created_work_item.to_reference}+")
+ expect(created_work_item.issue_type).to eq('task')
+ expect(created_work_item.work_item_type.base_type).to eq('task')
+ expect(mutation_response['workItem']).to include('id' => work_item.to_global_id.to_s)
+ expect(mutation_response['newWorkItem']).to include('id' => created_work_item.to_global_id.to_s)
+ end
+
+ context 'when creating a work item fails' do
+ let(:lock_version) { 2 }
+
+ it 'makes no changes to the DB and returns an error message' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.to not_change(WorkItem, :count).and(
+ not_change(work_item, :description)
+ )
+
+ expect(mutation_response['errors']).to contain_exactly('Stale work item. Check lock version')
+ end
+ end
+
+ it_behaves_like 'has spam protection' do
+ let(:mutation_class) { ::Mutations::WorkItems::CreateFromTask }
+ end
+
+ context 'when the work_items feature flag is disabled' do
+ before do
+ stub_feature_flags(work_items: false)
+ end
+
+ it 'does nothing and returns and error' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to not_change(WorkItem, :count)
+
+ expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/namespace_query_spec.rb b/spec/requests/api/graphql/namespace_query_spec.rb
index f7ee2bcb55d..e17469901c6 100644
--- a/spec/requests/api/graphql/namespace_query_spec.rb
+++ b/spec/requests/api/graphql/namespace_query_spec.rb
@@ -31,7 +31,8 @@ RSpec.describe 'Query' do
it 'fetches the expected data' do
expect(query_result).to include(
'fullPath' => target_namespace.full_path,
- 'name' => target_namespace.name
+ 'name' => target_namespace.name,
+ 'crossProjectPipelineAvailable' => target_namespace.licensed_feature_available?(:cross_project_pipeline)
)
end
end
diff --git a/spec/requests/api/graphql/project/jira_service_spec.rb b/spec/requests/api/graphql/project/jira_service_spec.rb
index 64e9e04ae44..d6abe94b873 100644
--- a/spec/requests/api/graphql/project/jira_service_spec.rb
+++ b/spec/requests/api/graphql/project/jira_service_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe 'query Jira service' do
services(active: true, type: JIRA_SERVICE) {
nodes {
type
+ serviceType
}
}
}
@@ -23,7 +24,7 @@ RSpec.describe 'query Jira service' do
)
end
- let(:services) { graphql_data.dig('project', 'services', 'nodes')}
+ let(:services) { graphql_data.dig('project', 'services', 'nodes') }
it_behaves_like 'unauthorized users cannot read services'
@@ -35,10 +36,8 @@ RSpec.describe 'query Jira service' do
it_behaves_like 'a working graphql query'
- it 'retuns list of jira imports' do
- service = services.first
-
- expect(service['type']).to eq('JiraService')
+ it 'returns list of jira integrations' do
+ expect(services).to contain_exactly({ 'type' => 'JiraService', 'serviceType' => 'JIRA_SERVICE' })
end
end
end
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index 353bf0356f6..cefe88aafc8 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -76,6 +76,24 @@ RSpec.describe 'getting merge request information nested in a project' do
end
end
+ context 'when the merge_request has committers' do
+ let(:mr_fields) do
+ <<~SELECT
+ committers { nodes { id username } }
+ SELECT
+ end
+
+ it 'includes committers' do
+ expected = merge_request.committers.map do |r|
+ a_hash_including('id' => global_id_of(r), 'username' => r.username)
+ end
+
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data_at(:project, :merge_request, :committers, :nodes)).to match_array(expected)
+ end
+ end
+
describe 'diffStats' do
let(:mr_fields) do
<<~FIELDS
diff --git a/spec/requests/api/graphql/project/work_item_types_spec.rb b/spec/requests/api/graphql/project/work_item_types_spec.rb
index 2caaedda2a1..157961c3f66 100644
--- a/spec/requests/api/graphql/project/work_item_types_spec.rb
+++ b/spec/requests/api/graphql/project/work_item_types_spec.rb
@@ -64,8 +64,8 @@ RSpec.describe 'getting a list of work item types for a project' do
post_graphql(query, current_user: current_user)
end
- it 'makes the workItemTypes field unavailable' do
- expect(graphql_errors).to contain_exactly(hash_including("message" => "Field 'workItemTypes' doesn't exist on type 'Project'"))
+ it 'returns null' do
+ expect(graphql_data.dig('project', 'workItemTypes')).to be_nil
end
end
end
diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb
index ecc7fffaef7..d650acc8354 100644
--- a/spec/requests/api/graphql/query_spec.rb
+++ b/spec/requests/api/graphql/query_spec.rb
@@ -11,6 +11,30 @@ RSpec.describe 'Query' do
let(:current_user) { developer }
+ describe 'gitpodEnabled field' do
+ let(:gitpod_enabled) { true }
+ let(:gitpod_enabled_query) do
+ <<~GRAPHQL
+ { gitpodEnabled }
+ GRAPHQL
+ end
+
+ before do
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:gitpod_enabled).and_return(gitpod_enabled)
+ post_graphql(gitpod_enabled_query)
+ end
+
+ context 'When Gitpod is enabled for the application' do
+ it { expect(graphql_data).to include('gitpodEnabled' => true) }
+ end
+
+ context 'When Gitpod is disabled for the application' do
+ let(:gitpod_enabled) { false }
+
+ it { expect(graphql_data).to include('gitpodEnabled' => false) }
+ end
+ end
+
describe '.designManagement' do
include DesignManagementTestHelpers
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
new file mode 100644
index 00000000000..bc5a8b3e006
--- /dev/null
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.work_item(id)' do
+ include GraphqlHelpers
+
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:project) { create(:project, :private).tap { |project| project.add_developer(developer) } }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+
+ let(:current_user) { developer }
+ let(:work_item_data) { graphql_data['workItem'] }
+ let(:work_item_fields) { all_graphql_fields_for('WorkItem') }
+ let(:global_id) { work_item.to_gid.to_s }
+
+ let(:query) do
+ graphql_query_for('workItem', { 'id' => global_id }, work_item_fields)
+ end
+
+ context 'when the user can read the work item' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns all fields' do
+ expect(work_item_data).to include(
+ 'description' => work_item.description,
+ 'id' => work_item.to_gid.to_s,
+ 'iid' => work_item.iid.to_s,
+ 'lockVersion' => work_item.lock_version,
+ 'state' => "OPEN",
+ 'title' => work_item.title,
+ 'workItemType' => hash_including('id' => work_item.work_item_type.to_gid.to_s)
+ )
+ end
+
+ context 'when an Issue Global ID is provided' do
+ let(:global_id) { Issue.find(work_item.id).to_gid.to_s }
+
+ it 'allows an Issue GID as input' do
+ expect(work_item_data).to include('id' => work_item.to_gid.to_s)
+ end
+ end
+ end
+
+ context 'when the user can not read the work item' do
+ let(:current_user) { create(:user) }
+
+ before do
+ post_graphql(query)
+ end
+
+ it 'returns an access error' do
+ expect(work_item_data).to be_nil
+ expect(graphql_errors).to contain_exactly(
+ hash_including('message' => ::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
+ )
+ end
+ end
+
+ context 'when the work_items feature flag is disabled' do
+ before do
+ stub_feature_flags(work_items: false)
+ end
+
+ it 'returns nil' do
+ post_graphql(query)
+
+ expect(work_item_data).to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb
index c48b5007f91..8e127bf0710 100644
--- a/spec/requests/api/group_clusters_spec.rb
+++ b/spec/requests/api/group_clusters_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe API::GroupClusters do
groups: [group])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api("/groups/#{group.id}/clusters", current_user) }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
get api("/groups/#{group.id}/clusters", unauthorized_user)
@@ -66,6 +70,10 @@ RSpec.describe API::GroupClusters do
groups: [group])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api("/groups/#{group.id}/clusters/#{cluster_id}", current_user) }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
get api("/groups/#{group.id}/clusters/#{cluster_id}", unauthorized_user)
@@ -181,6 +189,10 @@ RSpec.describe API::GroupClusters do
}
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
post api("/groups/#{group.id}/clusters/user", unauthorized_user), params: cluster_params
@@ -362,6 +374,10 @@ RSpec.describe API::GroupClusters do
groups: [group], domain: 'old-domain.com')
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { put api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: update_params }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
put api("/groups/#{group.id}/clusters/#{cluster.id}", unauthorized_user), params: update_params
@@ -503,6 +519,10 @@ RSpec.describe API::GroupClusters do
groups: [group])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { delete api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: cluster_params }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
delete api("/groups/#{group.id}/clusters/#{cluster.id}", unauthorized_user), params: cluster_params
diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb
index 11738e3cba8..34533da53dd 100644
--- a/spec/requests/api/group_labels_spec.rb
+++ b/spec/requests/api/group_labels_spec.rb
@@ -140,7 +140,7 @@ RSpec.describe API::GroupLabels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(group_label1.name)
- expect(json_response['color']).to eq(group_label1.color)
+ expect(json_response['color']).to be_color(group_label1.color)
expect(json_response['description']).to eq(group_label1.description)
end
end
@@ -156,7 +156,7 @@ RSpec.describe API::GroupLabels do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq(valid_new_label_title)
- expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['color']).to be_color('#FFAABB')
expect(json_response['description']).to eq('test')
end
@@ -169,7 +169,7 @@ RSpec.describe API::GroupLabels do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq(valid_new_label_title)
- expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['color']).to be_color('#FFAABB')
expect(json_response['description']).to be_nil
end
@@ -276,7 +276,7 @@ RSpec.describe API::GroupLabels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(valid_new_label_title)
- expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['color']).to be_color('#FFFFFF')
expect(json_response['description']).to eq('test')
end
@@ -332,7 +332,7 @@ RSpec.describe API::GroupLabels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(valid_new_label_title)
- expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['color']).to be_color('#FFFFFF')
expect(json_response['description']).to eq('test')
end
diff --git a/spec/requests/api/integrations_spec.rb b/spec/requests/api/integrations_spec.rb
index 033c80a5696..220c58afbe9 100644
--- a/spec/requests/api/integrations_spec.rb
+++ b/spec/requests/api/integrations_spec.rb
@@ -10,6 +10,14 @@ RSpec.describe API::Integrations do
create(:project, creator_id: user.id, namespace: user.namespace)
end
+ # The API supports all integrations except the GitLab Slack Application
+ # integration; this integration must be installed via the UI.
+ def self.integration_names
+ names = Integration.available_integration_names
+ names.delete(Integrations::GitlabSlackApplication.to_param) if Gitlab.ee?
+ names
+ end
+
%w[integrations services].each do |endpoint|
describe "GET /projects/:id/#{endpoint}" do
it 'returns authentication error when unauthenticated' do
@@ -43,7 +51,7 @@ RSpec.describe API::Integrations do
end
end
- Integration.available_integration_names.each do |integration|
+ integration_names.each do |integration|
describe "PUT /projects/:id/#{endpoint}/#{integration.dasherize}" do
include_context integration
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 59d185fe6c8..0e566dd8c0e 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -169,6 +169,7 @@ RSpec.describe API::Internal::Kubernetes do
'features' => {}
),
'gitaly_repository' => a_hash_including(
+ 'default_branch' => project.default_branch_or_main,
'storage_name' => project.repository_storage,
'relative_path' => project.disk_path + '.git',
'gl_repository' => "project-#{project.id}",
diff --git a/spec/requests/api/internal/mail_room_spec.rb b/spec/requests/api/internal/mail_room_spec.rb
index f3ca3708c0c..67ea617f90d 100644
--- a/spec/requests/api/internal/mail_room_spec.rb
+++ b/spec/requests/api/internal/mail_room_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe API::Internal::MailRoom do
}
end
- let(:auth_payload) { { 'iss' => Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_JWT_ISSUER, 'iat' => (Time.now - 10.seconds).to_i } }
+ let(:auth_payload) { { 'iss' => Gitlab::MailRoom::INTERNAL_API_REQUEST_JWT_ISSUER, 'iat' => (Time.now - 10.seconds).to_i } }
let(:incoming_email_secret) { 'incoming_email_secret' }
let(:service_desk_email_secret) { 'service_desk_email_secret' }
@@ -51,7 +51,7 @@ RSpec.describe API::Internal::MailRoom do
context 'handle incoming_email successfully' do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, incoming_email_secret, 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
it 'schedules a EmailReceiverWorker job with raw email content' do
@@ -71,7 +71,7 @@ RSpec.describe API::Internal::MailRoom do
context 'handle service_desk_email successfully' do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, service_desk_email_secret, 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
it 'schedules a ServiceDeskEmailReceiverWorker job with raw email content' do
@@ -91,7 +91,7 @@ RSpec.describe API::Internal::MailRoom do
context 'email content exceeds limit' do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, incoming_email_secret, 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
before do
@@ -134,7 +134,7 @@ RSpec.describe API::Internal::MailRoom do
context 'wrong token authentication' do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, 'wrongsecret', 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
it 'responds with 401 Unauthorized' do
@@ -147,7 +147,7 @@ RSpec.describe API::Internal::MailRoom do
context 'wrong mailbox type authentication' do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, service_desk_email_secret, 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
it 'responds with 401 Unauthorized' do
@@ -160,7 +160,7 @@ RSpec.describe API::Internal::MailRoom do
context 'not supported mailbox type' do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, incoming_email_secret, 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
it 'responds with 401 Unauthorized' do
@@ -181,7 +181,7 @@ RSpec.describe API::Internal::MailRoom do
let(:auth_headers) do
jwt_token = JWT.encode(auth_payload, service_desk_email_secret, 'HS256')
- { Gitlab::MailRoom::Authenticator::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ { Gitlab::MailRoom::INTERNAL_API_REQUEST_HEADER => jwt_token }
end
it 'responds with 401 Unauthorized' do
diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb
index 702e6ef0a2a..741cf793a77 100644
--- a/spec/requests/api/invitations_spec.rb
+++ b/spec/requests/api/invitations_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe API::Invitations do
let(:email) { 'email1@example.com' }
let(:email2) { 'email2@example.com' }
- let_it_be(:project) do
+ let_it_be(:project, reload: true) do
create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
project.add_maintainer(maintainer)
@@ -208,6 +208,25 @@ RSpec.describe API::Invitations do
end
end
+ context 'when adding project bot' do
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+
+ before do
+ unrelated_project = create(:project)
+ unrelated_project.add_maintainer(project_bot)
+ end
+
+ it 'returns error' do
+ expect do
+ post invitations_url(source, maintainer),
+ params: { email: project_bot.email, access_level: Member::DEVELOPER }
+
+ expect(json_response['status']).to eq 'error'
+ expect(json_response['message'][project_bot.email]).to include('User project bots cannot be added to other groups / projects')
+ end.not_to change { source.members.count }
+ end
+ end
+
it "returns a message if member already exists" do
post invitations_url(source, maintainer),
params: { email: developer.email, access_level: Member::MAINTAINER }
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index 82692366589..7c8994ad9ba 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -447,7 +447,7 @@ RSpec.describe API::Issues do
post_issue
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq({ 'error' => 'Spam detected' })
+ expect(json_response['message']['base']).to match_array([/issue has been recognized as spam/])
end
it 'creates a new spam log entry' do
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb
index dac721cbea0..6ea77cc6578 100644
--- a/spec/requests/api/issues/put_projects_issues_spec.rb
+++ b/spec/requests/api/issues/put_projects_issues_spec.rb
@@ -199,8 +199,8 @@ RSpec.describe API::Issues do
expect(spam_service).to receive_messages(check_for_spam?: true)
end
- expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
- expect(verdict_service).to receive(:execute).and_return(DISALLOW)
+ allow_next_instance_of(Spam::AkismetService) do |akismet_service|
+ allow(akismet_service).to receive(:spam?).and_return(true)
end
end
@@ -217,7 +217,7 @@ RSpec.describe API::Issues do
update_issue
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response).to include('message' => { 'error' => 'Spam detected' })
+ expect(json_response['message']['base']).to match_array([/issue has been recognized as spam/])
end
it 'creates a new spam log entry' do
@@ -323,44 +323,44 @@ RSpec.describe API::Issues do
end
it 'removes all labels and touches the record' do
- Timecop.travel(1.minute.from_now) do
+ travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: '' }
end
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to eq([])
- expect(json_response['updated_at']).to be > Time.now
+ expect(json_response['updated_at']).to be > Time.current
end
it 'removes all labels and touches the record with labels param as array' do
- Timecop.travel(1.minute.from_now) do
+ travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: [''] }
end
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to eq([])
- expect(json_response['updated_at']).to be > Time.now
+ expect(json_response['updated_at']).to be > Time.current
end
it 'updates labels and touches the record' do
- Timecop.travel(1.minute.from_now) do
+ travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: 'foo,bar' }
end
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to contain_exactly('foo', 'bar')
- expect(json_response['updated_at']).to be > Time.now
+ expect(json_response['updated_at']).to be > Time.current
end
it 'updates labels and touches the record with labels param as array' do
- Timecop.travel(1.minute.from_now) do
+ travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: %w(foo bar) }
end
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to include 'foo'
expect(json_response['labels']).to include 'bar'
- expect(json_response['updated_at']).to be > Time.now
+ expect(json_response['updated_at']).to be > Time.current
end
it 'allows special label names' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index db9d72245b3..48f2c45bd98 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(valid_label_title_2)
- expect(json_response['color']).to eq(label1.color)
+ expect(json_response['color']).to be_color(label1.color)
end
it "returns 200 if colors is changed (#{route_type} route)" do
@@ -42,7 +42,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(label1.name)
- expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['color']).to be_color('#FFFFFF')
end
it "returns 200 if a priority is added (#{route_type} route)" do
@@ -86,7 +86,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(valid_label_title_2)
- expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['color']).to be_color('#FFFFFF')
expect(json_response['description']).to eq('test')
end
@@ -266,8 +266,8 @@ RSpec.describe API::Labels do
'open_merge_requests_count' => 0,
'name' => group_label.name,
'description' => nil,
- 'color' => a_string_matching(/^#\h{6}$/),
- 'text_color' => a_string_matching(/^#\h{6}$/),
+ 'color' => a_valid_color,
+ 'text_color' => a_valid_color,
'priority' => nil,
'subscribed' => false,
'is_project_label' => false)
@@ -277,8 +277,8 @@ RSpec.describe API::Labels do
'open_merge_requests_count' => 1,
'name' => priority_label.name,
'description' => nil,
- 'color' => a_string_matching(/^#\h{6}$/),
- 'text_color' => a_string_matching(/^#\h{6}$/),
+ 'color' => a_valid_color,
+ 'text_color' => a_valid_color,
'priority' => 3,
'subscribed' => false,
'is_project_label' => true)
@@ -336,7 +336,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq(valid_label_title_2)
- expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['color']).to be_color('#FFAABB')
expect(json_response['description']).to eq('test')
expect(json_response['priority']).to eq(2)
end
@@ -350,7 +350,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq(valid_label_title_2)
- expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['color']).to be_color('#FFAABB')
expect(json_response['description']).to be_nil
expect(json_response['priority']).to be_nil
end
@@ -365,7 +365,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq(valid_label_title_2)
- expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['color']).to be_color('#FFAABB')
expect(json_response['description']).to be_nil
expect(json_response['priority']).to eq(3)
end
@@ -552,7 +552,7 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(label1.name)
- expect(json_response['color']).to eq(label1.color)
+ expect(json_response['color']).to be_color(label1.color.to_s)
end
context 'if group label already exists' do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 6186a43f992..561d81f9860 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -675,13 +675,13 @@ RSpec.describe API::Members do
end
context 'adding owner to project' do
- it 'returns 403' do
+ it 'returns created status' do
expect do
post api("/projects/#{project.id}/members", maintainer),
params: { user_id: stranger.id, access_level: Member::OWNER }
- expect(response).to have_gitlab_http_status(:bad_request)
- end.not_to change { project.members.count }
+ expect(response).to have_gitlab_http_status(:created)
+ end.to change { project.members.count }.by(1)
end
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 3c28aed6cac..455400072bf 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -228,44 +228,83 @@ RSpec.describe API::Notes do
end
let(:request_body) { 'Hi!' }
+ let(:params) { { body: request_body } }
let(:request_path) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes" }
- subject { post api(request_path, user), params: { body: request_body } }
+ subject { post api(request_path, user), params: params }
context 'a command only note' do
- let(:request_body) { "/spend 1h" }
+ context '/spend' do
+ let(:request_body) { "/spend 1h" }
- before do
- project.add_developer(user)
- end
+ before do
+ project.add_developer(user)
+ end
- it 'returns 202 Accepted status' do
- subject
+ it 'returns 202 Accepted status' do
+ subject
- expect(response).to have_gitlab_http_status(:accepted)
- end
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
- it 'does not actually create a new note' do
- expect { subject }.not_to change { Note.where(system: false).count }
- end
+ it 'does not actually create a new note' do
+ expect { subject }.not_to change { Note.where(system: false).count }
+ end
- it 'does however create a system note about the change', :sidekiq_inline do
- expect { subject }.to change { Note.system.count }.by(1)
- end
+ it 'does however create a system note about the change', :sidekiq_inline do
+ expect { subject }.to change { Note.system.count }.by(1)
+ end
+
+ it 'applies the commands' do
+ expect { subject }.to change { merge_request.reset.total_time_spent }
+ end
+
+ it 'reports the changes' do
+ subject
- it 'applies the commands' do
- expect { subject }.to change { merge_request.reset.total_time_spent }
+ expect(json_response).to include(
+ 'commands_changes' => include(
+ 'spend_time' => include('duration' => 3600)
+ ),
+ 'summary' => include('Added 1h spent time.')
+ )
+ end
end
- it 'reports the changes' do
- subject
+ context '/merge' do
+ let(:request_body) { "/merge" }
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request_with_multiple_diffs, source_project: project, target_project: project, author: user) }
+ let(:params) { { body: request_body, merge_request_diff_head_sha: merge_request.diff_head_sha } }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns 202 Accepted status' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+
+ it 'does not actually create a new note' do
+ expect { subject }.not_to change { Note.where(system: false).count }
+ end
+
+ it 'applies the commands' do
+ expect { subject }.to change { merge_request.reload.merge_jid.present? }.from(false).to(true)
+ end
- expect(json_response).to include(
- 'commands_changes' => include(
- 'spend_time' => include('duration' => 3600)
- ),
- 'summary' => include('Added 1h spent time.')
- )
+ it 'reports the changes' do
+ subject
+
+ expect(json_response).to include(
+ 'commands_changes' => include(
+ 'merge' => merge_request.diff_head_sha
+ ),
+ 'summary' => ['Merged this merge request.']
+ )
+ end
end
end
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 8a6e87944ec..02d377efd95 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -9,6 +9,7 @@ itself: # project
- external_webhook_token
- has_external_issue_tracker
- has_external_wiki
+ - hidden
- import_source
- import_type
- import_url
@@ -121,7 +122,6 @@ project_feature:
- created_at
- metrics_dashboard_access_level
- project_id
- - security_and_compliance_access_level
- updated_at
computed_attributes:
- issues_enabled
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index b83b41a881a..4c7da78f0d4 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -24,6 +24,10 @@ RSpec.describe API::ProjectClusters do
projects: [project])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api("/projects/#{project.id}/clusters", developer_user) }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
get api("/projects/#{project.id}/clusters", reporter_user)
@@ -67,6 +71,10 @@ RSpec.describe API::ProjectClusters do
projects: [project])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api("/projects/#{project.id}/clusters/#{cluster_id}", developer_user) }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
get api("/projects/#{project.id}/clusters/#{cluster_id}", reporter_user)
@@ -182,6 +190,10 @@ RSpec.describe API::ProjectClusters do
}
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { post api("/projects/#{project.id}/clusters/user", maintainer_user), params: cluster_params }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
post api("/projects/#{project.id}/clusters/user", developer_user), params: cluster_params
@@ -361,6 +373,10 @@ RSpec.describe API::ProjectClusters do
projects: [project])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { put api("/projects/#{project.id}/clusters/#{cluster.id}", maintainer_user), params: update_params }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
put api("/projects/#{project.id}/clusters/#{cluster.id}", developer_user), params: update_params
@@ -493,6 +509,10 @@ RSpec.describe API::ProjectClusters do
projects: [project])
end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { delete api("/projects/#{project.id}/clusters/#{cluster.id}", maintainer_user), params: cluster_params }
+ end
+
context 'non-authorized user' do
it 'responds with 403' do
delete api("/projects/#{project.id}/clusters/#{cluster.id}", developer_user), params: cluster_params
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 3ed08afd57d..a0f6d3d0081 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::ProjectImport do
+RSpec.describe API::ProjectImport, :aggregate_failures do
include WorkhorseHelpers
include AfterNextHelpers
@@ -47,7 +47,7 @@ RSpec.describe API::ProjectImport do
it 'executes a limited number of queries' do
control_count = ActiveRecord::QueryRecorder.new { subject }.count
- expect(control_count).to be <= 104
+ expect(control_count).to be <= 105
end
it 'schedules an import using a namespace' do
@@ -329,7 +329,7 @@ RSpec.describe API::ProjectImport do
)
service_response = ServiceResponse.success(payload: project)
- expect_next(::Import::GitlabProjects::CreateProjectFromRemoteFileService)
+ expect_next(::Import::GitlabProjects::CreateProjectService)
.to receive(:execute)
.and_return(service_response)
@@ -352,7 +352,86 @@ RSpec.describe API::ProjectImport do
message: 'Failed to import',
http_status: :bad_request
)
- expect_next(::Import::GitlabProjects::CreateProjectFromRemoteFileService)
+ expect_next(::Import::GitlabProjects::CreateProjectService)
+ .to receive(:execute)
+ .and_return(service_response)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response).to eq({
+ 'message' => 'Failed to import'
+ })
+ end
+ end
+ end
+ end
+
+ describe 'POST /projects/remote-import-s3' do
+ subject do
+ post api('/projects/remote-import-s3', user), params: params
+ end
+
+ let(:params) do
+ {
+ path: 'test-import',
+ region: 'region_name',
+ bucket_name: 'bucket_name',
+ file_key: 'file_key',
+ access_key_id: 'access_key_id',
+ secret_access_key: 'secret_access_key'
+ }
+ end
+
+ it_behaves_like 'requires authentication'
+
+ it 'returns NOT FOUND when the feature is disabled' do
+ stub_feature_flags(import_project_from_remote_file_s3: false)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when the feature flag is enabled' do
+ before do
+ stub_feature_flags(import_project_from_remote_file_s3: true)
+ end
+
+ context 'when the response is successful' do
+ it 'schedules the import successfully' do
+ project = create(
+ :project,
+ namespace: user.namespace,
+ name: 'test-import',
+ path: 'test-import'
+ )
+
+ service_response = ServiceResponse.success(payload: project)
+ expect_next(::Import::GitlabProjects::CreateProjectService)
+ .to receive(:execute)
+ .and_return(service_response)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response).to include({
+ 'id' => project.id,
+ 'name' => 'test-import',
+ 'name_with_namespace' => "#{user.namespace.name} / test-import",
+ 'path' => 'test-import',
+ 'path_with_namespace' => "#{user.namespace.path}/test-import"
+ })
+ end
+ end
+
+ context 'when the service returns an error' do
+ it 'fails to schedule the import' do
+ service_response = ServiceResponse.error(
+ message: 'Failed to import',
+ http_status: :bad_request
+ )
+ expect_next(::Import::GitlabProjects::CreateProjectService)
.to receive(:execute)
.and_return(service_response)
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 512cbf7c321..72519ed1683 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -276,7 +276,7 @@ RSpec.describe API::ProjectSnippets do
it 'rejects the snippet' do
expect { subject }.not_to change { Snippet.count }
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq({ "error" => "Spam detected" })
+ expect(json_response['message']['error']).to match(/snippet has been recognized as spam/)
end
it 'creates a spam log' do
@@ -344,7 +344,7 @@ RSpec.describe API::ProjectSnippets do
.not_to change { snippet.reload.title }
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq({ "error" => "Spam detected" })
+ expect(json_response['message']['error']).to match(/snippet has been recognized as spam/)
end
it 'creates a spam log' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 02df82d14a8..fc1d815a64e 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1077,6 +1077,7 @@ RSpec.describe API::Projects do
attrs[:operations_access_level] = 'disabled'
attrs[:analytics_access_level] = 'disabled'
attrs[:container_registry_access_level] = 'private'
+ attrs[:security_and_compliance_access_level] = 'private'
end
post api('/projects', user), params: project
@@ -1100,6 +1101,7 @@ RSpec.describe API::Projects do
expect(project.operations_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.analytics_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::PRIVATE)
+ expect(project.project_feature.security_and_compliance_access_level).to eq(ProjectFeature::PRIVATE)
end
it 'assigns container_registry_enabled to project', :aggregate_failures do
@@ -2227,6 +2229,7 @@ RSpec.describe API::Projects do
expect(json_response['restrict_user_defined_variables']).to eq(project.restrict_user_defined_variables?)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
expect(json_response['operations_access_level']).to be_present
+ expect(json_response['security_and_compliance_access_level']).to be_present
end
it 'exposes all necessary attributes' do
@@ -2295,6 +2298,7 @@ RSpec.describe API::Projects do
expect(json_response['wiki_access_level']).to be_present
expect(json_response['builds_access_level']).to be_present
expect(json_response['operations_access_level']).to be_present
+ expect(json_response['security_and_compliance_access_level']).to be_present
expect(json_response).to have_key('emails_disabled')
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
expect(json_response['remove_source_branch_after_merge']).to be_truthy
@@ -2542,9 +2546,11 @@ RSpec.describe API::Projects do
get api("/projects", user)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.first['permissions']['project_access']['access_level'])
+ detail_of_project = json_response.find { |detail| detail['id'] == project.id }
+
+ expect(detail_of_project.dig('permissions', 'project_access', 'access_level'))
.to eq(Gitlab::Access::MAINTAINER)
- expect(json_response.first['permissions']['group_access']).to be_nil
+ expect(detail_of_project.dig('permissions', 'group_access')).to be_nil
end
end
@@ -3220,6 +3226,30 @@ RSpec.describe API::Projects do
expect(project.reload.container_registry_access_level).to eq(ProjectFeature::ENABLED)
end
+ it 'sets security_and_compliance_access_level', :aggregate_failures do
+ put api("/projects/#{project.id}", user), params: { security_and_compliance_access_level: 'private' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['security_and_compliance_access_level']).to eq('private')
+ expect(Project.find_by(path: project[:path]).security_and_compliance_access_level).to eq(ProjectFeature::PRIVATE)
+ end
+
+ it 'sets operations_access_level', :aggregate_failures do
+ put api("/projects/#{project.id}", user), params: { operations_access_level: 'private' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['operations_access_level']).to eq('private')
+ expect(Project.find_by(path: project[:path]).operations_access_level).to eq(ProjectFeature::PRIVATE)
+ end
+
+ it 'sets analytics_access_level', :aggregate_failures do
+ put api("/projects/#{project.id}", user), params: { analytics_access_level: 'private' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['analytics_access_level']).to eq('private')
+ expect(Project.find_by(path: project[:path]).analytics_access_level).to eq(ProjectFeature::PRIVATE)
+ end
+
it 'returns 400 when nothing sent' do
project_param = {}
diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb
index fcd2d56e655..078db4f1509 100644
--- a/spec/requests/api/pypi_packages_spec.rb
+++ b/spec/requests/api/pypi_packages_spec.rb
@@ -185,6 +185,14 @@ RSpec.describe API::PypiPackages do
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
+
+ context 'without requires_python' do
+ let(:token) { personal_access_token.token }
+ let(:user_headers) { basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+
+ it_behaves_like 'PyPI package creation', :developer, :created, true
+ end
end
context 'with required_python too big' do
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index cb9b6a072b1..6038682de1e 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -160,7 +160,7 @@ RSpec.describe API::Releases do
get api("/projects/#{project.id}/releases", maintainer)
end.count
- create_list(:release, 2, :with_evidence, project: project, tag: 'v0.1', author: maintainer)
+ create_list(:release, 2, :with_evidence, project: project, author: maintainer)
create_list(:release, 2, project: project)
create_list(:release_link, 2, release: project.releases.first)
create_list(:release_link, 2, release: project.releases.last)
@@ -467,10 +467,10 @@ RSpec.describe API::Releases do
it "exposes tag and commit" do
create(:release,
project: project,
- tag: 'v0.1',
+ tag: 'v0.0.1',
author: maintainer,
created_at: 2.days.ago)
- get api("/projects/#{project.id}/releases/v0.1", guest)
+ get api("/projects/#{project.id}/releases/v0.0.1", guest)
expect(response).to match_response_schema('public_api/v4/release')
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index f42fc7aabc2..1d199a72d1d 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -783,6 +783,13 @@ RSpec.describe API::Repositories do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['notes']).to be_present
end
+
+ context 'when previous tag version does not exist' do
+ it_behaves_like '422 response' do
+ let(:request) { get api("/projects/#{project.id}/repository/changelog", user), params: { version: 'v0.0.0' } }
+ let(:message) { 'Failed to generate the changelog: The commit start range is unspecified, and no previous tag could be found to use instead' }
+ end
+ end
end
describe 'POST /projects/:id/repository/changelog' do
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 24cd95781c3..4d2a69cd85b 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -8,6 +8,11 @@ RSpec.describe API::Search do
let_it_be(:project, reload: true) { create(:project, :wiki_repo, :public, name: 'awesome project', group: group) }
let_it_be(:repo_project) { create(:project, :public, :repository, group: group) }
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
+ end
+
shared_examples 'response is correct' do |schema:, size: 1|
it { expect(response).to have_gitlab_http_status(:ok) }
it { expect(response).to match_response_schema(schema) }
@@ -347,7 +352,7 @@ RSpec.describe API::Search do
end
end
- it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
let(:current_user) { user }
def request
@@ -522,7 +527,7 @@ RSpec.describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
end
- it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
let(:current_user) { user }
def request
@@ -803,7 +808,7 @@ RSpec.describe API::Search do
end
end
- it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
let(:current_user) { user }
def request
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index dd5e6ac8a5e..13160519996 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -325,7 +325,7 @@ RSpec.describe API::Snippets, factory_default: :keep do
expect { subject }.not_to change { Snippet.count }
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq({ "error" => "Spam detected" })
+ expect(json_response['message']['error']).to match(/snippet has been recognized as spam/)
end
it 'creates a spam log' do
@@ -392,7 +392,7 @@ RSpec.describe API::Snippets, factory_default: :keep do
.not_to change { snippet.reload.title }
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq({ "error" => "Spam detected" })
+ expect(json_response['message']['error']).to match(/snippet has been recognized as spam/)
end
it 'creates a spam log' do
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 1511872d183..d94b70ec0f9 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -36,12 +36,57 @@ RSpec.describe API::SystemHooks do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
+ expect(response).to match_response_schema('public_api/v4/system_hooks')
+ expect(json_response.first).not_to have_key("token")
expect(json_response.first['url']).to eq(hook.url)
expect(json_response.first['push_events']).to be false
expect(json_response.first['tag_push_events']).to be false
expect(json_response.first['merge_requests_events']).to be false
expect(json_response.first['repository_update_events']).to be true
+ expect(json_response.first['enable_ssl_verification']).to be true
+ end
+ end
+ end
+
+ describe "GET /hooks/:id" do
+ context "when no user" do
+ it "returns authentication error" do
+ get api("/hooks/#{hook.id}")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context "when not an admin" do
+ it "returns forbidden error" do
+ get api("/hooks/#{hook.id}", user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "gets a hook", :aggregate_failures do
+ get api("/hooks/#{hook.id}", admin)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/system_hook')
+ expect(json_response).to match(
+ 'id' => be(hook.id),
+ 'url' => eq(hook.url),
+ 'created_at' => eq(hook.created_at.iso8601(3)),
+ 'push_events' => be(hook.push_events),
+ 'tag_push_events' => be(hook.tag_push_events),
+ 'merge_requests_events' => be(hook.merge_requests_events),
+ 'repository_update_events' => be(hook.repository_update_events),
+ 'enable_ssl_verification' => be(hook.enable_ssl_verification)
+ )
+ end
+
+ it 'returns 404 if the system hook does not exist' do
+ get api("/hooks/#{non_existing_record_id}", admin)
+
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
end
@@ -77,6 +122,7 @@ RSpec.describe API::SystemHooks do
post api('/hooks', admin), params: { url: 'http://mep.mep' }
expect(response).to have_gitlab_http_status(:created)
+ expect(response).to match_response_schema('public_api/v4/system_hook')
expect(json_response['enable_ssl_verification']).to be true
expect(json_response['push_events']).to be false
expect(json_response['tag_push_events']).to be false
@@ -98,6 +144,7 @@ RSpec.describe API::SystemHooks do
}
expect(response).to have_gitlab_http_status(:created)
+ expect(response).to match_response_schema('public_api/v4/system_hook')
expect(json_response['enable_ssl_verification']).to be false
expect(json_response['push_events']).to be true
expect(json_response['tag_push_events']).to be true
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index 24f38b04348..ae1e461d433 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -36,8 +36,8 @@ RSpec.describe API::Terraform::State do
let(:current_user) { maintainer }
it_behaves_like 'tracking unique hll events' do
- let(:target_id) { 'p_terraform_state_api_unique_users' }
- let(:expected_type) { instance_of(Integer) }
+ let(:target_event) { 'p_terraform_state_api_unique_users' }
+ let(:expected_value) { instance_of(Integer) }
end
end
end
diff --git a/spec/requests/api/topics_spec.rb b/spec/requests/api/topics_spec.rb
index 70eee8a1af9..5c17ca9581e 100644
--- a/spec/requests/api/topics_spec.rb
+++ b/spec/requests/api/topics_spec.rb
@@ -7,9 +7,9 @@ RSpec.describe API::Topics do
let_it_be(:file) { fixture_file_upload('spec/fixtures/dk.png') }
- let_it_be(:topic_1) { create(:topic, name: 'Git', total_projects_count: 1, avatar: file) }
- let_it_be(:topic_2) { create(:topic, name: 'GitLab', total_projects_count: 2) }
- let_it_be(:topic_3) { create(:topic, name: 'other-topic', total_projects_count: 3) }
+ let_it_be(:topic_1) { create(:topic, name: 'Git', total_projects_count: 1, non_private_projects_count: 1, avatar: file) }
+ let_it_be(:topic_2) { create(:topic, name: 'GitLab', total_projects_count: 2, non_private_projects_count: 2) }
+ let_it_be(:topic_3) { create(:topic, name: 'other-topic', total_projects_count: 3, non_private_projects_count: 3) }
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:user) { create(:user) }
@@ -142,6 +142,13 @@ RSpec.describe API::Topics do
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eql('name is missing')
end
+
+ it 'returns 400 if name is not unique (case insensitive)' do
+ post api('/topics/', admin), params: { name: topic_1.name.downcase }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']['name']).to eq(['has already been taken'])
+ end
end
context 'as normal user' do
@@ -248,4 +255,43 @@ RSpec.describe API::Topics do
end
end
end
+
+ describe 'DELETE /topics', :aggregate_failures do
+ context 'as administrator' do
+ it 'deletes a topic' do
+ delete api("/topics/#{topic_3.id}", admin), params: { name: 'my-topic' }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+
+ it 'returns 404 for non existing id' do
+ delete api("/topics/#{non_existing_record_id}", admin), params: { name: 'my-topic' }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns 400 for invalid `id` parameter' do
+ delete api('/topics/invalid', admin), params: { name: 'my-topic' }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eql('id is invalid')
+ end
+ end
+
+ context 'as normal user' do
+ it 'returns 403 Forbidden' do
+ delete api("/topics/#{topic_3.id}", user), params: { name: 'my-topic' }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'as anonymous' do
+ it 'returns 401 Unauthorized' do
+ delete api("/topics/#{topic_3.id}"), params: { name: 'my-topic' }
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/user_counts_spec.rb b/spec/requests/api/user_counts_spec.rb
index ab2aa87d1b7..27ebf02dd81 100644
--- a/spec/requests/api/user_counts_spec.rb
+++ b/spec/requests/api/user_counts_spec.rb
@@ -43,6 +43,21 @@ RSpec.describe API::UserCounts do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a Hash
expect(json_response['merge_requests']).to eq(2)
+ expect(json_response['attention_requests']).to eq(2)
+ end
+
+ describe 'mr_attention_requests is disabled' do
+ before do
+ stub_feature_flags(mr_attention_requests: false)
+ end
+
+ it 'does not include attention_requests count' do
+ create(:merge_request, source_project: project, author: user, assignees: [user])
+
+ get api('/user_counts', user)
+
+ expect(json_response.key?('attention_requests')).to be(false)
+ end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 985e07bf174..2d71674273b 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe API::Users do
let(:blocked_user) { create(:user, :blocked) }
let(:omniauth_user) { create(:omniauth_user) }
+ let(:ldap_user) { create(:omniauth_user, provider: 'ldapmain') }
let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
let(:private_user) { create(:user, private_profile: true) }
let(:deactivated_user) { create(:user, state: 'deactivated') }
@@ -649,20 +650,6 @@ RSpec.describe API::Users do
expect(response).to have_gitlab_http_status(:ok)
end
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(rate_limit_user_by_id_endpoint: false)
- end
-
- it 'does not throttle the request' do
- expect(Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
-
- get api("/users/#{user.id}", user)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
end
context 'when job title is present' do
@@ -1307,10 +1294,10 @@ RSpec.describe API::Users do
end
it "updates user's existing identity" do
- put api("/users/#{omniauth_user.id}", admin), params: { provider: 'ldapmain', extern_uid: '654321' }
+ put api("/users/#{ldap_user.id}", admin), params: { provider: 'ldapmain', extern_uid: '654321' }
expect(response).to have_gitlab_http_status(:ok)
- expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321')
+ expect(ldap_user.reload.identities.first.extern_uid).to eq('654321')
end
it 'updates user with new identity' do
@@ -1735,6 +1722,33 @@ RSpec.describe API::Users do
end
end
+ describe 'GET /user/:id/keys/:key_id' do
+ it 'gets existing key', :aggregate_failures do
+ user.keys << key
+
+ get api("/users/#{user.id}/keys/#{key.id}")
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['title']).to eq(key.title)
+ end
+
+ it 'returns 404 error if user not found', :aggregate_failures do
+ user.keys << key
+
+ get api("/users/0/keys/#{key.id}")
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+
+ it 'returns 404 error if key not found', :aggregate_failures do
+ get api("/users/#{user.id}/keys/#{non_existing_record_id}")
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Key Not Found')
+ end
+ end
+
describe 'DELETE /user/:id/keys/:key_id' do
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -3103,6 +3117,18 @@ RSpec.describe API::Users do
expect(response.body).to eq('null')
end
end
+
+ context 'with the API initiating user' do
+ let(:user_id) { admin.id }
+
+ it 'does not block the API initiating user, returns 403' do
+ block_user
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq('403 Forbidden - The API initiating user cannot be blocked by the API')
+ expect(admin.reload.state).to eq('active')
+ end
+ end
end
it 'is not available for non admin users' do
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index ec34dc7e7a1..06ae61ca5eb 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe API::Wikis do
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) }
+ let(:expected_keys_with_content) { %w(content format slug title encoding) }
let(:expected_keys_without_content) { %w(format slug title) }
let(:wiki) { project_wiki }
@@ -130,41 +130,42 @@ RSpec.describe API::Wikis do
describe 'GET /projects/:id/wikis/:slug' do
let(:page) { create(:wiki_page, wiki: project.wiki) }
let(:url) { "/projects/#{project.id}/wikis/#{page.slug}" }
+ let(:params) { {} }
+
+ subject(:request) { get api(url, user), params: params }
context 'when wiki is disabled' do
let(:project) { project_wiki_disabled }
+ before do
+ request
+ end
+
context 'when user is guest' do
- before do
- get api(url)
- end
+ let(:user) { nil }
include_examples 'wiki API 404 Project Not Found'
end
context 'when user is developer' do
- before do
- get api(url, developer)
- end
+ let(:user) { developer }
include_examples 'wiki API 403 Forbidden'
end
context 'when user is maintainer' do
- before do
- get api(url, maintainer)
- end
+ let(:user) { maintainer }
include_examples 'wiki API 403 Forbidden'
end
end
context 'when wiki is available only for team members' do
- let(:project) { create(:project, :wiki_repo, :wiki_private) }
+ let_it_be_with_reload(:project) { create(:project, :wiki_repo, :wiki_private) }
context 'when user is guest' do
before do
- get api(url)
+ request
end
include_examples 'wiki API 404 Project Not Found'
@@ -173,7 +174,6 @@ RSpec.describe API::Wikis do
context 'when user is developer' do
before do
project.add_developer(user)
- get api(url, user)
end
include_examples 'wikis API returns wiki page'
@@ -181,6 +181,10 @@ RSpec.describe API::Wikis do
context 'when page is not existing' do
let(:url) { "/projects/#{project.id}/wikis/unknown" }
+ before do
+ request
+ end
+
include_examples 'wiki API 404 Wiki Page Not Found'
end
end
@@ -188,8 +192,6 @@ RSpec.describe API::Wikis do
context 'when user is maintainer' do
before do
project.add_maintainer(user)
-
- get api(url, user)
end
include_examples 'wikis API returns wiki page'
@@ -197,17 +199,23 @@ RSpec.describe API::Wikis do
context 'when page is not existing' do
let(:url) { "/projects/#{project.id}/wikis/unknown" }
+ before do
+ request
+ end
+
include_examples 'wiki API 404 Wiki Page Not Found'
end
end
end
context 'when wiki is available for everyone with access' do
- let(:project) { create(:project, :wiki_repo) }
+ let_it_be_with_reload(:project) { create(:project, :wiki_repo) }
context 'when user is guest' do
+ let(:user) { nil }
+
before do
- get api(url)
+ request
end
include_examples 'wiki API 404 Project Not Found'
@@ -216,8 +224,6 @@ RSpec.describe API::Wikis do
context 'when user is developer' do
before do
project.add_developer(user)
-
- get api(url, user)
end
include_examples 'wikis API returns wiki page'
@@ -225,6 +231,10 @@ RSpec.describe API::Wikis do
context 'when page is not existing' do
let(:url) { "/projects/#{project.id}/wikis/unknown" }
+ before do
+ request
+ end
+
include_examples 'wiki API 404 Wiki Page Not Found'
end
end
@@ -232,8 +242,6 @@ RSpec.describe API::Wikis do
context 'when user is maintainer' do
before do
project.add_maintainer(user)
-
- get api(url, user)
end
include_examples 'wikis API returns wiki page'
@@ -241,6 +249,10 @@ RSpec.describe API::Wikis do
context 'when page is not existing' do
let(:url) { "/projects/#{project.id}/wikis/unknown" }
+ before do
+ request
+ end
+
include_examples 'wiki API 404 Wiki Page Not Found'
end
end