summaryrefslogtreecommitdiff
path: root/spec/requests/api
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-03-20 15:19:03 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-20 15:19:03 +0000
commit14bd84b61276ef29b97d23642d698de769bacfd2 (patch)
treef9eba90140c1bd874211dea17750a0d422c04080 /spec/requests/api
parent891c388697b2db0d8ee0c8358a9bdbf6dc56d581 (diff)
downloadgitlab-ce-14bd84b61276ef29b97d23642d698de769bacfd2.tar.gz
Add latest changes from gitlab-org/gitlab@15-10-stable-eev15.10.0-rc42
Diffstat (limited to 'spec/requests/api')
-rw-r--r--spec/requests/api/access_requests_spec.rb2
-rw-r--r--spec/requests/api/admin/batched_background_migrations_spec.rb93
-rw-r--r--spec/requests/api/admin/ci/variables_spec.rb121
-rw-r--r--spec/requests/api/admin/instance_clusters_spec.rb137
-rw-r--r--spec/requests/api/admin/plan_limits_spec.rb58
-rw-r--r--spec/requests/api/admin/sidekiq_spec.rb25
-rw-r--r--spec/requests/api/api_guard/admin_mode_middleware_spec.rb2
-rw-r--r--spec/requests/api/api_guard/response_coercer_middleware_spec.rb2
-rw-r--r--spec/requests/api/api_spec.rb2
-rw-r--r--spec/requests/api/appearance_spec.rb4
-rw-r--r--spec/requests/api/applications_spec.rb8
-rw-r--r--spec/requests/api/avatar_spec.rb1
-rw-r--r--spec/requests/api/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/ci/job_artifacts_spec.rb6
-rw-r--r--spec/requests/api/ci/pipeline_schedules_spec.rb4
-rw-r--r--spec/requests/api/ci/pipelines_spec.rb114
-rw-r--r--spec/requests/api/ci/runner/jobs_put_spec.rb11
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb13
-rw-r--r--spec/requests/api/ci/runner/runners_verify_post_spec.rb30
-rw-r--r--spec/requests/api/ci/runners_reset_registration_token_spec.rb15
-rw-r--r--spec/requests/api/ci/runners_spec.rb203
-rw-r--r--spec/requests/api/ci/variables_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb62
-rw-r--r--spec/requests/api/composer_packages_spec.rb6
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb40
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb42
-rw-r--r--spec/requests/api/doorkeeper_access_spec.rb2
-rw-r--r--spec/requests/api/draft_notes_spec.rb171
-rw-r--r--spec/requests/api/error_tracking/project_settings_spec.rb244
-rw-r--r--spec/requests/api/files_spec.rb35
-rw-r--r--spec/requests/api/freeze_periods_spec.rb196
-rw-r--r--spec/requests/api/graphql/achievements/user_achievements_query_spec.rb83
-rw-r--r--spec/requests/api/graphql/ci/config_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/group_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/instance_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb62
-rw-r--r--spec/requests/api/graphql/ci/manual_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/project_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb80
-rw-r--r--spec/requests/api/graphql/ci/runners_spec.rb31
-rw-r--r--spec/requests/api/graphql/current_user/todos_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/current_user_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/custom_emoji_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/multiplexed_queries_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/achievements/award_spec.rb106
-rw-r--r--spec/requests/api/graphql/mutations/achievements/revoke_spec.rb91
-rw-r--r--spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/add_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/cancel_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb)4
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/play_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/job_play_spec.rb)4
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/retry_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/job_retry_spec.rb)8
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/unschedule_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb)2
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_artifact/bulk_destroy_spec.rb197
-rw-r--r--spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb15
-rw-r--r--spec/requests/api/graphql/mutations/ci/runner/create_spec.rb121
-rw-r--r--spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/design_management/update_spec.rb77
-rw-r--r--spec/requests/api/graphql/mutations/issues/bulk_update_spec.rb68
-rw-r--r--spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb128
-rw-r--r--spec/requests/api/graphql/mutations/members/projects/bulk_update_spec.rb18
-rw-r--r--spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb16
-rw-r--r--spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb131
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb3
-rw-r--r--spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/create_spec.rb54
-rw-r--r--spec/requests/api/graphql/mutations/work_items/export_spec.rb67
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_spec.rb137
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_task_spec.rb2
-rw-r--r--spec/requests/api/graphql/namespace/projects_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/package_spec.rb25
-rw-r--r--spec/requests/api/graphql/project/base_service_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/container_repositories_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/flow_metrics_spec.rb23
-rw-r--r--spec/requests/api/graphql/project/fork_details_spec.rb35
-rw-r--r--spec/requests/api/graphql/project/merge_requests_spec.rb3
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb28
-rw-r--r--spec/requests/api/graphql/query_spec.rb2
-rw-r--r--spec/requests/api/graphql/user/user_achievements_query_spec.rb91
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb26
-rw-r--r--spec/requests/api/graphql_spec.rb2
-rw-r--r--spec/requests/api/group_milestones_spec.rb78
-rw-r--r--spec/requests/api/group_variables_spec.rb2
-rw-r--r--spec/requests/api/helm_packages_spec.rb15
-rw-r--r--spec/requests/api/helpers_spec.rb2
-rw-r--r--spec/requests/api/internal/base_spec.rb12
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb147
-rw-r--r--spec/requests/api/internal/pages_spec.rb378
-rw-r--r--spec/requests/api/internal/workhorse_spec.rb2
-rw-r--r--spec/requests/api/issues/get_group_issues_spec.rb30
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb42
-rw-r--r--spec/requests/api/issues/issues_spec.rb67
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb90
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb62
-rw-r--r--spec/requests/api/keys_spec.rb2
-rw-r--r--spec/requests/api/lint_spec.rb10
-rw-r--r--spec/requests/api/maven_packages_spec.rb37
-rw-r--r--spec/requests/api/merge_requests_spec.rb11
-rw-r--r--spec/requests/api/metadata_spec.rb2
-rw-r--r--spec/requests/api/npm_instance_packages_spec.rb34
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb4
-rw-r--r--spec/requests/api/nuget_group_packages_spec.rb12
-rw-r--r--spec/requests/api/nuget_project_packages_spec.rb11
-rw-r--r--spec/requests/api/oauth_tokens_spec.rb4
-rw-r--r--spec/requests/api/pages/internal_access_spec.rb68
-rw-r--r--spec/requests/api/pages/pages_spec.rb12
-rw-r--r--spec/requests/api/pages/private_access_spec.rb68
-rw-r--r--spec/requests/api/pages/public_access_spec.rb68
-rw-r--r--spec/requests/api/pages_domains_spec.rb40
-rw-r--r--spec/requests/api/personal_access_tokens/self_information_spec.rb2
-rw-r--r--spec/requests/api/personal_access_tokens_spec.rb2
-rw-r--r--spec/requests/api/project_milestones_spec.rb87
-rw-r--r--spec/requests/api/projects_spec.rb105
-rw-r--r--spec/requests/api/protected_branches_spec.rb9
-rw-r--r--spec/requests/api/pypi_packages_spec.rb30
-rw-r--r--spec/requests/api/release/links_spec.rb24
-rw-r--r--spec/requests/api/repositories_spec.rb1
-rw-r--r--spec/requests/api/resource_access_tokens_spec.rb2
-rw-r--r--spec/requests/api/rubygem_packages_spec.rb30
-rw-r--r--spec/requests/api/search_spec.rb17
-rw-r--r--spec/requests/api/settings_spec.rb10
-rw-r--r--spec/requests/api/sidekiq_metrics_spec.rb2
-rw-r--r--spec/requests/api/terraform/modules/v1/packages_spec.rb7
-rw-r--r--spec/requests/api/terraform/state_spec.rb9
-rw-r--r--spec/requests/api/terraform/state_version_spec.rb8
-rw-r--r--spec/requests/api/unleash_spec.rb8
-rw-r--r--spec/requests/api/users_spec.rb12
131 files changed, 3605 insertions, 1507 deletions
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb
index 8c14ead9e42..45d1594c734 100644
--- a/spec/requests/api/access_requests_spec.rb
+++ b/spec/requests/api/access_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::AccessRequests, feature_category: :authentication_and_authorization do
+RSpec.describe API::AccessRequests, feature_category: :system_access do
let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:access_requester) { create(:user) }
diff --git a/spec/requests/api/admin/batched_background_migrations_spec.rb b/spec/requests/api/admin/batched_background_migrations_spec.rb
index d946ac17f3f..e88fba3fbe7 100644
--- a/spec/requests/api/admin/batched_background_migrations_spec.rb
+++ b/spec/requests/api/admin/batched_background_migrations_spec.rb
@@ -4,22 +4,23 @@ require 'spec_helper'
RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :database do
let(:admin) { create(:admin) }
- let(:unauthorized_user) { create(:user) }
describe 'GET /admin/batched_background_migrations/:id' do
let!(:migration) { create(:batched_background_migration, :paused) }
let(:database) { :main }
let(:params) { { database: database } }
+ let(:path) { "/admin/batched_background_migrations/#{migration.id}" }
+
+ it_behaves_like "GET request permissions for admin mode"
subject(:show_migration) do
- get api("/admin/batched_background_migrations/#{migration.id}", admin), params: { database: database }
+ get api(path, admin, admin_mode: true), params: { database: database }
end
it 'fetches the batched background migration' do
show_migration
aggregate_failures "testing response" do
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(migration.id)
expect(json_response['status']).to eq('paused')
expect(json_response['job_class_name']).to eq(migration.job_class_name)
@@ -29,7 +30,8 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
context 'when the batched background migration does not exist' do
it 'returns 404' do
- get api("/admin/batched_background_migrations/#{non_existing_record_id}", admin), params: params
+ get api("/admin/batched_background_migrations/#{non_existing_record_id}", admin, admin_mode: true),
+ params: params
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -50,19 +52,11 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
end
end
- context 'when authenticated as a non-admin user' do
- it 'returns 403' do
- get api("/admin/batched_background_migrations/#{migration.id}", unauthorized_user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
context 'when the database name does not exist' do
let(:database) { :wrong_database }
- it 'returns bad request' do
- get api("/admin/batched_background_migrations/#{migration.id}", admin), params: params
+ it 'returns bad request', :aggregate_failures do
+ get api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include('database does not have a valid value')
@@ -72,13 +66,15 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
describe 'GET /admin/batched_background_migrations' do
let!(:migration) { create(:batched_background_migration) }
+ let(:path) { '/admin/batched_background_migrations' }
+
+ it_behaves_like "GET request permissions for admin mode"
context 'when is an admin user' do
it 'returns batched background migrations' do
- get api('/admin/batched_background_migrations', admin)
+ get api(path, admin, admin_mode: true)
aggregate_failures "testing response" do
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['id']).to eq(migration.id)
expect(json_response.first['job_class_name']).to eq(migration.job_class_name)
@@ -105,14 +101,14 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(ci_model.connection).and_yield
- get api('/admin/batched_background_migrations', admin), params: params
+ get api(path, admin, admin_mode: true), params: params
end
context 'when the database name does not exist' do
let(:database) { :wrong_database }
- it 'returns bad request' do
- get api("/admin/batched_background_migrations", admin), params: params
+ it 'returns bad request', :aggregate_failures do
+ get api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include('database does not have a valid value')
@@ -127,10 +123,9 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
create(:batched_background_migration, :active, gitlab_schema: schema)
end
- get api('/admin/batched_background_migrations', admin), params: params
+ get api(path, admin, admin_mode: true), params: params
aggregate_failures "testing response" do
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['id']).to eq(ci_database_migration.id)
expect(json_response.first['job_class_name']).to eq(ci_database_migration.job_class_name)
@@ -142,30 +137,24 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
end
end
end
-
- context 'when authenticated as a non-admin user' do
- it 'returns 403' do
- get api('/admin/batched_background_migrations', unauthorized_user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
describe 'PUT /admin/batched_background_migrations/:id/resume' do
let!(:migration) { create(:batched_background_migration, :paused) }
let(:database) { :main }
let(:params) { { database: database } }
+ let(:path) { "/admin/batched_background_migrations/#{migration.id}/resume" }
+
+ it_behaves_like "PUT request permissions for admin mode"
subject(:resume) do
- put api("/admin/batched_background_migrations/#{migration.id}/resume", admin), params: params
+ put api(path, admin, admin_mode: true), params: params
end
it 'pauses the batched background migration' do
resume
aggregate_failures "testing response" do
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(migration.id)
expect(json_response['status']).to eq('active')
end
@@ -173,7 +162,8 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
context 'when the batched background migration does not exist' do
it 'returns 404' do
- put api("/admin/batched_background_migrations/#{non_existing_record_id}/resume", admin), params: params
+ put api("/admin/batched_background_migrations/#{non_existing_record_id}/resume", admin, admin_mode: true),
+ params: params
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -183,7 +173,7 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
let!(:migration) { create(:batched_background_migration, :failed) }
it 'returns 422' do
- put api("/admin/batched_background_migrations/#{migration.id}/resume", admin), params: params
+ put api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
@@ -206,34 +196,28 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
context 'when the database name does not exist' do
let(:database) { :wrong_database }
- it 'returns bad request' do
- put api("/admin/batched_background_migrations/#{migration.id}/resume", admin), params: params
+ it 'returns bad request', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include('database does not have a valid value')
end
end
end
-
- context 'when authenticated as a non-admin user' do
- it 'returns 403' do
- put api("/admin/batched_background_migrations/#{migration.id}/resume", unauthorized_user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
describe 'PUT /admin/batched_background_migrations/:id/pause' do
let!(:migration) { create(:batched_background_migration, :active) }
let(:database) { :main }
let(:params) { { database: database } }
+ let(:path) { "/admin/batched_background_migrations/#{migration.id}/pause" }
+
+ it_behaves_like "PUT request permissions for admin mode"
it 'pauses the batched background migration' do
- put api("/admin/batched_background_migrations/#{migration.id}/pause", admin), params: params
+ put api(path, admin, admin_mode: true), params: params
aggregate_failures "testing response" do
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(migration.id)
expect(json_response['status']).to eq('paused')
end
@@ -241,7 +225,8 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
context 'when the batched background migration does not exist' do
it 'returns 404' do
- put api("/admin/batched_background_migrations/#{non_existing_record_id}/pause", admin), params: params
+ put api("/admin/batched_background_migrations/#{non_existing_record_id}/pause", admin, admin_mode: true),
+ params: params
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -251,7 +236,7 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
let!(:migration) { create(:batched_background_migration, :failed) }
it 'returns 422' do
- put api("/admin/batched_background_migrations/#{migration.id}/pause", admin), params: params
+ put api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
@@ -268,27 +253,19 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
it 'uses the correct connection' do
expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(ci_model.connection).and_yield
- put api("/admin/batched_background_migrations/#{migration.id}/pause", admin), params: params
+ put api(path, admin, admin_mode: true), params: params
end
context 'when the database name does not exist' do
let(:database) { :wrong_database }
- it 'returns bad request' do
- put api("/admin/batched_background_migrations/#{migration.id}/pause", admin), params: params
+ it 'returns bad request', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include('database does not have a valid value')
end
end
end
-
- context 'when authenticated as a non-admin user' do
- it 'returns 403' do
- put api("/admin/batched_background_migrations/#{non_existing_record_id}/pause", unauthorized_user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
end
diff --git a/spec/requests/api/admin/ci/variables_spec.rb b/spec/requests/api/admin/ci/variables_spec.rb
index 4bdc44cb583..dd4171b257a 100644
--- a/spec/requests/api/admin/ci/variables_spec.rb
+++ b/spec/requests/api/admin/ci/variables_spec.rb
@@ -5,68 +5,60 @@ require 'spec_helper'
RSpec.describe ::API::Admin::Ci::Variables do
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
+ let_it_be(:variable) { create(:ci_instance_variable) }
+ let_it_be(:path) { '/admin/ci/variables' }
describe 'GET /admin/ci/variables' do
- let!(:variable) { create(:ci_instance_variable) }
+ it_behaves_like 'GET request permissions for admin mode'
it 'returns instance-level variables for admins', :aggregate_failures do
- get api('/admin/ci/variables', admin)
+ get api(path, admin, admin_mode: true)
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a(Array)
end
- it 'does not return instance-level variables for regular users' do
- get api('/admin/ci/variables', user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
-
it 'does not return instance-level variables for unauthorized users' do
- get api('/admin/ci/variables')
+ get api(path, admin_mode: true)
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
describe 'GET /admin/ci/variables/:key' do
- let!(:variable) { create(:ci_instance_variable) }
+ let_it_be(:path) { "/admin/ci/variables/#{variable.key}" }
+
+ it_behaves_like 'GET request permissions for admin mode'
it 'returns instance-level variable details for admins', :aggregate_failures do
- get api("/admin/ci/variables/#{variable.key}", admin)
+ get api(path, admin, admin_mode: true)
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response['value']).to eq(variable.value)
expect(json_response['protected']).to eq(variable.protected?)
expect(json_response['variable_type']).to eq(variable.variable_type)
end
it 'responds with 404 Not Found if requesting non-existing variable' do
- get api('/admin/ci/variables/non_existing_variable', admin)
+ get api('/admin/ci/variables/non_existing_variable', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
- it 'does not return instance-level variable details for regular users' do
- get api("/admin/ci/variables/#{variable.key}", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
-
it 'does not return instance-level variable details for unauthorized users' do
- get api("/admin/ci/variables/#{variable.key}")
+ get api(path, admin_mode: true)
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
describe 'POST /admin/ci/variables' do
- context 'authorized user with proper permissions' do
- let!(:variable) { create(:ci_instance_variable) }
+ it_behaves_like 'POST request permissions for admin mode' do
+ let(:params) { { key: 'KEY', value: 'VALUE' } }
+ end
+ context 'authorized user with proper permissions' do
it 'creates variable for admins', :aggregate_failures do
expect do
- post api('/admin/ci/variables', admin),
+ post api(path, admin, admin_mode: true),
params: {
key: 'TEST_VARIABLE_2',
value: 'PROTECTED_VALUE_2',
@@ -76,7 +68,6 @@ RSpec.describe ::API::Admin::Ci::Variables do
}
end.to change { ::Ci::InstanceVariable.count }.by(1)
- expect(response).to have_gitlab_http_status(:created)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('PROTECTED_VALUE_2')
expect(json_response['protected']).to be_truthy
@@ -90,13 +81,13 @@ RSpec.describe ::API::Admin::Ci::Variables do
expect(::API::API::LOGGER).to receive(:info).with(include(params: include(masked_params)))
- post api("/admin/ci/variables", user),
+ post api(path, user, admin_mode: true),
params: { key: 'VAR_KEY', value: 'SENSITIVE', protected: true, masked: true }
end
it 'creates variable with optional attributes', :aggregate_failures do
expect do
- post api('/admin/ci/variables', admin),
+ post api(path, admin, admin_mode: true),
params: {
variable_type: 'file',
key: 'TEST_VARIABLE_2',
@@ -104,7 +95,6 @@ RSpec.describe ::API::Admin::Ci::Variables do
}
end.to change { ::Ci::InstanceVariable.count }.by(1)
- expect(response).to have_gitlab_http_status(:created)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('VALUE_2')
expect(json_response['protected']).to be_falsey
@@ -115,20 +105,20 @@ RSpec.describe ::API::Admin::Ci::Variables do
it 'does not allow to duplicate variable key' do
expect do
- post api('/admin/ci/variables', admin),
+ post api(path, admin, admin_mode: true),
params: { key: variable.key, value: 'VALUE_2' }
end.not_to change { ::Ci::InstanceVariable.count }
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'does not allow values above 10,000 characters' do
+ it 'does not allow values above 10,000 characters', :aggregate_failures do
too_long_message = <<~MESSAGE.strip
The value of the provided variable exceeds the 10000 character limit
MESSAGE
expect do
- post api('/admin/ci/variables', admin),
+ post api(path, admin, admin_mode: true),
params: { key: 'too_long', value: SecureRandom.hex(10_001) }
end.not_to change { ::Ci::InstanceVariable.count }
@@ -138,17 +128,9 @@ RSpec.describe ::API::Admin::Ci::Variables do
end
end
- context 'authorized user with invalid permissions' do
- it 'does not create variable' do
- post api('/admin/ci/variables', user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
context 'unauthorized user' do
it 'does not create variable' do
- post api('/admin/ci/variables')
+ post api(path, admin_mode: true)
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -156,20 +138,23 @@ RSpec.describe ::API::Admin::Ci::Variables do
end
describe 'PUT /admin/ci/variables/:key' do
- let!(:variable) { create(:ci_instance_variable) }
+ let_it_be(:path) { "/admin/ci/variables/#{variable.key}" }
+ let_it_be(:params) do
+ {
+ variable_type: 'file',
+ value: 'VALUE_1_UP',
+ protected: true,
+ masked: true,
+ raw: true
+ }
+ end
+
+ it_behaves_like 'PUT request permissions for admin mode'
context 'authorized user with proper permissions' do
it 'updates variable data', :aggregate_failures do
- put api("/admin/ci/variables/#{variable.key}", admin),
- params: {
- variable_type: 'file',
- value: 'VALUE_1_UP',
- protected: true,
- masked: true,
- raw: true
- }
-
- expect(response).to have_gitlab_http_status(:ok)
+ put api(path, admin, admin_mode: true), params: params
+
expect(variable.reload.value).to eq('VALUE_1_UP')
expect(variable.reload).to be_protected
expect(json_response['variable_type']).to eq('file')
@@ -182,28 +167,20 @@ RSpec.describe ::API::Admin::Ci::Variables do
expect(::API::API::LOGGER).to receive(:info).with(include(params: include(masked_params)))
- put api("/admin/ci/variables/#{variable.key}", admin),
+ put api(path, admin, admin_mode: true),
params: { value: 'SENSITIVE', protected: true, masked: true }
end
it 'responds with 404 Not Found if requesting non-existing variable' do
- put api('/admin/ci/variables/non_existing_variable', admin)
+ put api('/admin/ci/variables/non_existing_variable', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'authorized user with invalid permissions' do
- it 'does not update variable' do
- put api("/admin/ci/variables/#{variable.key}", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
context 'unauthorized user' do
it 'does not update variable' do
- put api("/admin/ci/variables/#{variable.key}")
+ put api(path, admin_mode: true)
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -211,35 +188,27 @@ RSpec.describe ::API::Admin::Ci::Variables do
end
describe 'DELETE /admin/ci/variables/:key' do
- let!(:variable) { create(:ci_instance_variable) }
+ let_it_be(:path) { "/admin/ci/variables/#{variable.key}" }
+
+ it_behaves_like 'DELETE request permissions for admin mode'
context 'authorized user with proper permissions' do
it 'deletes variable' do
expect do
- delete api("/admin/ci/variables/#{variable.key}", admin)
-
- expect(response).to have_gitlab_http_status(:no_content)
+ delete api(path, admin, admin_mode: true)
end.to change { ::Ci::InstanceVariable.count }.by(-1)
end
it 'responds with 404 Not Found if requesting non-existing variable' do
- delete api('/admin/ci/variables/non_existing_variable', admin)
+ delete api('/admin/ci/variables/non_existing_variable', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'authorized user with invalid permissions' do
- it 'does not delete variable' do
- delete api("/admin/ci/variables/#{variable.key}", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
context 'unauthorized user' do
it 'does not delete variable' do
- delete api("/admin/ci/variables/#{variable.key}")
+ delete api(path, admin_mode: true)
expect(response).to have_gitlab_http_status(:unauthorized)
end
diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb
index 7b510f74fd4..0a72f404e89 100644
--- a/spec/requests/api/admin/instance_clusters_spec.rb
+++ b/spec/requests/api/admin/instance_clusters_spec.rb
@@ -5,7 +5,6 @@ require 'spec_helper'
RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_management do
include KubernetesHelpers
- let_it_be(:regular_user) { create(:user) }
let_it_be(:admin_user) { create(:admin) }
let_it_be(:project) { create(:project) }
let_it_be(:project_cluster) do
@@ -17,35 +16,27 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
let(:project_cluster_id) { project_cluster.id }
describe "GET /admin/clusters" do
+ let_it_be(:path) { "/admin/clusters" }
let_it_be(:clusters) 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
+ it_behaves_like 'GET request permissions for admin mode'
- context "when authenticated as a non-admin user" do
- it 'returns 403' do
- get api('/admin/clusters', regular_user)
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ include_examples ':certificate_based_clusters feature flag API responses' do
+ let(:subject) { get api(path, admin_user, admin_mode: true) }
end
context "when authenticated as admin" do
before do
- get api("/admin/clusters", admin_user)
- end
-
- it 'returns 200' do
- expect(response).to have_gitlab_http_status(:ok)
+ get api(path, admin_user, admin_mode: true)
end
it 'includes pagination headers' do
expect(response).to include_pagination_headers
end
- it 'only returns the instance clusters' do
+ it 'only returns the instance clusters', :aggregate_failures do
cluster_ids = json_response.map { |cluster| cluster['id'] }
expect(cluster_ids).to match_array(clusters.pluck(:id))
expect(cluster_ids).not_to include(project_cluster_id)
@@ -60,19 +51,23 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
let_it_be(:cluster) do
create(:cluster, :instance, :provided_by_gcp, :with_domain,
- platform_kubernetes: platform_kubernetes,
- user: admin_user)
+ { platform_kubernetes: platform_kubernetes,
+ user: admin_user })
end
let(:cluster_id) { cluster.id }
+ let(:path) { "/admin/clusters/#{cluster_id}" }
+
+ it_behaves_like 'GET request permissions for admin mode'
+
include_examples ':certificate_based_clusters feature flag API responses' do
- let(:subject) { get api("/admin/clusters/#{cluster_id}", admin_user) }
+ let(:subject) { get api(path, admin_user, admin_mode: true) }
end
context "when authenticated as admin" do
before do
- get api("/admin/clusters/#{cluster_id}", admin_user)
+ get api(path, admin_user, admin_mode: true)
end
context "when no cluster associated to the ID" do
@@ -84,15 +79,11 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
end
context "when cluster with cluster_id exists" do
- it 'returns 200' do
- expect(response).to have_gitlab_http_status(:ok)
- end
-
it 'returns the cluster with cluster_id' do
expect(json_response['id']).to eq(cluster.id)
end
- it 'returns the cluster information' do
+ it 'returns the cluster information', :aggregate_failures do
expect(json_response['provider_type']).to eq('gcp')
expect(json_response['platform_type']).to eq('kubernetes')
expect(json_response['environment_scope']).to eq('*')
@@ -102,21 +93,21 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
expect(json_response['managed']).to be_truthy
end
- it 'returns kubernetes platform information' do
+ it 'returns kubernetes platform information', :aggregate_failures do
platform = json_response['platform_kubernetes']
expect(platform['api_url']).to eq('https://kubernetes.example.com')
expect(platform['ca_cert']).to be_present
end
- it 'returns user information' do
+ it 'returns user information', :aggregate_failures do
user = json_response['user']
expect(user['id']).to eq(admin_user.id)
expect(user['username']).to eq(admin_user.username)
end
- it 'returns GCP provider information' do
+ it 'returns GCP provider information', :aggregate_failures do
gcp_provider = json_response['provider_gcp']
expect(gcp_provider['cluster_id']).to eq(cluster.id)
@@ -140,18 +131,11 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
context 'when trying to get a project cluster via the instance cluster endpoint' do
it 'returns 404' do
- get api("/admin/clusters/#{project_cluster_id}", admin_user)
+ get api("/admin/clusters/#{project_cluster_id}", admin_user, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
-
- context "when authenticated as a non-admin user" do
- it 'returns 403' do
- get api("/admin/clusters/#{cluster_id}", regular_user)
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
end
@@ -159,6 +143,7 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
let(:api_url) { 'https://example.com' }
let(:authorization_type) { 'rbac' }
let(:clusterable) { Clusters::Instance.new }
+ let_it_be(:path) { '/admin/clusters/add' }
let(:platform_kubernetes_attributes) do
{
@@ -196,20 +181,20 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
}
end
+ it_behaves_like 'POST request permissions for admin mode' do
+ let(:params) { cluster_params }
+ end
+
include_examples ':certificate_based_clusters feature flag API responses' do
- let(:subject) { post api('/admin/clusters/add', admin_user), params: cluster_params }
+ let(:subject) { post api(path, admin_user, admin_mode: true), params: cluster_params }
end
context 'authorized user' do
before do
- post api('/admin/clusters/add', admin_user), params: cluster_params
+ post api(path, admin_user, admin_mode: true), params: cluster_params
end
context 'with valid params' do
- it 'responds with 201' do
- expect(response).to have_gitlab_http_status(:created)
- end
-
it 'creates a new Clusters::Cluster', :aggregate_failures do
cluster_result = Clusters::Cluster.find(json_response["id"])
platform_kubernetes = cluster_result.platform
@@ -271,7 +256,7 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
context 'when an instance cluster already exists' do
it 'allows user to add multiple clusters' do
- post api('/admin/clusters/add', admin_user), params: multiple_cluster_params
+ post api(path, admin_user, admin_mode: true), params: multiple_cluster_params
expect(Clusters::Instance.new.clusters.count).to eq(2)
end
@@ -280,8 +265,8 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
context 'with invalid params' do
context 'when missing a required parameter' do
- it 'responds with 400' do
- post api('/admin/clusters/add', admin_user), params: invalid_cluster_params
+ it 'responds with 400', :aggregate_failures do
+ post api(path, admin_user, admin_mode: true), params: invalid_cluster_params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eql('name is missing')
end
@@ -300,14 +285,6 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
end
end
end
-
- context 'non-authorized user' do
- it 'responds with 403' do
- post api('/admin/clusters/add', regular_user), params: cluster_params
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
describe 'PUT /admin/clusters/:cluster_id' do
@@ -329,23 +306,25 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
create(:cluster, :instance, :provided_by_gcp, domain: 'old-domain.com')
end
+ let(:path) { "/admin/clusters/#{cluster.id}" }
+
+ it_behaves_like 'PUT request permissions for admin mode' do
+ let(:params) { update_params }
+ end
+
include_examples ':certificate_based_clusters feature flag API responses' do
- let(:subject) { put api("/admin/clusters/#{cluster.id}", admin_user), params: update_params }
+ let(:subject) { put api(path, admin_user, admin_mode: true), params: update_params }
end
context 'authorized user' do
before do
- put api("/admin/clusters/#{cluster.id}", admin_user), params: update_params
+ put api(path, admin_user, admin_mode: true), params: update_params
cluster.reload
end
context 'with valid params' do
- it 'responds with 200' do
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'updates cluster attributes' do
+ it 'updates cluster attributes', :aggregate_failures do
expect(cluster.domain).to eq('new-domain.com')
expect(cluster.managed).to be_falsy
expect(cluster.enabled).to be_falsy
@@ -359,7 +338,7 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'does not update cluster attributes' do
+ it 'does not update cluster attributes', :aggregate_failures do
expect(cluster.domain).to eq('old-domain.com')
expect(cluster.managed).to be_truthy
expect(cluster.enabled).to be_truthy
@@ -422,7 +401,7 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
expect(response).to have_gitlab_http_status(:ok)
end
- it 'updates platform kubernetes attributes' do
+ it 'updates platform kubernetes attributes', :aggregate_failures do
platform_kubernetes = cluster.platform_kubernetes
expect(cluster.name).to eq('new-name')
@@ -435,26 +414,18 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
let(:cluster_id) { 1337 }
it 'returns 404' do
- put api("/admin/clusters/#{cluster_id}", admin_user), params: update_params
+ put api("/admin/clusters/#{cluster_id}", admin_user, admin_mode: true), params: update_params
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when trying to update a project cluster via the instance cluster endpoint' do
it 'returns 404' do
- put api("/admin/clusters/#{project_cluster_id}", admin_user), params: update_params
+ put api("/admin/clusters/#{project_cluster_id}", admin_user, admin_mode: true), params: update_params
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
-
- context 'non-authorized user' do
- it 'responds with 403' do
- put api("/admin/clusters/#{cluster.id}", regular_user), params: update_params
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
describe 'DELETE /admin/clusters/:cluster_id' do
@@ -464,17 +435,17 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
create(:cluster, :instance, :provided_by_gcp)
end
+ let_it_be(:path) { "/admin/clusters/#{cluster.id}" }
+
+ it_behaves_like 'DELETE request permissions for admin mode'
+
include_examples ':certificate_based_clusters feature flag API responses' do
- let(:subject) { delete api("/admin/clusters/#{cluster.id}", admin_user), params: cluster_params }
+ let(:subject) { delete api(path, admin_user, admin_mode: true), params: cluster_params }
end
context 'authorized user' do
before do
- delete api("/admin/clusters/#{cluster.id}", admin_user), params: cluster_params
- end
-
- it 'responds with 204' do
- expect(response).to have_gitlab_http_status(:no_content)
+ delete api(path, admin_user, admin_mode: true), params: cluster_params
end
it 'deletes the cluster' do
@@ -485,25 +456,17 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :kubernetes_man
let(:cluster_id) { 1337 }
it 'returns 404' do
- delete api("/admin/clusters/#{cluster_id}", admin_user)
+ delete api(path, admin_user, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when trying to update a project cluster via the instance cluster endpoint' do
it 'returns 404' do
- delete api("/admin/clusters/#{project_cluster_id}", admin_user)
+ delete api("/admin/clusters/#{project_cluster_id}", admin_user, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
-
- context 'non-authorized user' do
- it 'responds with 403' do
- delete api("/admin/clusters/#{cluster.id}", regular_user), params: cluster_params
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
end
end
diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb
index 2de7a66d803..dffe062c031 100644
--- a/spec/requests/api/admin/plan_limits_spec.rb
+++ b/spec/requests/api/admin/plan_limits_spec.rb
@@ -2,26 +2,19 @@
require 'spec_helper'
-RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owned do
- let_it_be(:user) { create(:user) }
+RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared do
let_it_be(:admin) { create(:admin) }
let_it_be(:plan) { create(:plan, name: 'default') }
+ let_it_be(:path) { '/application/plan_limits' }
describe 'GET /application/plan_limits' do
- context 'as a non-admin user' do
- it 'returns 403' do
- get api('/application/plan_limits', user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
+ it_behaves_like 'GET request permissions for admin mode'
context 'as an admin user' do
context 'no params' do
- it 'returns plan limits' do
- get api('/application/plan_limits', admin)
+ it 'returns plan limits', :aggregate_failures do
+ get api(path, admin, admin_mode: true)
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['ci_pipeline_size']).to eq(Plan.default.actual_limits.ci_pipeline_size)
expect(json_response['ci_active_jobs']).to eq(Plan.default.actual_limits.ci_active_jobs)
@@ -49,8 +42,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
@params = { plan_name: 'default' }
end
- it 'returns plan limits' do
- get api('/application/plan_limits', admin), params: @params
+ it 'returns plan limits', :aggregate_failures do
+ get api(path, admin, admin_mode: true), params: @params
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
@@ -80,8 +73,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
@params = { plan_name: 'my-plan' }
end
- it 'returns validation error' do
- get api('/application/plan_limits', admin), params: @params
+ it 'returns validation error', :aggregate_failures do
+ get api(path, admin, admin_mode: true), params: @params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('plan_name does not have a valid value')
@@ -91,18 +84,14 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
end
describe 'PUT /application/plan_limits' do
- context 'as a non-admin user' do
- it 'returns 403' do
- put api('/application/plan_limits', user), params: { plan_name: 'default' }
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'PUT request permissions for admin mode' do
+ let(:params) { { 'plan_name': 'default' } }
end
context 'as an admin user' do
context 'correct params' do
- it 'updates multiple plan limits' do
- put api('/application/plan_limits', admin), params: {
+ it 'updates multiple plan limits', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: {
'plan_name': 'default',
'ci_pipeline_size': 101,
'ci_active_jobs': 102,
@@ -124,7 +113,6 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
'pipeline_hierarchy_size': 250
}
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['ci_pipeline_size']).to eq(101)
expect(json_response['ci_active_jobs']).to eq(102)
@@ -146,8 +134,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
expect(json_response['pipeline_hierarchy_size']).to eq(250)
end
- it 'updates single plan limits' do
- put api('/application/plan_limits', admin), params: {
+ it 'updates single plan limits', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: {
'plan_name': 'default',
'maven_max_file_size': 100
}
@@ -159,8 +147,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
end
context 'empty params' do
- it 'fails to update plan limits' do
- put api('/application/plan_limits', admin), params: {}
+ it 'fails to update plan limits', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: {}
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to match('plan_name is missing')
@@ -168,8 +156,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
end
context 'params with wrong type' do
- it 'fails to update plan limits' do
- put api('/application/plan_limits', admin), params: {
+ it 'fails to update plan limits', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: {
'plan_name': 'default',
'ci_pipeline_size': 'z',
'ci_active_jobs': 'y',
@@ -216,8 +204,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
end
context 'missing plan_name in params' do
- it 'fails to update plan limits' do
- put api('/application/plan_limits', admin), params: { 'conan_max_file_size': 0 }
+ it 'fails to update plan limits', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: { 'conan_max_file_size': 0 }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to match('plan_name is missing')
@@ -229,8 +217,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :not_owne
Plan.default.actual_limits.update!({ 'golang_max_file_size': 1000 })
end
- it 'updates only declared plan limits' do
- put api('/application/plan_limits', admin), params: {
+ it 'updates only declared plan limits', :aggregate_failures do
+ put api(path, admin, admin_mode: true), params: {
'plan_name': 'default',
'pypi_max_file_size': 200,
'golang_max_file_size': 999
diff --git a/spec/requests/api/admin/sidekiq_spec.rb b/spec/requests/api/admin/sidekiq_spec.rb
index 0b456721d4f..8bcd7884fd2 100644
--- a/spec/requests/api/admin/sidekiq_spec.rb
+++ b/spec/requests/api/admin/sidekiq_spec.rb
@@ -2,18 +2,10 @@
require 'spec_helper'
-RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues, feature_category: :not_owned do
+RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues, feature_category: :shared do
let_it_be(:admin) { create(:admin) }
describe 'DELETE /admin/sidekiq/queues/:queue_name' do
- context 'when the user is not an admin' do
- it 'returns a 403' do
- delete api("/admin/sidekiq/queues/authorized_projects?user=#{admin.username}", create(:user))
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
context 'when the user is an admin' do
around do |example|
Sidekiq::Queue.new('authorized_projects').clear
@@ -31,14 +23,19 @@ RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues, feature_category
end
context 'valid request' do
- it 'returns info about the deleted jobs' do
+ before do
add_job(admin, [1])
add_job(admin, [2])
add_job(create(:user), [3])
+ end
+
+ let_it_be(:path) { "/admin/sidekiq/queues/authorized_projects?user=#{admin.username}&worker_class=AuthorizedProjectsWorker" }
- delete api("/admin/sidekiq/queues/authorized_projects?user=#{admin.username}&worker_class=AuthorizedProjectsWorker", admin)
+ it_behaves_like 'DELETE request permissions for admin mode', success_status_code: :ok
+
+ it 'returns info about the deleted jobs' do
+ delete api(path, admin, admin_mode: true)
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq('completed' => true,
'deleted_jobs' => 2,
'queue_size' => 1)
@@ -47,7 +44,7 @@ RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues, feature_category
context 'when no required params are provided' do
it 'returns a 400' do
- delete api("/admin/sidekiq/queues/authorized_projects?user_2=#{admin.username}", admin)
+ delete api("/admin/sidekiq/queues/authorized_projects?user_2=#{admin.username}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -55,7 +52,7 @@ RSpec.describe API::Admin::Sidekiq, :clean_gitlab_redis_queues, feature_category
context 'when the queue does not exist' do
it 'returns a 404' do
- delete api("/admin/sidekiq/queues/authorized_projects_2?user=#{admin.username}", admin)
+ delete api("/admin/sidekiq/queues/authorized_projects_2?user=#{admin.username}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
index 21f3691c20b..7268fa2c90b 100644
--- a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
+++ b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::APIGuard::AdminModeMiddleware, :request_store, feature_category: :not_owned do
+RSpec.describe API::APIGuard::AdminModeMiddleware, :request_store, feature_category: :shared do
let(:user) { create(:admin) }
it 'is loaded' do
diff --git a/spec/requests/api/api_guard/response_coercer_middleware_spec.rb b/spec/requests/api/api_guard/response_coercer_middleware_spec.rb
index 77498c2e2b3..4a993d0b255 100644
--- a/spec/requests/api/api_guard/response_coercer_middleware_spec.rb
+++ b/spec/requests/api/api_guard/response_coercer_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::APIGuard::ResponseCoercerMiddleware, feature_category: :not_owned do
+RSpec.describe API::APIGuard::ResponseCoercerMiddleware, feature_category: :shared do
using RSpec::Parameterized::TableSyntax
it 'is loaded' do
diff --git a/spec/requests/api/api_spec.rb b/spec/requests/api/api_spec.rb
index 35851fff6c8..d5ad0779bd9 100644
--- a/spec/requests/api/api_spec.rb
+++ b/spec/requests/api/api_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::API, feature_category: :authentication_and_authorization do
+RSpec.describe API::API, feature_category: :system_access do
include GroupAPIHelpers
describe 'Record user last activity in after hook' do
diff --git a/spec/requests/api/appearance_spec.rb b/spec/requests/api/appearance_spec.rb
index c08ecae28e8..3550e51d585 100644
--- a/spec/requests/api/appearance_spec.rb
+++ b/spec/requests/api/appearance_spec.rb
@@ -36,7 +36,9 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
end
describe "PUT /application/appearance" do
- it_behaves_like 'PUT request permissions for admin mode', { title: "Test" }
+ it_behaves_like 'PUT request permissions for admin mode' do
+ let(:params) { { title: "Test" } }
+ end
context 'as an admin user' do
context "instance basics" do
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
index b81cdcfea8e..5b07bded82c 100644
--- a/spec/requests/api/applications_spec.rb
+++ b/spec/requests/api/applications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Applications, :api, feature_category: :authentication_and_authorization do
+RSpec.describe API::Applications, :api, feature_category: :system_access do
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
let_it_be(:scopes) { 'api' }
@@ -10,7 +10,9 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
let!(:application) { create(:application, name: 'another_application', owner: nil, redirect_uri: 'http://other_application.url', scopes: scopes) }
describe 'POST /applications' do
- it_behaves_like 'POST request permissions for admin mode', { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api' }
+ it_behaves_like 'POST request permissions for admin mode' do
+ let(:params) { { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api' } }
+ end
context 'authenticated and authorized user' do
it 'creates and returns an OAuth application' do
@@ -22,7 +24,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
expect(json_response).to be_a Hash
expect(json_response['application_id']).to eq application.uid
- expect(json_response['secret']).to eq application.secret
+ expect(application.secret_matches?(json_response['secret'])).to eq(true)
expect(json_response['callback_url']).to eq application.redirect_uri
expect(json_response['confidential']).to eq application.confidential
expect(application.scopes.to_s).to eq('api')
diff --git a/spec/requests/api/avatar_spec.rb b/spec/requests/api/avatar_spec.rb
index fcef5b6ca78..0a77b6e228e 100644
--- a/spec/requests/api/avatar_spec.rb
+++ b/spec/requests/api/avatar_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe API::Avatar, feature_category: :user_profile do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['avatar_url']).to eql("#{::Settings.gitlab.base_url}#{user.avatar.local_url}")
+ is_expected.to have_request_urgency(:medium)
end
end
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 87dc06b7d15..22c67a253e3 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::AwardEmoji, feature_category: :not_owned do
+RSpec.describe API::AwardEmoji, feature_category: :shared do
let_it_be_with_reload(:project) { create(:project, :private) }
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb
index ee390773f29..7cea744cdb9 100644
--- a/spec/requests/api/ci/job_artifacts_spec.rb
+++ b/spec/requests/api/ci/job_artifacts_spec.rb
@@ -190,7 +190,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
end
context 'when project is public with artifacts that are non public' do
- let(:job) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline) }
it 'rejects access to artifacts' do
project.update_column(:visibility_level,
@@ -439,7 +439,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
context 'when public project guest and artifacts are non public' do
let(:api_user) { guest }
- let(:job) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline) }
before do
project.update_column(:visibility_level,
@@ -644,7 +644,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
end
context 'when project is public with non public artifacts' do
- let(:job) { create(:ci_build, :artifacts, :non_public_artifacts, pipeline: pipeline, user: api_user) }
+ let(:job) { create(:ci_build, :artifacts, :with_private_artifacts_config, pipeline: pipeline, user: api_user) }
let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
let(:public_builds) { true }
diff --git a/spec/requests/api/ci/pipeline_schedules_spec.rb b/spec/requests/api/ci/pipeline_schedules_spec.rb
index 2a2c5f65aee..d760e4ddf28 100644
--- a/spec/requests/api/ci/pipeline_schedules_spec.rb
+++ b/spec/requests/api/ci/pipeline_schedules_spec.rb
@@ -473,12 +473,12 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
end
context 'as the existing owner of the schedule' do
- it 'rejects the request and leaves the schedule unchanged' do
+ it 'accepts the request and leaves the schedule unchanged' do
expect do
post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership", developer)
end.not_to change { pipeline_schedule.reload.owner }
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:success)
end
end
end
diff --git a/spec/requests/api/ci/pipelines_spec.rb b/spec/requests/api/ci/pipelines_spec.rb
index 6d69da85449..4e81a052ecf 100644
--- a/spec/requests/api/ci/pipelines_spec.rb
+++ b/spec/requests/api/ci/pipelines_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
it_behaves_like 'pipelines visibility table'
context 'authorized user' do
- it 'returns project pipelines' do
+ it 'returns project pipelines', :aggregate_failures do
get api("/projects/#{project.id}/pipelines", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -52,7 +52,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
create(:ci_pipeline, project: project, status: target)
end
- it 'returns matched pipelines' do
+ it 'returns matched pipelines', :aggregate_failures do
get api("/projects/#{project.id}/pipelines", user), params: { scope: target }
expect(response).to have_gitlab_http_status(:ok)
@@ -307,7 +307,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not return project pipelines' do
+ it 'does not return project pipelines', :aggregate_failures do
get api("/projects/#{project.id}/pipelines", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -335,13 +335,13 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'authorized user' do
- it 'returns pipeline jobs' do
+ it 'returns pipeline jobs', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
end
- it 'returns correct values' do
+ it 'returns correct values', :aggregate_failures do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
@@ -354,7 +354,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let(:api_endpoint) { "/projects/#{project.id}/pipelines/#{pipeline.id}/jobs" }
end
- it 'returns pipeline data' do
+ it 'returns pipeline data', :aggregate_failures do
json_job = json_response.first
expect(json_job['pipeline']).not_to be_empty
@@ -368,7 +368,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'filter jobs with one scope element' do
let(:query) { { 'scope' => 'pending' } }
- it do
+ it :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
@@ -382,7 +382,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when filtering to only running jobs' do
let(:query) { { 'scope' => 'running' } }
- it do
+ it :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
@@ -402,7 +402,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'filter jobs with array of scope elements' do
let(:query) { { scope: %w(pending running) } }
- it do
+ it :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
end
@@ -442,7 +442,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let_it_be(:successor) { create(:ci_build, :success, name: 'build', pipeline: pipeline) }
- it 'does not return retried jobs by default' do
+ it 'does not return retried jobs by default', :aggregate_failures do
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
end
@@ -450,7 +450,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when include_retried is false' do
let(:query) { { include_retried: false } }
- it 'does not return retried jobs' do
+ it 'does not return retried jobs', :aggregate_failures do
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
end
@@ -459,7 +459,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when include_retried is true' do
let(:query) { { include_retried: true } }
- it 'returns retried jobs' do
+ it 'returns retried jobs', :aggregate_failures do
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
expect(json_response[0]['name']).to eq(json_response[1]['name'])
@@ -469,7 +469,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'no pipeline is found' do
- it 'does not return jobs' do
+ it 'does not return jobs', :aggregate_failures do
get api("/projects/#{project2.id}/pipelines/#{pipeline.id}/jobs", user)
expect(json_response['message']).to eq '404 Project Not Found'
@@ -481,7 +481,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when user is not logged in' do
let(:api_user) { nil }
- it 'does not return jobs' do
+ it 'does not return jobs', :aggregate_failures do
expect(json_response['message']).to eq '404 Project Not Found'
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -523,13 +523,13 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'authorized user' do
- it 'returns pipeline bridges' do
+ it 'returns pipeline bridges', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
end
- it 'returns correct values' do
+ it 'returns correct values', :aggregate_failures do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
expect(json_response.first['id']).to eq bridge.id
@@ -537,7 +537,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
expect(json_response.first['stage']).to eq bridge.stage
end
- it 'returns pipeline data' do
+ it 'returns pipeline data', :aggregate_failures do
json_bridge = json_response.first
expect(json_bridge['pipeline']).not_to be_empty
@@ -548,7 +548,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
expect(json_bridge['pipeline']['status']).to eq bridge.pipeline.status
end
- it 'returns downstream pipeline data' do
+ it 'returns downstream pipeline data', :aggregate_failures do
json_bridge = json_response.first
expect(json_bridge['downstream_pipeline']).not_to be_empty
@@ -568,7 +568,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'with one scope element' do
let(:query) { { 'scope' => 'pending' } }
- it :skip_before_request do
+ it :skip_before_request, :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/bridges", api_user), params: query
expect(response).to have_gitlab_http_status(:ok)
@@ -581,7 +581,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'with array of scope elements' do
let(:query) { { scope: %w(pending running) } }
- it :skip_before_request do
+ it :skip_before_request, :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/bridges", api_user), params: query
expect(response).to have_gitlab_http_status(:ok)
@@ -635,7 +635,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'no pipeline is found' do
- it 'does not return bridges' do
+ it 'does not return bridges', :aggregate_failures do
get api("/projects/#{project2.id}/pipelines/#{pipeline.id}/bridges", user)
expect(json_response['message']).to eq '404 Project Not Found'
@@ -647,7 +647,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when user is not logged in' do
let(:api_user) { nil }
- it 'does not return bridges' do
+ it 'does not return bridges', :aggregate_failures do
expect(json_response['message']).to eq '404 Project Not Found'
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -704,7 +704,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
stub_ci_pipeline_to_return_yaml_file
end
- it 'creates and returns a new pipeline' do
+ it 'creates and returns a new pipeline', :aggregate_failures do
expect do
post api("/projects/#{project.id}/pipeline", user), params: { ref: project.default_branch }
end.to change { project.ci_pipelines.count }.by(1)
@@ -717,7 +717,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'variables given' do
let(:variables) { [{ 'variable_type' => 'file', 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] }
- it 'creates and returns a new pipeline using the given variables' do
+ it 'creates and returns a new pipeline using the given variables', :aggregate_failures do
expect do
post api("/projects/#{project.id}/pipeline", user), params: { ref: project.default_branch, variables: variables }
end.to change { project.ci_pipelines.count }.by(1)
@@ -738,7 +738,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
stub_ci_pipeline_yaml_file(config)
end
- it 'creates and returns a new pipeline using the given variables' do
+ it 'creates and returns a new pipeline using the given variables', :aggregate_failures do
expect do
post api("/projects/#{project.id}/pipeline", user), params: { ref: project.default_branch, variables: variables }
end.to change { project.ci_pipelines.count }.by(1)
@@ -763,7 +763,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
end
- it 'fails when using an invalid ref' do
+ it 'fails when using an invalid ref', :aggregate_failures do
post api("/projects/#{project.id}/pipeline", user), params: { ref: 'invalid_ref' }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -778,7 +778,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
project.update!(auto_devops_attributes: { enabled: false })
end
- it 'fails to create pipeline' do
+ it 'fails to create pipeline', :aggregate_failures do
post api("/projects/#{project.id}/pipeline", user), params: { ref: project.default_branch }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -790,7 +790,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not create pipeline' do
+ it 'does not create pipeline', :aggregate_failures do
post api("/projects/#{project.id}/pipeline", non_member), params: { ref: project.default_branch }
expect(response).to have_gitlab_http_status(:not_found)
@@ -811,21 +811,21 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'authorized user' do
- it 'exposes known attributes' do
+ it 'exposes known attributes', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/pipeline/detail')
end
- it 'returns project pipeline' do
+ it 'returns project pipeline', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['sha']).to match(/\A\h{40}\z/)
end
- it 'returns 404 when it does not exist' do
+ it 'returns 404 when it does not exist', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{non_existing_record_id}", user)
expect(response).to have_gitlab_http_status(:not_found)
@@ -847,7 +847,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not return a project pipeline' do
+ it 'does not return a project pipeline', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -863,7 +863,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
create(:ci_pipeline, source: dangling_source, project: project)
end
- it 'returns the specified pipeline' do
+ it 'returns the specified pipeline', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{dangling_pipeline.id}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -887,7 +887,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'default repository branch' do
- it 'gets the latest pipleine' do
+ it 'gets the latest pipleine', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/latest", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -898,7 +898,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'ref parameter' do
- it 'gets the latest pipleine' do
+ it 'gets the latest pipleine', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/latest", user), params: { ref: second_branch.name }
expect(response).to have_gitlab_http_status(:ok)
@@ -910,7 +910,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not return a project pipeline' do
+ it 'does not return a project pipeline', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -926,7 +926,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let(:api_user) { user }
context 'user is a mantainer' do
- it 'returns pipeline variables empty' do
+ it 'returns pipeline variables empty', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -936,7 +936,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'with variables' do
let!(:variable) { create(:ci_pipeline_variable, pipeline: pipeline, key: 'foo', value: 'bar') }
- it 'returns pipeline variables' do
+ it 'returns pipeline variables', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -962,7 +962,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let(:api_user) { pipeline_owner_user }
let!(:variable) { create(:ci_pipeline_variable, pipeline: pipeline, key: 'foo', value: 'bar') }
- it 'returns pipeline variables' do
+ it 'returns pipeline variables', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -987,7 +987,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'user is not a project member' do
- it 'does not return pipeline variables' do
+ it 'does not return pipeline variables', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/variables", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -1000,14 +1000,14 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'authorized user' do
let(:owner) { project.first_owner }
- it 'destroys the pipeline' do
+ it 'destroys the pipeline', :aggregate_failures do
delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner)
expect(response).to have_gitlab_http_status(:no_content)
expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
- it 'returns 404 when it does not exist' do
+ it 'returns 404 when it does not exist', :aggregate_failures do
delete api("/projects/#{project.id}/pipelines/#{non_existing_record_id}", owner)
expect(response).to have_gitlab_http_status(:not_found)
@@ -1021,7 +1021,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when the pipeline has jobs' do
let_it_be(:build) { create(:ci_build, project: project, pipeline: pipeline) }
- it 'destroys associated jobs' do
+ it 'destroys associated jobs', :aggregate_failures do
delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner)
expect(response).to have_gitlab_http_status(:no_content)
@@ -1044,7 +1044,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'unauthorized user' do
context 'when user is not member' do
- it 'returns a 404' do
+ it 'returns a 404', :aggregate_failures do
delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -1059,7 +1059,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
project.add_developer(developer)
end
- it 'returns a 403' do
+ it 'returns a 403', :aggregate_failures do
delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", developer)
expect(response).to have_gitlab_http_status(:forbidden)
@@ -1078,7 +1078,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let_it_be(:build) { create(:ci_build, :failed, pipeline: pipeline) }
- it 'retries failed builds' do
+ it 'retries failed builds', :aggregate_failures do
expect do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/retry", user)
end.to change { pipeline.builds.count }.from(1).to(2)
@@ -1089,7 +1089,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not return a project pipeline' do
+ it 'does not return a project pipeline', :aggregate_failures do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/retry", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -1106,7 +1106,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
end
- it 'returns error' do
+ it 'returns error', :aggregate_failures do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/retry", user)
expect(response).to have_gitlab_http_status(:forbidden)
@@ -1124,7 +1124,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let_it_be(:build) { create(:ci_build, :running, pipeline: pipeline) }
- context 'authorized user' do
+ context 'authorized user', :aggregate_failures do
it 'retries failed builds', :sidekiq_might_not_need_inline do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", user)
@@ -1140,7 +1140,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
project.add_reporter(reporter)
end
- it 'rejects the action' do
+ it 'rejects the action', :aggregate_failures do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", reporter)
expect(response).to have_gitlab_http_status(:forbidden)
@@ -1156,7 +1156,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline does not have a test report' do
- it 'returns an empty test report' do
+ it 'returns an empty test report', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -1167,7 +1167,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when pipeline has a test report' do
let(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
- it 'returns the test report' do
+ it 'returns the test report', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -1180,7 +1180,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
create(:ci_build, :broken_test_reports, name: 'rspec', pipeline: pipeline)
end
- it 'returns a suite_error' do
+ it 'returns a suite_error', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -1190,7 +1190,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not return project pipelines' do
+ it 'does not return project pipelines', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/test_report", non_member)
expect(response).to have_gitlab_http_status(:not_found)
@@ -1208,7 +1208,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline does not have a test report summary' do
- it 'returns an empty test report summary' do
+ it 'returns an empty test report summary', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -1219,7 +1219,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
context 'when pipeline has a test report summary' do
let(:pipeline) { create(:ci_pipeline, :with_report_results, project: project) }
- it 'returns the test report summary' do
+ it 'returns the test report summary', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:ok)
@@ -1229,7 +1229,7 @@ RSpec.describe API::Ci::Pipelines, feature_category: :continuous_integration do
end
context 'unauthorized user' do
- it 'does not return project pipelines' do
+ it 'does not return project pipelines', :aggregate_failures do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/test_report_summary", non_member)
expect(response).to have_gitlab_http_status(:not_found)
diff --git a/spec/requests/api/ci/runner/jobs_put_spec.rb b/spec/requests/api/ci/runner/jobs_put_spec.rb
index ef3b38e3fc4..bf28b25e0a6 100644
--- a/spec/requests/api/ci/runner/jobs_put_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_put_spec.rb
@@ -43,6 +43,17 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
.and change { runner_machine.reload.contacted_at }
end
+ context 'when runner_machine_heartbeat is disabled' do
+ before do
+ stub_feature_flags(runner_machine_heartbeat: false)
+ end
+
+ it 'does not load runner machine' do
+ queries = ActiveRecord::QueryRecorder.new { update_job(state: 'success') }
+ expect(queries.log).not_to include(/ci_runner_machines/)
+ end
+ end
+
context 'when status is given' do
it 'marks job as succeeded' do
update_job(state: 'success')
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 6e721d40560..28dbc4fd168 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -831,19 +831,6 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
end
end
end
-
- context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
- before do
- stub_feature_flags(ci_hooks_pre_get_sources_script: false)
- end
-
- it 'does not return the pre_get_sources_script' do
- request_job
-
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response).not_to have_key('hooks')
- end
- end
end
describe 'port support' do
diff --git a/spec/requests/api/ci/runner/runners_verify_post_spec.rb b/spec/requests/api/ci/runner/runners_verify_post_spec.rb
index a6a1ad947aa..1b7dfe7706c 100644
--- a/spec/requests/api/ci/runner/runners_verify_post_spec.rb
+++ b/spec/requests/api/ci/runner/runners_verify_post_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
end
describe '/api/v4/runners' do
- describe 'POST /api/v4/runners/verify' do
+ describe 'POST /api/v4/runners/verify', :freeze_time do
let_it_be_with_reload(:runner) { create(:ci_runner, token_expires_at: 3.days.from_now) }
let(:params) {}
@@ -50,6 +50,30 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
stub_feature_flags(create_runner_machine: true)
end
+ context 'with glrt-prefixed token' do
+ let_it_be(:registration_token) { 'glrt-abcdefg123456' }
+ let_it_be(:registration_type) { :authenticated_user }
+ let_it_be(:runner) do
+ create(:ci_runner, registration_type: registration_type,
+ token: registration_token, token_expires_at: 3.days.from_now)
+ end
+
+ it 'verifies Runner credentials' do
+ verify
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq({
+ 'id' => runner.id,
+ 'token' => runner.token,
+ 'token_expires_at' => runner.token_expires_at.iso8601(3)
+ })
+ end
+
+ it 'does not update contacted_at' do
+ expect { verify }.not_to change { runner.reload.contacted_at }.from(nil)
+ end
+ end
+
it 'verifies Runner credentials' do
verify
@@ -61,6 +85,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
})
end
+ it 'updates contacted_at' do
+ expect { verify }.to change { runner.reload.contacted_at }.from(nil).to(Time.current)
+ end
+
context 'with non-expiring runner token' do
before do
runner.update!(token_expires_at: nil)
diff --git a/spec/requests/api/ci/runners_reset_registration_token_spec.rb b/spec/requests/api/ci/runners_reset_registration_token_spec.rb
index 1110dbf5fbc..98edde93e95 100644
--- a/spec/requests/api/ci/runners_reset_registration_token_spec.rb
+++ b/spec/requests/api/ci/runners_reset_registration_token_spec.rb
@@ -3,10 +3,12 @@
require 'spec_helper'
RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
- subject { post api("#{prefix}/runners/reset_registration_token", user) }
+ let_it_be(:admin_mode) { false }
+
+ subject { post api("#{prefix}/runners/reset_registration_token", user, admin_mode: admin_mode) }
shared_examples 'bad request' do |result|
- it 'returns 400 error' do
+ it 'returns 400 error', :aggregate_failures do
expect { subject }.not_to change { get_token }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -15,7 +17,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
shared_examples 'unauthenticated' do
- it 'returns 401 error' do
+ it 'returns 401 error', :aggregate_failures do
expect { subject }.not_to change { get_token }
expect(response).to have_gitlab_http_status(:unauthorized)
@@ -23,7 +25,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
shared_examples 'unauthorized' do
- it 'returns 403 error' do
+ it 'returns 403 error', :aggregate_failures do
expect { subject }.not_to change { get_token }
expect(response).to have_gitlab_http_status(:forbidden)
@@ -31,7 +33,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
shared_examples 'not found' do |scope|
- it 'returns 404 error' do
+ it 'returns 404 error', :aggregate_failures do
expect { subject }.not_to change { get_token }
expect(response).to have_gitlab_http_status(:not_found)
@@ -58,7 +60,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
shared_context 'when authorized' do |scope|
- it 'resets runner registration token' do
+ it 'resets runner registration token', :aggregate_failures do
expect { subject }.to change { get_token }
expect(response).to have_gitlab_http_status(:success)
@@ -99,6 +101,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
include_context 'when authorized', 'instance' do
let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:admin_mode) { true }
def get_token
ApplicationSetting.current_without_cache.runners_registration_token
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index ca051386265..ec9b5621c37 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
describe 'GET /runners' do
context 'authorized user' do
- it 'returns response status and headers' do
+ it 'returns response status and headers', :aggregate_failures do
get api('/runners', user)
expect(response).to have_gitlab_http_status(:ok)
@@ -53,7 +53,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
]
end
- it 'filters runners by scope' do
+ it 'filters runners by scope', :aggregate_failures do
create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
get api('/runners?scope=paused', user)
@@ -112,7 +112,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'filters runners by tag_list' do
+ it 'filters runners by tag_list', :aggregate_failures do
create(:ci_runner, :project, description: 'Runner tagged with tag1 and tag2', projects: [project], tag_list: %w[tag1 tag2])
create(:ci_runner, :project, description: 'Runner tagged with tag2', projects: [project], tag_list: ['tag2'])
@@ -137,14 +137,14 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'authorized user' do
context 'with admin privileges' do
it 'returns response status and headers' do
- get api('/runners/all', admin)
+ get api('/runners/all', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
end
it 'returns all runners' do
- get api('/runners/all', admin)
+ get api('/runners/all', admin, admin_mode: true)
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner', 'is_shared' => false, 'active' => true, 'paused' => false, 'runner_type' => 'project_type'),
@@ -155,8 +155,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
]
end
- it 'filters runners by scope' do
- get api('/runners/all?scope=shared', admin)
+ it 'filters runners by scope', :aggregate_failures do
+ get api('/runners/all?scope=shared', admin, admin_mode: true)
shared = json_response.all? { |r| r['is_shared'] }
expect(response).to have_gitlab_http_status(:ok)
@@ -166,8 +166,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(shared).to be_truthy
end
- it 'filters runners by scope' do
- get api('/runners/all?scope=specific', admin)
+ it 'filters runners by scope', :aggregate_failures do
+ get api('/runners/all?scope=specific', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -181,12 +181,12 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'avoids filtering if scope is invalid' do
- get api('/runners/all?scope=unknown', admin)
+ get api('/runners/all?scope=unknown', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'filters runners by project type' do
- get api('/runners/all?type=project_type', admin)
+ get api('/runners/all?type=project_type', admin, admin_mode: true)
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
@@ -195,7 +195,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'filters runners by group type' do
- get api('/runners/all?type=group_type', admin)
+ get api('/runners/all?type=group_type', admin, admin_mode: true)
expect(json_response).to match_array [
a_hash_including('description' => 'Group runner A'),
@@ -204,7 +204,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'does not filter by invalid type' do
- get api('/runners/all?type=bogus', admin)
+ get api('/runners/all?type=bogus', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -213,7 +213,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
let_it_be(:runner) { create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) }
it 'filters runners by status' do
- get api('/runners/all?paused=true', admin)
+ get api('/runners/all?paused=true', admin, admin_mode: true)
expect(json_response).to match_array [
a_hash_including('description' => 'Inactive project runner')
@@ -221,7 +221,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'filters runners by status' do
- get api('/runners/all?status=paused', admin)
+ get api('/runners/all?status=paused', admin, admin_mode: true)
expect(json_response).to match_array [
a_hash_including('description' => 'Inactive project runner')
@@ -230,16 +230,16 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'does not filter by invalid status' do
- get api('/runners/all?status=bogus', admin)
+ get api('/runners/all?status=bogus', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'filters runners by tag_list' do
+ it 'filters runners by tag_list', :aggregate_failures do
create(:ci_runner, :project, description: 'Runner tagged with tag1 and tag2', projects: [project], tag_list: %w[tag1 tag2])
create(:ci_runner, :project, description: 'Runner tagged with tag2', projects: [project], tag_list: ['tag2'])
- get api('/runners/all?tag_list=tag1,tag2', admin)
+ get api('/runners/all?tag_list=tag1,tag2', admin, admin_mode: true)
expect(json_response).to match_array [
a_hash_including('description' => 'Runner tagged with tag1 and tag2')
@@ -268,7 +268,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
describe 'GET /runners/:id' do
context 'admin user' do
context 'when runner is shared' do
- it "returns runner's details" do
+ it "returns runner's details", :aggregate_failures do
get api("/runners/#{shared_runner.id}", admin)
expect(response).to have_gitlab_http_status(:ok)
@@ -284,39 +284,39 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'when unused runner is present' do
let!(:unused_project_runner) { create(:ci_runner, :project, :without_projects) }
- it 'deletes unused runner' do
+ it 'deletes unused runner', :aggregate_failures do
expect do
- delete api("/runners/#{unused_project_runner.id}", admin)
+ delete api("/runners/#{unused_project_runner.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { ::Ci::Runner.project_type.count }.by(-1)
end
end
- it "returns runner's details" do
- get api("/runners/#{project_runner.id}", admin)
+ it "returns runner's details", :aggregate_failures do
+ get api("/runners/#{project_runner.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['description']).to eq(project_runner.description)
end
it "returns the project's details for a project runner" do
- get api("/runners/#{project_runner.id}", admin)
+ get api("/runners/#{project_runner.id}", admin, admin_mode: true)
expect(json_response['projects'].first['id']).to eq(project.id)
end
end
it 'returns 404 if runner does not exist' do
- get api('/runners/0', admin)
+ get api('/runners/0', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when the runner is a group runner' do
- it "returns the runner's details" do
- get api("/runners/#{group_runner_a.id}", admin)
+ it "returns the runner's details", :aggregate_failures do
+ get api("/runners/#{group_runner_a.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['description']).to eq(group_runner_a.description)
@@ -326,7 +326,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context "runner project's administrative user" do
context 'when runner is not shared' do
- it "returns runner's details" do
+ it "returns runner's details", :aggregate_failures do
get api("/runners/#{project_runner.id}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -335,7 +335,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner is shared' do
- it "returns runner's details" do
+ it "returns runner's details", :aggregate_failures do
get api("/runners/#{shared_runner.id}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -373,7 +373,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(shared_runner.reload.description).to eq("#{description}_updated")
end
- it 'runner active state' do
+ it 'runner active state', :aggregate_failures do
active = shared_runner.active
update_runner(shared_runner.id, admin, active: !active)
@@ -381,7 +381,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(shared_runner.reload.active).to eq(!active)
end
- it 'runner paused state' do
+ it 'runner paused state', :aggregate_failures do
active = shared_runner.active
update_runner(shared_runner.id, admin, paused: active)
@@ -389,14 +389,14 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(shared_runner.reload.active).to eq(!active)
end
- it 'runner tag list' do
+ it 'runner tag list', :aggregate_failures do
update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
expect(response).to have_gitlab_http_status(:ok)
expect(shared_runner.reload.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
end
- it 'unrelated runner attribute on an existing runner with too many tags' do
+ it 'unrelated runner attribute on an existing runner with too many tags', :aggregate_failures do
# This test ensures that it is possible to update any attribute on a runner that currently fails the
# validation that ensures that there aren't too many tags associated with a runner
existing_invalid_shared_runner = build(:ci_runner, :instance, tag_list: (1..::Ci::Runner::TAG_LIST_MAX_LENGTH + 1).map { |i| "tag#{i}" })
@@ -409,7 +409,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(existing_invalid_shared_runner.reload.active).to eq(!active)
end
- it 'runner untagged flag' do
+ it 'runner untagged flag', :aggregate_failures do
# Ensure tag list is non-empty before setting untagged to false.
update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
update_runner(shared_runner.id, admin, run_untagged: 'false')
@@ -418,28 +418,28 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(shared_runner.reload.run_untagged?).to be(false)
end
- it 'runner unlocked flag' do
+ it 'runner unlocked flag', :aggregate_failures do
update_runner(shared_runner.id, admin, locked: 'true')
expect(response).to have_gitlab_http_status(:ok)
expect(shared_runner.reload.locked?).to be(true)
end
- it 'runner access level' do
+ it 'runner access level', :aggregate_failures do
update_runner(shared_runner.id, admin, access_level: 'ref_protected')
expect(response).to have_gitlab_http_status(:ok)
expect(shared_runner.reload.ref_protected?).to be_truthy
end
- it 'runner maximum timeout' do
+ it 'runner maximum timeout', :aggregate_failures do
update_runner(shared_runner.id, admin, maximum_timeout: 1234)
expect(response).to have_gitlab_http_status(:ok)
expect(shared_runner.reload.maximum_timeout).to eq(1234)
end
- it 'fails with no parameters' do
+ it 'fails with no parameters', :aggregate_failures do
put api("/runners/#{shared_runner.id}", admin)
shared_runner.reload
@@ -448,7 +448,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner is shared' do
- it 'updates runner' do
+ it 'updates runner', :aggregate_failures do
description = shared_runner.description
active = shared_runner.active
runner_queue_value = shared_runner.ensure_runner_queue_value
@@ -476,7 +476,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner is not shared' do
- it 'updates runner' do
+ it 'updates runner', :aggregate_failures do
description = project_runner.description
runner_queue_value = project_runner.ensure_runner_queue_value
@@ -498,14 +498,16 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
def update_runner(id, user, args)
- put api("/runners/#{id}", user), params: args
+ put api("/runners/#{id}", user, admin_mode: true), params: args
end
end
context 'authorized user' do
+ let_it_be(:params) { { description: 'test' } }
+
context 'when runner is shared' do
it 'does not update runner' do
- put api("/runners/#{shared_runner.id}", user), params: { description: 'test' }
+ put api("/runners/#{shared_runner.id}", user), params: params
expect(response).to have_gitlab_http_status(:forbidden)
end
@@ -518,12 +520,11 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(response).to have_gitlab_http_status(:forbidden)
end
- it 'updates project runner with access to it' do
+ it 'updates project runner with access to it', :aggregate_failures do
description = project_runner.description
- put api("/runners/#{project_runner.id}", admin), params: { description: 'test' }
+ put api("/runners/#{project_runner.id}", admin, admin_mode: true), params: params
project_runner.reload
- expect(response).to have_gitlab_http_status(:ok)
expect(project_runner.description).to eq('test')
expect(project_runner.description).not_to eq(description)
end
@@ -542,13 +543,13 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
describe 'DELETE /runners/:id' do
context 'admin user' do
context 'when runner is shared' do
- it 'deletes runner' do
+ it 'deletes runner', :aggregate_failures do
expect_next_instance_of(Ci::Runners::UnregisterRunnerService, shared_runner, admin) do |service|
expect(service).to receive(:execute).once.and_call_original
end
expect do
- delete api("/runners/#{shared_runner.id}", admin)
+ delete api("/runners/#{shared_runner.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { ::Ci::Runner.instance_type.count }.by(-1)
@@ -560,25 +561,25 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner is not shared' do
- it 'deletes used project runner' do
+ it 'deletes used project runner', :aggregate_failures do
expect_next_instance_of(Ci::Runners::UnregisterRunnerService, project_runner, admin) do |service|
expect(service).to receive(:execute).once.and_call_original
end
expect do
- delete api("/runners/#{project_runner.id}", admin)
+ delete api("/runners/#{project_runner.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { ::Ci::Runner.project_type.count }.by(-1)
end
end
- it 'returns 404 if runner does not exist' do
+ it 'returns 404 if runner does not exist', :aggregate_failures do
allow_next_instance_of(Ci::Runners::UnregisterRunnerService) do |service|
expect(service).not_to receive(:execute)
end
- delete api('/runners/0', admin)
+ delete api('/runners/0', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -603,7 +604,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(response).to have_gitlab_http_status(:forbidden)
end
- it 'deletes project runner for one owned project' do
+ it 'deletes project runner for one owned project', :aggregate_failures do
expect do
delete api("/runners/#{project_runner.id}", user)
@@ -658,7 +659,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'unauthorized user' do
- it 'does not delete project runner' do
+ it 'does not delete project runner', :aggregate_failures do
allow_next_instance_of(Ci::Runners::UnregisterRunnerService) do |service|
expect(service).not_to receive(:execute)
end
@@ -672,31 +673,31 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
describe 'POST /runners/:id/reset_authentication_token' do
context 'admin user' do
- it 'resets shared runner authentication token' do
+ it 'resets shared runner authentication token', :aggregate_failures do
expect do
- post api("/runners/#{shared_runner.id}/reset_authentication_token", admin)
+ post api("/runners/#{shared_runner.id}/reset_authentication_token", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq({ 'token' => shared_runner.reload.token, 'token_expires_at' => nil })
end.to change { shared_runner.reload.token }
end
- it 'returns 404 if runner does not exist' do
- post api('/runners/0/reset_authentication_token', admin)
+ it 'returns 404 if runner does not exist', :aggregate_failures do
+ post api('/runners/0/reset_authentication_token', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'authorized user' do
- it 'does not reset project runner authentication token without access to it' do
+ it 'does not reset project runner authentication token without access to it', :aggregate_failures do
expect do
post api("/runners/#{project_runner.id}/reset_authentication_token", user2)
expect(response).to have_gitlab_http_status(:forbidden)
end.not_to change { project_runner.reload.token }
end
- it 'resets project runner authentication token for owned project' do
+ it 'resets project runner authentication token for owned project', :aggregate_failures do
expect do
post api("/runners/#{project_runner.id}/reset_authentication_token", user)
@@ -705,7 +706,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end.to change { project_runner.reload.token }
end
- it 'does not reset group runner authentication token with guest access' do
+ it 'does not reset group runner authentication token with guest access', :aggregate_failures do
expect do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", group_guest)
@@ -713,7 +714,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end.not_to change { group_runner_a.reload.token }
end
- it 'does not reset group runner authentication token with reporter access' do
+ it 'does not reset group runner authentication token with reporter access', :aggregate_failures do
expect do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", group_reporter)
@@ -721,7 +722,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end.not_to change { group_runner_a.reload.token }
end
- it 'does not reset group runner authentication token with developer access' do
+ it 'does not reset group runner authentication token with developer access', :aggregate_failures do
expect do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", group_developer)
@@ -729,7 +730,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end.not_to change { group_runner_a.reload.token }
end
- it 'does not reset group runner authentication token with maintainer access' do
+ it 'does not reset group runner authentication token with maintainer access', :aggregate_failures do
expect do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", group_maintainer)
@@ -737,7 +738,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end.not_to change { group_runner_a.reload.token }
end
- it 'resets group runner authentication token with owner access' do
+ it 'resets group runner authentication token with owner access', :aggregate_failures do
expect do
post api("/runners/#{group_runner_a.id}/reset_authentication_token", user)
@@ -746,7 +747,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end.to change { group_runner_a.reload.token }
end
- it 'resets group runner authentication token with owner access with expiration time', :freeze_time do
+ it 'resets group runner authentication token with owner access with expiration time', :aggregate_failures, :freeze_time do
expect(group_runner_a.reload.token_expires_at).to be_nil
group.update!(runner_token_expiration_interval: 5.days)
@@ -763,7 +764,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'unauthorized user' do
- it 'does not reset authentication token' do
+ it 'does not reset authentication token', :aggregate_failures do
expect do
post api("/runners/#{shared_runner.id}/reset_authentication_token")
@@ -783,8 +784,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'admin user' do
context 'when runner exists' do
context 'when runner is shared' do
- it 'return jobs' do
- get api("/runners/#{shared_runner.id}/jobs", admin)
+ it 'return jobs', :aggregate_failures do
+ get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -795,8 +796,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner is a project runner' do
- it 'return jobs' do
- get api("/runners/#{project_runner.id}/jobs", admin)
+ it 'return jobs', :aggregate_failures do
+ get api("/runners/#{project_runner.id}/jobs", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -806,7 +807,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when user does not have authorization to see all jobs' do
- it 'shows only jobs it has permission to see' do
+ it 'shows only jobs it has permission to see', :aggregate_failures do
create(:ci_build, :running, runner: two_projects_runner, project: project)
create(:ci_build, :running, runner: two_projects_runner, project: project2)
@@ -824,8 +825,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when valid status is provided' do
- it 'return filtered jobs' do
- get api("/runners/#{project_runner.id}/jobs?status=failed", admin)
+ it 'return filtered jobs', :aggregate_failures do
+ get api("/runners/#{project_runner.id}/jobs?status=failed", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -838,8 +839,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'when valid order_by is provided' do
context 'when sort order is not specified' do
- it 'return jobs in descending order' do
- get api("/runners/#{project_runner.id}/jobs?order_by=id", admin)
+ it 'return jobs in descending order', :aggregate_failures do
+ get api("/runners/#{project_runner.id}/jobs?order_by=id", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -851,8 +852,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when sort order is specified as asc' do
- it 'return jobs sorted in ascending order' do
- get api("/runners/#{project_runner.id}/jobs?order_by=id&sort=asc", admin)
+ it 'return jobs sorted in ascending order', :aggregate_failures do
+ get api("/runners/#{project_runner.id}/jobs?order_by=id&sort=asc", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -866,7 +867,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'when invalid status is provided' do
it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?status=non-existing", admin)
+ get api("/runners/#{project_runner.id}/jobs?status=non-existing", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -874,7 +875,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'when invalid order_by is provided' do
it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?order_by=non-existing", admin)
+ get api("/runners/#{project_runner.id}/jobs?order_by=non-existing", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -882,7 +883,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'when invalid sort is provided' do
it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?sort=non-existing", admin)
+ get api("/runners/#{project_runner.id}/jobs?sort=non-existing", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -890,16 +891,16 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'avoids N+1 DB queries' do
- get api("/runners/#{shared_runner.id}/jobs", admin)
+ get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
control = ActiveRecord::QueryRecorder.new do
- get api("/runners/#{shared_runner.id}/jobs", admin)
+ get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
end
create(:ci_build, :failed, runner: shared_runner, project: project)
expect do
- get api("/runners/#{shared_runner.id}/jobs", admin)
+ get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
end.not_to exceed_query_limit(control.count)
end
@@ -925,12 +926,12 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
]).once.and_call_original
end
- get api("/runners/#{shared_runner.id}/jobs", admin), params: { per_page: 2, order_by: 'id', sort: 'desc' }
+ get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true), params: { per_page: 2, order_by: 'id', sort: 'desc' }
end
context "when runner doesn't exist" do
it 'returns 404' do
- get api('/runners/0/jobs', admin)
+ get api('/runners/0/jobs', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -948,7 +949,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner is a project runner' do
- it 'return jobs' do
+ it 'return jobs', :aggregate_failures do
get api("/runners/#{project_runner.id}/jobs", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -960,7 +961,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when valid status is provided' do
- it 'return filtered jobs' do
+ it 'return filtered jobs', :aggregate_failures do
get api("/runners/#{project_runner.id}/jobs?status=failed", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -1027,8 +1028,8 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
describe 'GET /projects/:id/runners' do
context 'authorized user with maintainer privileges' do
- it 'returns response status and headers' do
- get api('/runners/all', admin)
+ it 'returns response status and headers', :aggregate_failures do
+ get api('/runners/all', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -1044,7 +1045,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
]
end
- it 'filters runners by scope' do
+ it 'filters runners by scope', :aggregate_failures do
get api("/projects/#{project.id}/runners?scope=specific", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -1102,7 +1103,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'filters runners by tag_list' do
+ it 'filters runners by tag_list', :aggregate_failures do
create(:ci_runner, :project, description: 'Runner tagged with tag1 and tag2', projects: [project], tag_list: %w[tag1 tag2])
create(:ci_runner, :project, description: 'Runner tagged with tag2', projects: [project], tag_list: ['tag2'])
@@ -1183,7 +1184,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
end
- it 'filters runners by tag_list' do
+ it 'filters runners by tag_list', :aggregate_failures do
create(:ci_runner, :group, description: 'Runner tagged with tag1 and tag2', groups: [group], tag_list: %w[tag1 tag2])
create(:ci_runner, :group, description: 'Runner tagged with tag2', groups: [group], tag_list: %w[tag1])
@@ -1203,21 +1204,21 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'authorized user' do
let_it_be(:project_runner2) { create(:ci_runner, :project, projects: [project2]) }
- it 'enables project runner' do
+ it 'enables project runner', :aggregate_failures do
expect do
post api("/projects/#{project.id}/runners", user), params: { runner_id: project_runner2.id }
end.to change { project.runners.count }.by(+1)
expect(response).to have_gitlab_http_status(:created)
end
- it 'avoids changes when enabling already enabled runner' do
+ it 'avoids changes when enabling already enabled runner', :aggregate_failures do
expect do
post api("/projects/#{project.id}/runners", user), params: { runner_id: project_runner.id }
end.to change { project.runners.count }.by(0)
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'does not enable locked runner' do
+ it 'does not enable locked runner', :aggregate_failures do
project_runner2.update!(locked: true)
expect do
@@ -1243,9 +1244,9 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
context 'when project runner is used' do
let!(:new_project_runner) { create(:ci_runner, :project) }
- it 'enables any project runner' do
+ it 'enables any project runner', :aggregate_failures do
expect do
- post api("/projects/#{project.id}/runners", admin), params: { runner_id: new_project_runner.id }
+ post api("/projects/#{project.id}/runners", admin, admin_mode: true), params: { runner_id: new_project_runner.id }
end.to change { project.runners.count }.by(+1)
expect(response).to have_gitlab_http_status(:created)
end
@@ -1255,9 +1256,9 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
create(:plan_limits, :default_plan, ci_registered_project_runners: 1)
end
- it 'does not enable project runner' do
+ it 'does not enable project runner', :aggregate_failures do
expect do
- post api("/projects/#{project.id}/runners", admin), params: { runner_id: new_project_runner.id }
+ post api("/projects/#{project.id}/runners", admin, admin_mode: true), params: { runner_id: new_project_runner.id }
end.not_to change { project.runners.count }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -1266,7 +1267,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
it 'raises an error when no runner_id param is provided' do
- post api("/projects/#{project.id}/runners", admin)
+ post api("/projects/#{project.id}/runners", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -1316,7 +1317,7 @@ RSpec.describe API::Ci::Runners, feature_category: :runner_fleet do
end
context 'when runner have one associated projects' do
- it "does not disable project's runner" do
+ it "does not disable project's runner", :aggregate_failures do
expect do
delete api("/projects/#{project.id}/runners/#{project_runner.id}", user)
end.to change { project.runners.count }.by(0)
diff --git a/spec/requests/api/ci/variables_spec.rb b/spec/requests/api/ci/variables_spec.rb
index 0f9f1bc80d6..5ea9104cb15 100644
--- a/spec/requests/api/ci/variables_spec.rb
+++ b/spec/requests/api/ci/variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Variables, feature_category: :pipeline_authoring do
+RSpec.describe API::Ci::Variables, feature_category: :pipeline_composition do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index bcc27a80cf8..b4bc4507021 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -132,6 +132,42 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
it_behaves_like 'project commits'
end
+ context 'with author parameter' do
+ let(:params) { { author: 'Zaporozhets' } }
+
+ it 'returns only this author commits' do
+ get api(route, user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ author_names = json_response.map { |commit| commit['author_name'] }.uniq
+
+ expect(author_names).to contain_exactly('Dmitriy Zaporozhets')
+ end
+
+ context 'when author is missing' do
+ let(:params) { { author: '' } }
+
+ it 'returns all commits' do
+ get api(route, user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.count).to eq(20)
+ end
+ end
+
+ context 'when author does not exists' do
+ let(:params) { { author: 'does not exist' } }
+
+ it 'returns an empty list' do
+ get api(route, user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq([])
+ end
+ end
+ end
+
context 'when repository does not exist' do
let(:project) { create(:project, creator: user, path: 'my.project') }
@@ -425,6 +461,27 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
describe "POST /projects/:id/repository/commits" do
let!(:url) { "/projects/#{project_id}/repository/commits" }
+ context 'when unauthenticated', 'and project is public' do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let(:params) do
+ {
+ branch: 'master',
+ commit_message: 'message',
+ actions: [
+ {
+ action: 'create',
+ file_path: '/test.rb',
+ content: 'puts 8'
+ }
+ ]
+ }
+ end
+
+ it_behaves_like '401 response' do
+ let(:request) { post api(url), params: params }
+ end
+ end
+
it 'returns a 403 unauthorized for user without permissions' do
post api(url, guest)
@@ -523,7 +580,6 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
let(:property) { 'g_edit_by_web_ide' }
let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit' }
let(:context) { [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context] }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
end
context 'counts.web_ide_commits Snowplow event tracking' do
@@ -1776,7 +1832,7 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
context 'when unauthenticated', 'and project is public' do
let_it_be(:project) { create(:project, :public, :repository) }
- it_behaves_like '403 response' do
+ it_behaves_like '401 response' do
let(:request) { post api(route), params: { branch: 'master' } }
end
end
@@ -1956,7 +2012,7 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
context 'when unauthenticated', 'and project is public' do
let_it_be(:project) { create(:project, :public, :repository) }
- it_behaves_like '403 response' do
+ it_behaves_like '401 response' do
let(:request) { post api(route), params: { branch: branch } }
end
end
diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb
index 0c726d46a01..2bb2ffa03c4 100644
--- a/spec/requests/api/composer_packages_spec.rb
+++ b/spec/requests/api/composer_packages_spec.rb
@@ -504,7 +504,11 @@ RSpec.describe API::ComposerPackages, feature_category: :package_registry do
include_context 'Composer user type', params[:user_role], params[:member] do
if params[:expected_status] == :success
let(:snowplow_gitlab_standard_context) do
- { project: project, namespace: project.namespace, property: 'i_package_composer_user' }
+ if user_role == :anonymous || (project_visibility_level == 'PUBLIC' && user_token == false)
+ { project: project, namespace: project.namespace, property: 'i_package_composer_user' }
+ else
+ { project: project, namespace: project.namespace, property: 'i_package_composer_user', user: user }
+ end
end
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index 0c80b7d830f..25b99862100 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -31,9 +31,11 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages" }
+ let(:target_component_file) { component_file }
+ let(:target_component_name) { component.name }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/Packages" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
+ it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete Packages file/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages.gz' do
@@ -43,27 +45,37 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
+ let(:target_component_file) { component_file_older_sha256 }
+ let(:target_component_name) { component.name }
+ let(:target_sha256) { target_component_file.file_sha256 }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/Sources' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
+ let(:target_component_file) { component_file_sources }
+ let(:target_component_name) { component.name }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/Sources" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
+ it_behaves_like 'Debian packages index endpoint', /^Description: This is an incomplete Sources file$/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
+ let(:target_component_file) { component_file_sources_older_sha256 }
+ let(:target_component_name) { component.name }
+ let(:target_sha256) { target_component_file.file_sha256 }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/by-hash/SHA256/#{target_sha256}" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
+ let(:target_component_file) { component_file_di }
+ let(:target_component_name) { component.name }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/Packages" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
+ it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete D-I Packages file/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages.gz' do
@@ -73,9 +85,12 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
+ let(:target_component_file) { component_file_di_older_sha256 }
+ let(:target_component_name) { component.name }
+ let(:target_sha256) { target_component_file.file_sha256 }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
end
describe 'GET groups/:id/-/packages/debian/pool/:codename/:project_id/:letter/:package_name/:package_version/:file_name' do
@@ -89,6 +104,7 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
'sample_1.2.3~alpha2.dsc' | /^Format: 3.0 \(native\)/
'libsample0_1.2.3~alpha2_amd64.deb' | /^!<arch>/
'sample-udeb_1.2.3~alpha2_amd64.udeb' | /^!<arch>/
+ 'sample-ddeb_1.2.3~alpha2_amd64.ddeb' | /^!<arch>/
'sample_1.2.3~alpha2_amd64.buildinfo' | /Build-Tainted-By/
'sample_1.2.3~alpha2_amd64.changes' | /urgency=medium/
end
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index 46f79efd928..e9ad39a08ab 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -44,9 +44,11 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages" }
+ let(:target_component_file) { component_file }
+ let(:target_component_name) { component.name }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/Packages" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
+ it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete Packages file/
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -57,30 +59,40 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
+ let(:target_component_file) { component_file_older_sha256 }
+ let(:target_component_name) { component.name }
+ let(:target_sha256) { target_component_file.file_sha256 }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/source/Sources' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
+ let(:target_component_file) { component_file_sources }
+ let(:target_component_name) { component.name }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/Sources" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
+ it_behaves_like 'Debian packages index endpoint', /^Description: This is an incomplete Sources file$/
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
+ let(:target_component_file) { component_file_sources_older_sha256 }
+ let(:target_component_name) { component.name }
+ let(:target_sha256) { target_component_file.file_sha256 }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/by-hash/SHA256/#{target_sha256}" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
+ let(:target_component_file) { component_file_di }
+ let(:target_component_name) { component.name }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/Packages" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
+ it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete D-I Packages file/
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -91,9 +103,12 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
+ let(:target_component_file) { component_file_di_older_sha256 }
+ let(:target_component_name) { component.name }
+ let(:target_sha256) { target_component_file.file_sha256 }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" }
- it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -108,6 +123,7 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
'sample_1.2.3~alpha2.dsc' | /^Format: 3.0 \(native\)/
'libsample0_1.2.3~alpha2_amd64.deb' | /^!<arch>/
'sample-udeb_1.2.3~alpha2_amd64.udeb' | /^!<arch>/
+ 'sample-ddeb_1.2.3~alpha2_amd64.ddeb' | /^!<arch>/
'sample_1.2.3~alpha2_amd64.buildinfo' | /Build-Tainted-By/
'sample_1.2.3~alpha2_amd64.changes' | /urgency=medium/
end
@@ -162,7 +178,7 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:extra_params) { { distribution: distribution.codename, component: 'main' } }
it_behaves_like "Debian packages upload request", :bad_request,
- /^file_name Only debs and udebs can be directly added to a distribution$/
+ /^file_name Only debs, udebs and ddebs can be directly added to a distribution$/
end
end
end
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb
index 5116f074894..888220c2251 100644
--- a/spec/requests/api/doorkeeper_access_spec.rb
+++ b/spec/requests/api/doorkeeper_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'doorkeeper access', feature_category: :authentication_and_authorization do
+RSpec.describe 'doorkeeper access', feature_category: :system_access do
let!(:user) { create(:user) }
let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" }
diff --git a/spec/requests/api/draft_notes_spec.rb b/spec/requests/api/draft_notes_spec.rb
index e8f519e004d..d239853ac1d 100644
--- a/spec/requests/api/draft_notes_spec.rb
+++ b/spec/requests/api/draft_notes_spec.rb
@@ -8,11 +8,16 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
let_it_be(:project) { create(:project, :public) }
let_it_be(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ let_it_be(:private_project) { create(:project, :private) }
+ let_it_be(:private_merge_request) do
+ create(:merge_request, source_project: private_project, target_project: private_project)
+ end
+
let_it_be(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
let!(:draft_note_by_current_user) { create(:draft_note, merge_request: merge_request, author: user) }
let!(:draft_note_by_random_user) { create(:draft_note, merge_request: merge_request) }
- let_it_be(:api_stub) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}" }
+ let_it_be(:base_url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes" }
before do
project.add_developer(user)
@@ -20,13 +25,13 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
describe "Get a list of merge request draft notes" do
it "returns 200 OK status" do
- get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes", user)
+ get api(base_url, user)
expect(response).to have_gitlab_http_status(:ok)
end
it "returns only draft notes authored by the current user" do
- get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes", user)
+ get api(base_url, user)
draft_note_ids = json_response.pluck("id")
@@ -40,7 +45,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when requesting an existing draft note by the user" do
before do
get api(
- "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes/#{draft_note_by_current_user.id}",
+ "#{base_url}/#{draft_note_by_current_user.id}",
user
)
end
@@ -56,7 +61,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when requesting a non-existent draft note" do
it "returns a 404 Not Found response" do
get api(
- "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes/#{DraftNote.last.id + 1}",
+ "#{base_url}/#{DraftNote.last.id + 1}",
user
)
@@ -67,7 +72,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when requesting an existing draft note by another user" do
it "returns a 404 Not Found response" do
get api(
- "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes/#{draft_note_by_random_user.id}",
+ "#{base_url}/#{draft_note_by_random_user.id}",
user
)
@@ -83,7 +88,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
before do
delete api(
- "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes/#{draft_note_by_current_user.id}",
+ "#{base_url}/#{draft_note_by_current_user.id}",
user
)
end
@@ -100,7 +105,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when deleting a non-existent draft note" do
it "returns a 404 Not Found" do
delete api(
- "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes/#{non_existing_record_id}",
+ "#{base_url}/#{non_existing_record_id}",
user
)
@@ -111,7 +116,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when deleting a draft note by a different user" do
it "returns a 404 Not Found" do
delete api(
- "/projects/#{project.id}/merge_requests/#{merge_request.iid}/draft_notes/#{draft_note_by_random_user.id}",
+ "#{base_url}/#{draft_note_by_random_user.id}",
user
)
@@ -120,10 +125,152 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
end
end
+ def create_draft_note(params = {}, url = base_url)
+ post api(url, user), params: params
+ end
+
+ describe "Create a new draft note" do
+ let(:basic_create_params) do
+ {
+ note: "Example body string"
+ }
+ end
+
+ context "when creating a new draft note" do
+ context "with required params" do
+ it "returns 201 Created status" do
+ create_draft_note(basic_create_params)
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it "creates a new draft note with the submitted params" do
+ expect { create_draft_note(basic_create_params) }.to change { DraftNote.count }.by(1)
+
+ expect(json_response["note"]).to eq(basic_create_params[:note])
+ expect(json_response["merge_request_id"]).to eq(merge_request.id)
+ expect(json_response["author_id"]).to eq(user.id)
+ end
+ end
+
+ context "without required params" do
+ it "returns 400 Bad Request status" do
+ create_draft_note({})
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context "when providing a non-existing commit_id" do
+ it "returns a 400 Bad Request" do
+ create_draft_note(
+ basic_create_params.merge(
+ commit_id: 'bad SHA'
+ )
+ )
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context "when targeting a merge request the user doesn't have access to" do
+ it "returns a 404 Not Found" do
+ create_draft_note(
+ basic_create_params,
+ "/projects/#{private_project.id}/merge_requests/#{private_merge_request.iid}"
+ )
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context "when attempting to resolve a disscussion" do
+ context "when providing a non-existant ID" do
+ it "returns a 400 Bad Request" do
+ create_draft_note(
+ basic_create_params.merge(
+ resolve_discussion: true,
+ in_reply_to_discussion_id: non_existing_record_id
+ )
+ )
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context "when not providing an ID" do
+ it "returns a 400 Bad Request" do
+ create_draft_note(basic_create_params.merge(resolve_discussion: true))
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it "returns a validation error message" do
+ create_draft_note(basic_create_params.merge(resolve_discussion: true))
+
+ expect(response.body)
+ .to eq("{\"message\":{\"base\":[\"User is not allowed to resolve thread\"]}}")
+ end
+ end
+ end
+ end
+ end
+
+ def update_draft_note(params = {}, url = base_url)
+ put api("#{url}/#{draft_note_by_current_user.id}", user), params: params
+ end
+
+ describe "Update a draft note" do
+ let(:basic_update_params) do
+ {
+ note: "Example updated body string"
+ }
+ end
+
+ context "when updating an existing draft note" do
+ context "with required params" do
+ it "returns 200 Success status" do
+ update_draft_note(basic_update_params)
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it "updates draft note with the new content" do
+ update_draft_note(basic_update_params)
+
+ expect(json_response["note"]).to eq(basic_update_params[:note])
+ end
+ end
+
+ context "without including an update to the note body" do
+ it "returns the draft note with no changes" do
+ expect { update_draft_note({}) }
+ .not_to change { draft_note_by_current_user.note }
+ end
+ end
+
+ context "when updating a non-existent draft note" do
+ it "returns a 404 Not Found" do
+ put api("#{base_url}/#{non_existing_record_id}", user), params: basic_update_params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context "when updating a draft note by a different user" do
+ it "returns a 404 Not Found" do
+ put api("#{base_url}/#{draft_note_by_random_user.id}", user), params: basic_update_params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
describe "Publishing a draft note" do
let(:publish_draft_note) do
put api(
- "#{api_stub}/draft_notes/#{draft_note_by_current_user.id}/publish",
+ "#{base_url}/#{draft_note_by_current_user.id}/publish",
user
)
end
@@ -144,7 +291,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when publishing a non-existent draft note" do
it "returns a 404 Not Found" do
put api(
- "#{api_stub}/draft_notes/#{non_existing_record_id}/publish",
+ "#{base_url}/#{non_existing_record_id}/publish",
user
)
@@ -155,7 +302,7 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
context "when publishing a draft note by a different user" do
it "returns a 404 Not Found" do
put api(
- "#{api_stub}/draft_notes/#{draft_note_by_random_user.id}/publish",
+ "#{base_url}/#{draft_note_by_random_user.id}/publish",
user
)
diff --git a/spec/requests/api/error_tracking/project_settings_spec.rb b/spec/requests/api/error_tracking/project_settings_spec.rb
index 5906cdf105a..3b01dec6f9c 100644
--- a/spec/requests/api/error_tracking/project_settings_spec.rb
+++ b/spec/requests/api/error_tracking/project_settings_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
end
- shared_examples 'returns 404' do
+ shared_examples 'returns no project settings' do
it 'returns no project settings' do
make_request
@@ -48,6 +48,57 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
end
+ shared_examples 'returns 400' do
+ it 'rejects request' do
+ make_request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ shared_examples 'returns 401' do
+ it 'rejects request' do
+ make_request
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ shared_examples 'returns 403' do
+ it 'rejects request' do
+ make_request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ shared_examples 'returns 404' do
+ it 'rejects request' do
+ make_request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ shared_examples 'returns 400 with `integrated` param required or invalid' do |error|
+ it 'returns 400' do
+ make_request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error'])
+ .to eq(error)
+ end
+ end
+
+ shared_examples "returns error from UpdateService" do
+ it "returns errors" do
+ make_request
+
+ expect(json_response['http_status']).to eq('forbidden')
+ expect(json_response['message']).to eq('An error occurred')
+ end
+ end
+
describe "PATCH /projects/:id/error_tracking/settings" do
let(:params) { { active: false } }
@@ -127,14 +178,34 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'without a project setting' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project) }
before do
project.add_maintainer(user)
end
context 'patch settings' do
- it_behaves_like 'returns 404'
+ it_behaves_like 'returns no project settings'
+ end
+ end
+
+ context "when ::Projects::Operations::UpdateService responds with an error" do
+ before do
+ allow_next_instance_of(::Projects::Operations::UpdateService) do |service|
+ allow(service)
+ .to receive(:execute)
+ .and_return({ status: :error, message: 'An error occurred', http_status: :forbidden })
+ end
+ end
+
+ context "when integrated" do
+ let(:integrated) { true }
+
+ it_behaves_like 'returns error from UpdateService'
+ end
+
+ context "without integrated" do
+ it_behaves_like 'returns error from UpdateService'
end
end
end
@@ -145,11 +216,7 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'patch request' do
- it 'returns 403' do
- make_request
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'returns 403'
end
end
@@ -159,21 +226,13 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'patch request' do
- it 'returns 403' do
- make_request
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'returns 403'
end
end
context 'when authenticated as non-member' do
context 'patch request' do
- it 'returns 404' do
- make_request
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it_behaves_like 'returns 404'
end
end
@@ -181,11 +240,7 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
let(:user) { nil }
context 'patch request' do
- it 'returns 401 for update request' do
- make_request
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
+ it_behaves_like 'returns 401'
end
end
end
@@ -227,7 +282,7 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'get settings' do
- it_behaves_like 'returns 404'
+ it_behaves_like 'returns no project settings'
end
end
@@ -236,11 +291,7 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
project.add_reporter(user)
end
- it 'returns 403' do
- make_request
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'returns 403'
end
context 'when authenticated as developer' do
@@ -248,29 +299,138 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
project.add_developer(user)
end
- it 'returns 403' do
- make_request
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'returns 403'
end
context 'when authenticated as non-member' do
- it 'returns 404' do
- make_request
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it_behaves_like 'returns 404'
end
context 'when unauthenticated' do
let(:user) { nil }
- it 'returns 401' do
- make_request
+ it_behaves_like 'returns 401'
+ end
+ end
+
+ describe "PUT /projects/:id/error_tracking/settings" do
+ let(:params) { { active: active, integrated: integrated } }
+ let(:active) { true }
+ let(:integrated) { true }
+
+ def make_request
+ put api("/projects/#{project.id}/error_tracking/settings", user), params: params
+ end
+
+ context 'when authenticated' do
+ context 'as maintainer' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context "when integrated" do
+ let(:integrated) { true }
+
+ context "with existing setting" do
+ let(:setting) { create(:project_error_tracking_setting, :integrated) }
+ let(:active) { false }
+
+ it "updates a setting" do
+ expect { make_request }.not_to change { ErrorTracking::ProjectErrorTrackingSetting.count }
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(json_response).to eq(
+ "active" => false,
+ "api_url" => nil,
+ "integrated" => integrated,
+ "project_name" => nil,
+ "sentry_external_url" => nil
+ )
+ end
+ end
+
+ context "without setting" do
+ let(:active) { true }
+ let_it_be(:project) { create(:project) }
+
+ it "creates a setting" do
+ expect { make_request }.to change { ErrorTracking::ProjectErrorTrackingSetting.count }
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(json_response).to eq(
+ "active" => true,
+ "api_url" => nil,
+ "integrated" => integrated,
+ "project_name" => nil,
+ "sentry_external_url" => nil
+ )
+ end
+ end
+
+ context "when ::Projects::Operations::UpdateService responds with an error" do
+ before do
+ allow_next_instance_of(::Projects::Operations::UpdateService) do |service|
+ allow(service)
+ .to receive(:execute)
+ .and_return({ status: :error, message: 'An error occurred', http_status: :forbidden })
+ end
+ end
+
+ it_behaves_like 'returns error from UpdateService'
+ end
+ end
- expect(response).to have_gitlab_http_status(:unauthorized)
+ context "integrated_error_tracking feature disabled" do
+ let(:integrated) { true }
+
+ before do
+ stub_feature_flags(integrated_error_tracking: false)
+ end
+
+ it_behaves_like 'returns 404'
+ end
+
+ context "when integrated param is invalid" do
+ let(:params) { { active: active, integrated: 'invalid_string' } }
+
+ it_behaves_like 'returns 400 with `integrated` param required or invalid', 'integrated is invalid'
+ end
+
+ context "when integrated param is missing" do
+ let(:params) { { active: active } }
+
+ it_behaves_like 'returns 400 with `integrated` param required or invalid', 'integrated is missing'
+ end
+ end
+
+ context 'as reporter' do
+ before do
+ project.add_reporter(user)
+ end
+
+ it_behaves_like 'returns 403'
+ end
+
+ context "as developer" do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'returns 403'
end
+
+ context 'as non-member' do
+ it_behaves_like 'returns 404'
+ end
+ end
+
+ context "when unauthorized" do
+ let(:user) { nil }
+ let(:integrated) { true }
+
+ it_behaves_like 'returns 401'
end
end
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index f4066c54c47..ed84e3e5f48 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -55,6 +55,11 @@ RSpec.describe API::Files, feature_category: :source_code_management do
}
end
+ let(:last_commit_for_path) do
+ Gitlab::Git::Commit
+ .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
+ end
+
shared_context 'with author parameters' do
let(:author_email) { 'user@example.org' }
let(:author_name) { 'John Doe' }
@@ -136,6 +141,12 @@ RSpec.describe API::Files, feature_category: :source_code_management do
it 'caches sha256 of the content', :use_clean_rails_redis_caching do
head api(route(file_path), current_user, **options), params: params
+ expect(Gitlab::Cache::Client).to receive(:build_with_metadata).with(
+ cache_identifier: 'API::Files#content_sha',
+ feature_category: :source_code_management,
+ backing_resource: :gitaly
+ ).and_call_original
+
expect(Rails.cache.fetch("blob_content_sha256:#{project.full_path}:#{response.headers['X-Gitlab-Blob-Id']}"))
.to eq(content_sha256)
@@ -829,7 +840,6 @@ RSpec.describe API::Files, feature_category: :source_code_management do
expect_to_send_git_blob(api(url, current_user), params)
expect(response.headers['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store, no-cache')
- expect(response.headers['Pragma']).to eq('no-cache')
expect(response.headers['Expires']).to eq('Fri, 01 Jan 1990 00:00:00 GMT')
end
@@ -1180,7 +1190,7 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
context 'when updating an existing file with stale last commit id' do
- let(:params_with_stale_id) { params.merge(last_commit_id: 'stale') }
+ let(:params_with_stale_id) { params.merge(last_commit_id: last_commit_for_path.parent_id) }
it 'returns a 400 bad request' do
put api(route(file_path), user), params: params_with_stale_id
@@ -1191,12 +1201,7 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
context 'with correct last commit id' do
- let(:last_commit) do
- Gitlab::Git::Commit
- .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
- end
-
- let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
+ let(:params_with_correct_id) { params.merge(last_commit_id: last_commit_for_path.id) }
it 'updates existing file in project repo' do
put api(route(file_path), user), params: params_with_correct_id
@@ -1206,12 +1211,7 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
context 'when file path is invalid' do
- let(:last_commit) do
- Gitlab::Git::Commit
- .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
- end
-
- let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
+ let(:params_with_correct_id) { params.merge(last_commit_id: last_commit_for_path.id) }
it 'returns a 400 bad request' do
put api(route(invalid_file_path), user), params: params_with_correct_id
@@ -1222,12 +1222,7 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
it_behaves_like 'when path is absolute' do
- let(:last_commit) do
- Gitlab::Git::Commit
- .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
- end
-
- let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
+ let(:params_with_correct_id) { params.merge(last_commit_id: last_commit_for_path.id) }
subject { put api(route(absolute_path), user), params: params_with_correct_id }
end
diff --git a/spec/requests/api/freeze_periods_spec.rb b/spec/requests/api/freeze_periods_spec.rb
index 170871706dc..a53db516940 100644
--- a/spec/requests/api/freeze_periods_spec.rb
+++ b/spec/requests/api/freeze_periods_spec.rb
@@ -12,14 +12,12 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
let(:last_freeze_period) { project.freeze_periods.last }
describe 'GET /projects/:id/freeze_periods' do
+ let_it_be(:path) { "/projects/#{project.id}/freeze_periods" }
+
context 'when the user is the admin' do
let!(:freeze_period) { create(:ci_freeze_period, project: project, created_at: 2.days.ago) }
- it 'returns 200 HTTP status' do
- get api("/projects/#{project.id}/freeze_periods", admin)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
+ it_behaves_like 'GET request permissions for admin mode when admin', :not_found
end
context 'when the user is the maintainer' do
@@ -31,36 +29,26 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
let!(:freeze_period_1) { create(:ci_freeze_period, project: project, created_at: 2.days.ago) }
let!(:freeze_period_2) { create(:ci_freeze_period, project: project, created_at: 1.day.ago) }
- it 'returns 200 HTTP status' do
- get api("/projects/#{project.id}/freeze_periods", user)
+ it 'returns freeze_periods ordered by created_at ascending', :aggregate_failures do
+ get api(path, user)
expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns freeze_periods ordered by created_at ascending' do
- get api("/projects/#{project.id}/freeze_periods", user)
-
expect(json_response.count).to eq(2)
expect(freeze_period_ids).to eq([freeze_period_1.id, freeze_period_2.id])
end
it 'matches response schema' do
- get api("/projects/#{project.id}/freeze_periods", user)
+ get api(path, user)
expect(response).to match_response_schema('public_api/v4/freeze_periods')
end
end
context 'when there are no freeze_periods' do
- it 'returns 200 HTTP status' do
- get api("/projects/#{project.id}/freeze_periods", user)
+ it 'returns 200 HTTP status with empty response', :aggregate_failures do
+ get api(path, user)
expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns an empty response' do
- get api("/projects/#{project.id}/freeze_periods", user)
-
expect(json_response).to be_empty
end
end
@@ -75,28 +63,21 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
create(:ci_freeze_period, project: project)
end
- it 'responds 403 Forbidden' do
- get api("/projects/#{project.id}/freeze_periods", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'GET request permissions for admin mode when user', :forbidden do
+ let(:current_user) { user }
+ end
end
end
context 'when user is not a project member' do
- it 'responds 404 Not Found' do
- get api("/projects/#{project.id}/freeze_periods", user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it_behaves_like 'GET request permissions for admin mode when user', :not_found
context 'when project is public' do
let(:project) { create(:project, :public) }
+ let(:path) { "/projects/#{project.id}/freeze_periods" }
- it 'responds 403 Forbidden' do
- get api("/projects/#{project.id}/freeze_periods", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'GET request permissions for admin mode when user', :forbidden
end
end
end
@@ -107,14 +88,12 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
create(:ci_freeze_period, project: project)
end
+ let(:path) { "/projects/#{project.id}/freeze_periods/#{freeze_period.id}" }
+
context 'when the user is the admin' do
let!(:freeze_period) { create(:ci_freeze_period, project: project, created_at: 2.days.ago) }
- it 'responds 200 OK' do
- get api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", admin)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
+ it_behaves_like 'GET request permissions for admin mode when admin', :not_found
end
context 'when the user is the maintainer' do
@@ -122,15 +101,10 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
project.add_maintainer(user)
end
- it 'responds 200 OK' do
- get api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", user)
+ it 'returns a freeze period', :aggregate_failures do
+ get api(path, user)
expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns a freeze period' do
- get api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", user)
-
expect(json_response).to include(
'id' => freeze_period.id,
'freeze_start' => freeze_period.freeze_start,
@@ -139,7 +113,7 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
end
it 'matches response schema' do
- get api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", user)
+ get api(path, user)
expect(response).to match_response_schema('public_api/v4/freeze_period')
end
@@ -150,28 +124,26 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
project.add_guest(user)
end
- it 'responds 403 Forbidden' do
- get api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'GET request permissions for admin mode when user' do
+ let(:current_user) { user }
+ end
end
context 'when project is public' do
let(:project) { create(:project, :public) }
- context 'when freeze_period exists' do
- it 'responds 403 Forbidden' do
- get api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden when freeze_period exists' do
+ it_behaves_like 'GET request permissions for admin mode when user' do
+ let(:current_user) { user }
end
end
- context 'when freeze_period does not exist' do
- it 'responds 403 Forbidden' do
- get api("/projects/#{project.id}/freeze_periods/0", user)
+ context 'and responds 403 Forbidden when freeze_period does not exist' do
+ let(:path) { "/projects/#{project.id}/freeze_periods/0" }
- expect(response).to have_gitlab_http_status(:forbidden)
+ it_behaves_like 'GET request permissions for admin mode when user' do
+ let(:current_user) { user }
end
end
end
@@ -188,15 +160,13 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
}
end
- subject { post api("/projects/#{project.id}/freeze_periods", api_user), params: params }
+ let(:path) { "/projects/#{project.id}/freeze_periods" }
- context 'when the user is the admin' do
- let(:api_user) { admin }
-
- it 'accepts the request' do
- subject
+ subject { post api(path, api_user), params: params }
- expect(response).to have_gitlab_http_status(:created)
+ context 'when the user is the admin' do
+ it_behaves_like 'POST request permissions for admin mode when admin', :not_found do
+ let(:current_user) { admin }
end
end
@@ -212,7 +182,7 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
expect(response).to have_gitlab_http_status(:created)
end
- it 'creates a new freeze period' do
+ it 'creates a new freeze period', :aggregate_failures do
expect do
subject
end.to change { Ci::FreezePeriod.count }.by(1)
@@ -268,10 +238,10 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
project.add_developer(user)
end
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'POST request permissions for admin mode when user' do
+ let(:current_user) { user }
+ end
end
end
@@ -280,28 +250,22 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
project.add_reporter(user)
end
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'POST request permissions for admin mode when user' do
+ let(:current_user) { user }
+ end
end
end
context 'when user is not a project member' do
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'POST request permissions for admin mode when user', :not_found
end
context 'when project is public' do
let(:project) { create(:project, :public) }
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'POST request permissions for admin mode when user', :forbidden
end
end
end
@@ -309,17 +273,12 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
describe 'PUT /projects/:id/freeze_periods/:freeze_period_id' do
let(:params) { { freeze_start: '0 22 * * 5', freeze_end: '5 4 * * sun' } }
let!(:freeze_period) { create :ci_freeze_period, project: project }
+ let(:path) { "/projects/#{project.id}/freeze_periods/#{freeze_period.id}" }
- subject { put api("/projects/#{project.id}/freeze_periods/#{freeze_period.id}", api_user), params: params }
+ subject { put api(path, api_user), params: params }
context 'when user is the admin' do
- let(:api_user) { admin }
-
- it 'accepts the request' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
+ it_behaves_like 'PUT request permissions for admin mode when admin', :not_found
end
context 'when user is the maintainer' do
@@ -367,27 +326,23 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
project.add_reporter(user)
end
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'PUT request permissions for admin mode when user' do
+ let(:current_user) { user }
+ end
end
end
context 'when user is not a project member' do
- it 'responds 404 Not Found' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
+ context 'and responds 404 Not Found' do
+ it_behaves_like 'PUT request permissions for admin mode when user', :not_found
end
context 'when project is public' do
let(:project) { create(:project, :public) }
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'PUT request permissions for admin mode when user'
end
end
end
@@ -396,17 +351,12 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
describe 'DELETE /projects/:id/freeze_periods/:freeze_period_id' do
let!(:freeze_period) { create :ci_freeze_period, project: project }
let(:freeze_period_id) { freeze_period.id }
+ let(:path) { "/projects/#{project.id}/freeze_periods/#{freeze_period_id}" }
- subject { delete api("/projects/#{project.id}/freeze_periods/#{freeze_period_id}", api_user) }
+ subject { delete api(path, api_user) }
context 'when user is the admin' do
- let(:api_user) { admin }
-
- it 'accepts the request' do
- subject
-
- expect(response).to have_gitlab_http_status(:no_content)
- end
+ it_behaves_like 'DELETE request permissions for admin mode when admin', failed_status_code: :not_found
end
context 'when user is the maintainer' do
@@ -442,27 +392,23 @@ RSpec.describe API::FreezePeriods, feature_category: :continuous_delivery do
project.add_reporter(user)
end
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'DELETE request permissions for admin mode when user' do
+ let(:current_user) { user }
+ end
end
end
context 'when user is not a project member' do
- it 'responds 404 Not Found' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
+ context 'and responds 404 Not Found' do
+ it_behaves_like 'DELETE request permissions for admin mode when user', :not_found
end
context 'when project is public' do
let(:project) { create(:project, :public) }
- it 'responds 403 Forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'and responds 403 Forbidden' do
+ it_behaves_like 'DELETE request permissions for admin mode when user'
end
end
end
diff --git a/spec/requests/api/graphql/achievements/user_achievements_query_spec.rb b/spec/requests/api/graphql/achievements/user_achievements_query_spec.rb
new file mode 100644
index 00000000000..dc19f3bdc91
--- /dev/null
+++ b/spec/requests/api/graphql/achievements/user_achievements_query_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'UserAchievements', feature_category: :user_profile do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+ let_it_be(:user_achievement1) { create(:user_achievement, achievement: achievement, user: user) }
+ let_it_be(:user_achievement2) { create(:user_achievement, :revoked, achievement: achievement, user: user) }
+ let_it_be(:fields) do
+ <<~HEREDOC
+ id
+ achievements {
+ nodes {
+ userAchievements {
+ nodes {
+ id
+ achievement {
+ id
+ }
+ user {
+ id
+ }
+ awardedByUser {
+ id
+ }
+ revokedByUser {
+ id
+ }
+ }
+ }
+ }
+ }
+ HEREDOC
+ end
+
+ let_it_be(:query) do
+ graphql_query_for('namespace', { full_path: group.full_path }, fields)
+ end
+
+ before_all do
+ group.add_guest(user)
+ end
+
+ before do
+ post_graphql(query, current_user: user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns all user_achievements' do
+ expect(graphql_data_at(:namespace, :achievements, :nodes, :userAchievements, :nodes))
+ .to contain_exactly(
+ a_graphql_entity_for(user_achievement1),
+ a_graphql_entity_for(user_achievement2)
+ )
+ end
+
+ it 'can lookahead to eliminate N+1 queries', :use_clean_rails_memory_store_caching do
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: user)
+ end.count
+
+ user2 = create(:user)
+ create_list(:achievement, 3, namespace: group) do |a|
+ create(:user_achievement, achievement: a, user: user2)
+ end
+
+ expect { post_graphql(query, current_user: user) }.not_to exceed_all_query_limit(control_count)
+ end
+
+ context 'when the achievements feature flag is disabled' do
+ before do
+ stub_feature_flags(achievements: false)
+ post_graphql(query, current_user: user)
+ end
+
+ specify { expect(graphql_data_at(:namespace, :achievements, :nodes, :userAchievements, :nodes)).to be_empty }
+ end
+end
diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb
index f76bb8ff837..d77e66d2239 100644
--- a/spec/requests/api/graphql/ci/config_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/config_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_category: :pipeline_authoring do
+RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_category: :pipeline_composition do
include GraphqlHelpers
include ReactiveCachingHelpers
diff --git a/spec/requests/api/graphql/ci/group_variables_spec.rb b/spec/requests/api/graphql/ci/group_variables_spec.rb
index d78b30787c9..042f93e9779 100644
--- a/spec/requests/api/graphql/ci/group_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/group_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.group(fullPath).ciVariables', feature_category: :pipeline_authoring do
+RSpec.describe 'Query.group(fullPath).ciVariables', feature_category: :pipeline_composition do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/ci/instance_variables_spec.rb b/spec/requests/api/graphql/ci/instance_variables_spec.rb
index 5b65ae88426..286a7af3c01 100644
--- a/spec/requests/api/graphql/ci/instance_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/instance_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.ciVariables', feature_category: :pipeline_authoring do
+RSpec.describe 'Query.ciVariables', feature_category: :pipeline_composition do
include GraphqlHelpers
let(:query) do
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index 674407c0a0e..3f556a75869 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -260,6 +260,68 @@ RSpec.describe 'Query.project.pipeline', feature_category: :continuous_integrati
end
end
+ describe '.jobs.runnerMachine' do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:runner_machine) { create(:ci_runner_machine, created_at: Time.current, contacted_at: Time.current) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) do
+ create(:ci_build, pipeline: pipeline, name: 'my test job', runner_machine: runner_machine)
+ end
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ pipeline(iid: "#{pipeline.iid}") {
+ jobs {
+ nodes {
+ id
+ name
+ runnerMachine {
+ #{all_graphql_fields_for('CiRunnerMachine', excluded: [:runner], max_depth: 1)}
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ let(:jobs_graphql_data) { graphql_data_at(:project, :pipeline, :jobs, :nodes) }
+
+ it 'returns the runner machine in each job of a pipeline' do
+ post_graphql(query, current_user: admin)
+
+ expect(jobs_graphql_data).to contain_exactly(
+ a_graphql_entity_for(
+ build,
+ name: build.name,
+ runner_machine: a_graphql_entity_for(
+ runner_machine,
+ system_id: runner_machine.system_xid,
+ created_at: runner_machine.created_at.iso8601,
+ contacted_at: runner_machine.contacted_at.iso8601,
+ status: runner_machine.status.to_s.upcase
+ )
+ )
+ )
+ end
+
+ it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
+ admin2 = create(:admin)
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: admin)
+ end
+
+ runner_machine2 = create(:ci_runner_machine)
+ create(:ci_build, pipeline: pipeline, name: 'my test job2', runner_machine: runner_machine2)
+
+ expect { post_graphql(query, current_user: admin2) }.not_to exceed_all_query_limit(control)
+ end
+ end
+
describe '.jobs.count' do
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:successful_job) { create(:ci_build, :success, pipeline: pipeline) }
diff --git a/spec/requests/api/graphql/ci/manual_variables_spec.rb b/spec/requests/api/graphql/ci/manual_variables_spec.rb
index 921c69e535d..98d91e9ded0 100644
--- a/spec/requests/api/graphql/ci/manual_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/manual_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).pipelines.jobs.manualVariables', feature_category: :pipeline_authoring do
+RSpec.describe 'Query.project(fullPath).pipelines.jobs.manualVariables', feature_category: :pipeline_composition do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/ci/project_variables_spec.rb b/spec/requests/api/graphql/ci/project_variables_spec.rb
index 0ddcac89b34..947991a2e62 100644
--- a/spec/requests/api/graphql/ci/project_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/project_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.project(fullPath).ciVariables', feature_category: :pipeline_authoring do
+RSpec.describe 'Query.project(fullPath).ciVariables', feature_category: :pipeline_composition do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 986e3ce9e52..da71ee675b7 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -6,11 +6,13 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
include GraphqlHelpers
let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:another_admin) { create(:user, :admin) }
let_it_be(:group) { create(:group) }
let_it_be(:active_instance_runner) do
- create(:ci_runner, :instance,
+ create(:ci_runner, :instance, :with_runner_machine,
description: 'Runner 1',
+ creator: user,
contacted_at: 2.hours.ago,
active: true,
version: 'adfe156',
@@ -28,6 +30,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
let_it_be(:inactive_instance_runner) do
create(:ci_runner, :instance,
description: 'Runner 2',
+ creator: another_admin,
contacted_at: 1.day.ago,
active: false,
version: 'adfe157',
@@ -55,7 +58,9 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
end
let_it_be(:project1) { create(:project) }
- let_it_be(:active_project_runner) { create(:ci_runner, :project, projects: [project1]) }
+ let_it_be(:active_project_runner) do
+ create(:ci_runner, :project, :with_runner_machine, projects: [project1])
+ end
shared_examples 'runner details fetch' do
let(:query) do
@@ -77,6 +82,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
expect(runner_data).to match a_graphql_entity_for(
runner,
description: runner.description,
+ created_by: runner.creator ? a_graphql_entity_for(runner.creator) : nil,
created_at: runner.created_at&.iso8601,
contacted_at: runner.contacted_at&.iso8601,
version: runner.version,
@@ -107,15 +113,39 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
),
project_count: nil,
admin_url: "http://localhost/admin/runners/#{runner.id}",
+ edit_admin_url: "http://localhost/admin/runners/#{runner.id}/edit",
+ register_admin_url: runner.registration_available? ? "http://localhost/admin/runners/#{runner.id}/register" : nil,
user_permissions: {
'readRunner' => true,
'updateRunner' => true,
'deleteRunner' => true,
'assignRunner' => true
- }
+ },
+ machines: a_hash_including(
+ "count" => runner.runner_machines.count,
+ "nodes" => an_instance_of(Array),
+ "pageInfo" => anything
+ )
)
expect(runner_data['tagList']).to match_array runner.tag_list
end
+
+ it 'does not execute more queries per runner', :aggregate_failures do
+ # warm-up license cache and so on:
+ personal_access_token = create(:personal_access_token, user: user)
+ args = { current_user: user, token: { personal_access_token: personal_access_token } }
+ post_graphql(query, **args)
+ expect(graphql_data_at(:runner)).not_to be_nil
+
+ personal_access_token = create(:personal_access_token, user: another_admin)
+ args = { current_user: another_admin, token: { personal_access_token: personal_access_token } }
+ control = ActiveRecord::QueryRecorder.new { post_graphql(query, **args) }
+
+ create(:ci_runner, :instance, version: '14.0.0', tag_list: %w[tag5 tag6], creator: another_admin)
+ create(:ci_runner, :project, version: '14.0.1', projects: [project1], tag_list: %w[tag3 tag8], creator: another_admin)
+
+ expect { post_graphql(query, **args) }.not_to exceed_query_limit(control)
+ end
end
shared_examples 'retrieval with no admin url' do
@@ -135,7 +165,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
runner_data = graphql_data_at(:runner)
expect(runner_data).not_to be_nil
- expect(runner_data).to match a_graphql_entity_for(runner, admin_url: nil)
+ expect(runner_data).to match a_graphql_entity_for(runner, admin_url: nil, edit_admin_url: nil)
expect(runner_data['tagList']).to match_array runner.tag_list
end
end
@@ -307,6 +337,24 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
it_behaves_like 'runner details fetch'
end
+ describe 'for registration type' do
+ context 'when registered with registration token' do
+ let(:runner) do
+ create(:ci_runner, registration_type: :registration_token)
+ end
+
+ it_behaves_like 'runner details fetch'
+ end
+
+ context 'when registered with authenticated user' do
+ let(:runner) do
+ create(:ci_runner, registration_type: :authenticated_user)
+ end
+
+ it_behaves_like 'runner details fetch'
+ end
+ end
+
describe 'for group runner request' do
let(:query) do
%(
@@ -568,14 +616,14 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
end
end
- context 'with request made by creator' do
+ context 'with request made by creator', :frozen_time do
let(:user) { creator }
context 'with runner created in UI' do
let(:registration_type) { :authenticated_user }
- context 'with runner created in last 3 hours' do
- let(:created_at) { (3.hours - 1.second).ago }
+ context 'with runner created in last hour' do
+ let(:created_at) { (Ci::Runner::REGISTRATION_AVAILABILITY_TIME - 1.second).ago }
context 'with no runner machine registed yet' do
it_behaves_like 'an ephemeral_authentication_token'
@@ -589,13 +637,13 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
end
context 'with runner created almost too long ago' do
- let(:created_at) { (3.hours - 1.second).ago }
+ let(:created_at) { (Ci::Runner::REGISTRATION_AVAILABILITY_TIME - 1.second).ago }
it_behaves_like 'an ephemeral_authentication_token'
end
context 'with runner created too long ago' do
- let(:created_at) { 3.hours.ago }
+ let(:created_at) { Ci::Runner::REGISTRATION_AVAILABILITY_TIME.ago }
it_behaves_like 'a protected ephemeral_authentication_token'
end
@@ -604,8 +652,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
context 'with runner registered from command line' do
let(:registration_type) { :registration_token }
- context 'with runner created in last 3 hours' do
- let(:created_at) { (3.hours - 1.second).ago }
+ context 'with runner created in last 1 hour' do
+ let(:created_at) { (Ci::Runner::REGISTRATION_AVAILABILITY_TIME - 1.second).ago }
it_behaves_like 'a protected ephemeral_authentication_token'
end
@@ -628,6 +676,12 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
<<~SINGLE
runner(id: "#{runner.to_global_id}") {
#{all_graphql_fields_for('CiRunner', excluded: excluded_fields)}
+ createdBy {
+ id
+ username
+ webPath
+ webUrl
+ }
groups {
nodes {
id
@@ -658,7 +712,7 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
let(:active_group_runner2) { create(:ci_runner, :group) }
# Exclude fields that are already hardcoded above
- let(:excluded_fields) { %w[jobs groups projects ownerProject] }
+ let(:excluded_fields) { %w[createdBy jobs groups projects ownerProject] }
let(:single_query) do
<<~QUERY
@@ -691,6 +745,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
control = ActiveRecord::QueryRecorder.new { post_graphql(single_query, **args) }
+ personal_access_token = create(:personal_access_token, user: another_admin)
+ args = { current_user: another_admin, token: { personal_access_token: personal_access_token } }
expect { post_graphql(double_query, **args) }.not_to exceed_query_limit(control)
expect(graphql_data.count).to eq 6
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 75d8609dc38..c8706ae9698 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -11,16 +11,24 @@ RSpec.describe 'Query.runners', feature_category: :runner_fleet do
let_it_be(:instance_runner) { create(:ci_runner, :instance, version: 'abc', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') }
let_it_be(:project_runner) { create(:ci_runner, :project, active: false, version: 'def', revision: '456', description: 'Project runner', projects: [project], ip_address: '127.0.0.1') }
- let(:runners_graphql_data) { graphql_data['runners'] }
+ let(:runners_graphql_data) { graphql_data_at(:runners) }
let(:params) { {} }
let(:fields) do
<<~QUERY
nodes {
- #{all_graphql_fields_for('CiRunner', excluded: %w[ownerProject])}
+ #{all_graphql_fields_for('CiRunner', excluded: %w[createdBy ownerProject])}
+ createdBy {
+ username
+ webPath
+ webUrl
+ }
ownerProject {
id
+ path
+ fullPath
+ webUrl
}
}
QUERY
@@ -50,6 +58,25 @@ RSpec.describe 'Query.runners', feature_category: :runner_fleet do
it 'returns expected runner' do
expect(runners_graphql_data['nodes']).to contain_exactly(a_graphql_entity_for(expected_runner))
end
+
+ it 'does not execute more queries per runner', :aggregate_failures do
+ # warm-up license cache and so on:
+ personal_access_token = create(:personal_access_token, user: current_user)
+ args = { current_user: current_user, token: { personal_access_token: personal_access_token } }
+ post_graphql(query, **args)
+ expect(graphql_data_at(:runners, :nodes)).not_to be_empty
+
+ admin2 = create(:admin)
+ personal_access_token = create(:personal_access_token, user: admin2)
+ args = { current_user: admin2, token: { personal_access_token: personal_access_token } }
+ control = ActiveRecord::QueryRecorder.new { post_graphql(query, **args) }
+
+ create(:ci_runner, :instance, version: '14.0.0', tag_list: %w[tag5 tag6], creator: admin2)
+ create(:ci_runner, :project, version: '14.0.1', projects: [project], tag_list: %w[tag3 tag8],
+ creator: current_user)
+
+ expect { post_graphql(query, **args) }.not_to exceed_query_limit(control)
+ end
end
context 'runner_type is INSTANCE_TYPE and status is ACTIVE' do
diff --git a/spec/requests/api/graphql/current_user/todos_query_spec.rb b/spec/requests/api/graphql/current_user/todos_query_spec.rb
index f7e23aeb241..ee019a99f8d 100644
--- a/spec/requests/api/graphql/current_user/todos_query_spec.rb
+++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'Query current user todos', feature_category: :source_code_manage
let(:fields) do
<<~QUERY
nodes {
- #{all_graphql_fields_for('todos'.classify, max_depth: 2)}
+ #{all_graphql_fields_for('todos'.classify, max_depth: 2, excluded: ['productAnalyticsState'])}
}
QUERY
end
diff --git a/spec/requests/api/graphql/current_user_query_spec.rb b/spec/requests/api/graphql/current_user_query_spec.rb
index 53d2580caee..aceef77920d 100644
--- a/spec/requests/api/graphql/current_user_query_spec.rb
+++ b/spec/requests/api/graphql/current_user_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting project information', feature_category: :authentication_and_authorization do
+RSpec.describe 'getting project information', feature_category: :system_access do
include GraphqlHelpers
let(:fields) do
diff --git a/spec/requests/api/graphql/custom_emoji_query_spec.rb b/spec/requests/api/graphql/custom_emoji_query_spec.rb
index 7b804623e01..1858ea831dd 100644
--- a/spec/requests/api/graphql/custom_emoji_query_spec.rb
+++ b/spec/requests/api/graphql/custom_emoji_query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting custom emoji within namespace', feature_category: :not_owned do
+RSpec.describe 'getting custom emoji within namespace', feature_category: :shared do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/multiplexed_queries_spec.rb b/spec/requests/api/graphql/multiplexed_queries_spec.rb
index 4d615d3eaa4..0a5c87ebef8 100644
--- a/spec/requests/api/graphql/multiplexed_queries_spec.rb
+++ b/spec/requests/api/graphql/multiplexed_queries_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Multiplexed queries', feature_category: :not_owned do
+RSpec.describe 'Multiplexed queries', feature_category: :shared do
include GraphqlHelpers
it 'returns responses for multiple queries' do
diff --git a/spec/requests/api/graphql/mutations/achievements/award_spec.rb b/spec/requests/api/graphql/mutations/achievements/award_spec.rb
new file mode 100644
index 00000000000..9bc0751e924
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/achievements/award_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Achievements::Award, feature_category: :user_profile do
+ include GraphqlHelpers
+
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+ let_it_be(:recipient) { create(:user) }
+
+ let(:mutation) { graphql_mutation(:achievements_award, params) }
+ let(:achievement_id) { achievement&.to_global_id }
+ let(:recipient_id) { recipient&.to_global_id }
+ let(:params) do
+ {
+ achievement_id: achievement_id,
+ user_id: recipient_id
+ }
+ end
+
+ subject { post_graphql_mutation(mutation, current_user: current_user) }
+
+ def mutation_response
+ graphql_mutation_response(:achievements_create)
+ end
+
+ before_all do
+ group.add_developer(developer)
+ group.add_maintainer(maintainer)
+ end
+
+ context 'when the user does not have permission' do
+ let(:current_user) { developer }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+
+ it 'does not create an achievement' do
+ expect { subject }.not_to change { Achievements::UserAchievement.count }
+ end
+ end
+
+ context 'when the user has permission' do
+ let(:current_user) { maintainer }
+
+ context 'when the params are invalid' do
+ let(:achievement) { nil }
+
+ it 'returns the validation error' do
+ subject
+
+ expect(graphql_errors.to_s).to include('invalid value for achievementId (Expected value to not be null)')
+ end
+ end
+
+ context 'when the recipient_id is invalid' do
+ let(:recipient_id) { "gid://gitlab/User/#{non_existing_record_id}" }
+
+ it 'returns the validation error' do
+ subject
+
+ expect(graphql_data_at(:achievements_award,
+ :errors)).to include("Couldn't find User with 'id'=#{non_existing_record_id}")
+ end
+ end
+
+ context 'when the achievement_id is invalid' do
+ let(:achievement_id) { "gid://gitlab/Achievements::Achievement/#{non_existing_record_id}" }
+
+ it 'returns the validation error' do
+ subject
+
+ expect(graphql_errors.to_s)
+ .to include("The resource that you are attempting to access does not exist or you don't have permission")
+ end
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(achievements: false)
+ end
+
+ it 'returns the relevant error' do
+ subject
+
+ expect(graphql_errors.to_s)
+ .to include("The resource that you are attempting to access does not exist or you don't have permission")
+ end
+ end
+
+ it 'creates an achievement' do
+ expect { subject }.to change { Achievements::UserAchievement.count }.by(1)
+ end
+
+ it 'returns the new achievement' do
+ subject
+
+ expect(graphql_data_at(:achievements_award, :user_achievement, :achievement, :id))
+ .to eq(achievement.to_global_id.to_s)
+ expect(graphql_data_at(:achievements_award, :user_achievement, :user, :id))
+ .to eq(recipient.to_global_id.to_s)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/achievements/revoke_spec.rb b/spec/requests/api/graphql/mutations/achievements/revoke_spec.rb
new file mode 100644
index 00000000000..925a1bb9fcc
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/achievements/revoke_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Achievements::Revoke, feature_category: :user_profile do
+ include GraphqlHelpers
+
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+ let_it_be(:user_achievement) { create(:user_achievement, achievement: achievement) }
+
+ let(:mutation) { graphql_mutation(:achievements_revoke, params) }
+ let(:user_achievement_id) { user_achievement&.to_global_id }
+ let(:params) { { user_achievement_id: user_achievement_id } }
+
+ subject { post_graphql_mutation(mutation, current_user: current_user) }
+
+ def mutation_response
+ graphql_mutation_response(:achievements_create)
+ end
+
+ before_all do
+ group.add_developer(developer)
+ group.add_maintainer(maintainer)
+ end
+
+ context 'when the user does not have permission' do
+ let(:current_user) { developer }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+
+ it 'does not revoke any achievements' do
+ expect { subject }.not_to change { Achievements::UserAchievement.where(revoked_by_user_id: nil).count }
+ end
+ end
+
+ context 'when the user has permission' do
+ let(:current_user) { maintainer }
+
+ context 'when the params are invalid' do
+ let(:user_achievement) { nil }
+
+ it 'returns the validation error' do
+ subject
+
+ expect(graphql_errors.to_s).to include('invalid value for userAchievementId (Expected value to not be null)')
+ end
+ end
+
+ context 'when the user_achievement_id is invalid' do
+ let(:user_achievement_id) { "gid://gitlab/Achievements::UserAchievement/#{non_existing_record_id}" }
+
+ it 'returns the validation error' do
+ subject
+
+ expect(graphql_errors.to_s)
+ .to include("The resource that you are attempting to access does not exist or you don't have permission")
+ end
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(achievements: false)
+ end
+
+ it 'returns the relevant error' do
+ subject
+
+ expect(graphql_errors.to_s)
+ .to include("The resource that you are attempting to access does not exist or you don't have permission")
+ end
+ end
+
+ it 'revokes an achievement' do
+ expect { subject }.to change { Achievements::UserAchievement.where(revoked_by_user_id: nil).count }.by(-1)
+ end
+
+ it 'returns the revoked achievement' do
+ subject
+
+ expect(graphql_data_at(:achievements_revoke, :user_achievement, :achievement, :id))
+ .to eq(achievement.to_global_id.to_s)
+ expect(graphql_data_at(:achievements_revoke, :user_achievement, :revoked_by_user, :id))
+ .to eq(current_user.to_global_id.to_s)
+ expect(graphql_data_at(:achievements_revoke, :user_achievement, :revoked_at))
+ .not_to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
index 64ea6d32f5f..b3d25155a6f 100644
--- a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
+++ b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues, feature_category: :not_owned do
+RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues, feature_category: :shared do
include GraphqlHelpers
let_it_be(:admin) { create(:admin) }
diff --git a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
index fdbff0f93cd..18cc85d36e0 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Adding an AwardEmoji', feature_category: :not_owned do
+RSpec.describe 'Adding an AwardEmoji', feature_category: :shared do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb
index e200bfc2d18..7ec2b061a88 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Removing an AwardEmoji', feature_category: :not_owned do
+RSpec.describe 'Removing an AwardEmoji', feature_category: :shared do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
index 6dba2b58357..7c6a487cdd0 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Toggling an AwardEmoji', feature_category: :not_owned do
+RSpec.describe 'Toggling an AwardEmoji', feature_category: :shared do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb b/spec/requests/api/graphql/mutations/ci/job/cancel_spec.rb
index 468a9e57f56..abad1ae0812 100644
--- a/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/cancel_spec.rb
@@ -15,12 +15,12 @@ RSpec.describe "JobCancel", feature_category: :continuous_integration do
id: job.to_global_id.to_s
}
graphql_mutation(:job_cancel, variables,
- <<-QL
+ <<-QL
errors
job {
id
}
- QL
+ QL
)
end
diff --git a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb b/spec/requests/api/graphql/mutations/ci/job/play_spec.rb
index 9ba80e51dee..8100274ed97 100644
--- a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/play_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'JobPlay', feature_category: :continuous_integration do
let(:mutation) do
graphql_mutation(:job_play, variables,
- <<-QL
+ <<-QL
errors
job {
id
@@ -28,7 +28,7 @@ RSpec.describe 'JobPlay', feature_category: :continuous_integration do
}
}
}
- QL
+ QL
)
end
diff --git a/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb b/spec/requests/api/graphql/mutations/ci/job/retry_spec.rb
index e49ee6f3163..4114c77491b 100644
--- a/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/retry_spec.rb
@@ -16,12 +16,12 @@ RSpec.describe 'JobRetry', feature_category: :continuous_integration do
id: job.to_global_id.to_s
}
graphql_mutation(:job_retry, variables,
- <<-QL
+ <<-QL
errors
job {
id
}
- QL
+ QL
)
end
@@ -57,12 +57,12 @@ RSpec.describe 'JobRetry', feature_category: :continuous_integration do
}
graphql_mutation(:job_retry, variables,
- <<-QL
+ <<-QL
errors
job {
id
}
- QL
+ QL
)
end
diff --git a/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb b/spec/requests/api/graphql/mutations/ci/job/unschedule_spec.rb
index 6868b0ea279..08e155e808b 100644
--- a/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/unschedule_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'JobUnschedule', feature_category: :continuous_integration do
id: job.to_global_id.to_s
}
graphql_mutation(:job_unschedule, variables,
- <<-QL
+ <<-QL
errors
job {
id
diff --git a/spec/requests/api/graphql/mutations/ci/job_artifact/bulk_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job_artifact/bulk_destroy_spec.rb
new file mode 100644
index 00000000000..4e25669a0ca
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/job_artifact/bulk_destroy_spec.rb
@@ -0,0 +1,197 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'BulkDestroy', feature_category: :build_artifacts do
+ include GraphqlHelpers
+
+ let(:maintainer) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:first_artifact) { create(:ci_job_artifact) }
+ let(:second_artifact) { create(:ci_job_artifact, project: project) }
+ let(:second_artifact_another_project) { create(:ci_job_artifact) }
+ let(:project) { first_artifact.job.project }
+ let(:ids) { [first_artifact.to_global_id.to_s] }
+ let(:not_authorized_project_error_message) do
+ "The resource that you are attempting to access " \
+ "does not exist or you don't have permission to perform this action"
+ end
+
+ let(:mutation) do
+ variables = {
+ project_id: project.to_global_id.to_s,
+ ids: ids
+ }
+ graphql_mutation(:bulk_destroy_job_artifacts, variables, <<~FIELDS)
+ destroyedCount
+ destroyedIds
+ errors
+ FIELDS
+ end
+
+ let(:mutation_response) { graphql_mutation_response(:bulk_destroy_job_artifacts) }
+
+ it 'fails to destroy the artifact if a user not in a project' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect(graphql_errors).to include(
+ a_hash_including('message' => not_authorized_project_error_message)
+ )
+
+ expect(first_artifact.reload).to be_persisted
+ end
+
+ context 'when the `ci_job_artifact_bulk_destroy` feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_job_artifact_bulk_destroy: false)
+ project.add_maintainer(maintainer)
+ end
+
+ it 'returns a resource not available error' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect(graphql_errors).to contain_exactly(
+ hash_including(
+ 'message' => '`ci_job_artifact_bulk_destroy` feature flag is disabled.'
+ )
+ )
+ end
+ end
+
+ context "when the user is a developer in a project" do
+ before do
+ project.add_developer(developer)
+ end
+
+ it 'fails to destroy the artifact' do
+ post_graphql_mutation(mutation, current_user: developer)
+
+ expect(graphql_errors).to include(
+ a_hash_including('message' => not_authorized_project_error_message)
+ )
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(first_artifact.reload).to be_persisted
+ end
+ end
+
+ context "when the user is a maintainer in a project" do
+ before do
+ project.add_maintainer(maintainer)
+ end
+
+ shared_examples 'failing mutation' do
+ it 'rejects the request' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect(graphql_errors(mutation_response)).to include(expected_error_message)
+
+ expected_not_found_artifacts.each do |artifact|
+ expect { artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ expected_found_artifacts.each do |artifact|
+ expect(artifact.reload).to be_persisted
+ end
+ end
+ end
+
+ it 'destroys the artifact' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect(mutation_response).to include("destroyedCount" => 1, "destroyedIds" => [gid_string(first_artifact)])
+ expect(response).to have_gitlab_http_status(:success)
+ expect { first_artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ context "and one artifact doesn't belong to the project" do
+ let(:not_owned_artifact) { create(:ci_job_artifact) }
+ let(:ids) { [first_artifact.to_global_id.to_s, not_owned_artifact.to_global_id.to_s] }
+ let(:expected_error_message) { "Not all artifacts belong to requested project" }
+ let(:expected_not_found_artifacts) { [] }
+ let(:expected_found_artifacts) { [first_artifact, not_owned_artifact] }
+
+ it_behaves_like 'failing mutation'
+ end
+
+ context "and multiple artifacts belong to the maintainer's project" do
+ let(:ids) { [first_artifact.to_global_id.to_s, second_artifact.to_global_id.to_s] }
+
+ it 'destroys all artifacts' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect(mutation_response).to include(
+ "destroyedCount" => 2,
+ "destroyedIds" => [gid_string(first_artifact), gid_string(second_artifact)]
+ )
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect { first_artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { second_artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context "and one artifact belongs to a different maintainer's project" do
+ let(:ids) { [first_artifact.to_global_id.to_s, second_artifact_another_project.to_global_id.to_s] }
+ let(:expected_found_artifacts) { [first_artifact, second_artifact_another_project] }
+ let(:expected_not_found_artifacts) { [] }
+ let(:expected_error_message) { "Not all artifacts belong to requested project" }
+
+ it_behaves_like 'failing mutation'
+ end
+
+ context "and not found" do
+ let(:ids) { [first_artifact.to_global_id.to_s, second_artifact.to_global_id.to_s] }
+ let(:not_found_ids) { expected_not_found_artifacts.map(&:id).join(',') }
+ let(:expected_error_message) { "Artifacts (#{not_found_ids}) not found" }
+
+ before do
+ expected_not_found_artifacts.each(&:destroy!)
+ end
+
+ context "with one artifact" do
+ let(:expected_not_found_artifacts) { [second_artifact] }
+ let(:expected_found_artifacts) { [first_artifact] }
+
+ it_behaves_like 'failing mutation'
+ end
+
+ context "with all artifact" do
+ let(:expected_not_found_artifacts) { [first_artifact, second_artifact] }
+ let(:expected_found_artifacts) { [] }
+
+ it_behaves_like 'failing mutation'
+ end
+ end
+
+ context 'when empty request' do
+ before do
+ project.add_maintainer(maintainer)
+ end
+
+ context 'with nil value' do
+ let(:ids) { nil }
+
+ it 'does nothing and returns empty answer' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect_graphql_errors_to_include(/was provided invalid value for ids \(Expected value to not be null\)/)
+ end
+ end
+
+ context 'with empty array' do
+ let(:ids) { [] }
+
+ it 'raises argument error' do
+ post_graphql_mutation(mutation, current_user: maintainer)
+
+ expect_graphql_errors_to_include(/IDs array of job artifacts can not be empty/)
+ end
+ end
+ end
+
+ def gid_string(object)
+ Gitlab::GlobalId.build(object, id: object.id).to_s
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
index 99e55c44773..0951d165d46 100644
--- a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
@@ -101,21 +101,6 @@ RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integr
expect(response).to have_gitlab_http_status(:success)
expect(project.ci_inbound_job_token_scope_enabled).to eq(true)
end
-
- context 'when ci_inbound_job_token_scope disabled' do
- before do
- stub_feature_flags(ci_inbound_job_token_scope: false)
- end
-
- it 'does not update inbound_job_token_scope_enabled' do
- post_graphql_mutation(mutation, current_user: user)
-
- project.reload
-
- expect(response).to have_gitlab_http_status(:success)
- expect(project.ci_inbound_job_token_scope_enabled).to eq(true)
- end
- end
end
it 'updates ci_opt_in_jwt' do
diff --git a/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb b/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb
new file mode 100644
index 00000000000..f39f6f84c99
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
+
+ let(:mutation_params) do
+ {
+ description: 'create description',
+ maintenance_note: 'create maintenance note',
+ maximum_timeout: 900,
+ access_level: 'REF_PROTECTED',
+ paused: true,
+ run_untagged: false,
+ tag_list: %w[tag1 tag2]
+ }
+ end
+
+ let(:mutation) do
+ variables = {
+ **mutation_params
+ }
+
+ graphql_mutation(
+ :runner_create,
+ variables,
+ <<-QL
+ runner {
+ ephemeralAuthenticationToken
+
+ runnerType
+ description
+ maintenanceNote
+ paused
+ tagList
+ accessLevel
+ locked
+ maximumTimeout
+ runUntagged
+ }
+ errors
+ QL
+ )
+ end
+
+ let(:mutation_response) { graphql_mutation_response(:runner_create) }
+
+ context 'when user does not have permissions' do
+ let(:current_user) { user }
+
+ it 'returns an error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['errors']).to contain_exactly "Insufficient permissions"
+ end
+ end
+
+ context 'when user has permissions', :enable_admin_mode do
+ let(:current_user) { admin }
+
+ context 'when :create_runner_workflow_for_admin feature flag is disabled' do
+ before do
+ stub_feature_flags(create_runner_workflow_for_admin: false)
+ end
+
+ it 'returns an error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_errors).not_to be_empty
+ expect(graphql_errors[0]['message'])
+ .to eq("`create_runner_workflow_for_admin` feature flag is disabled.")
+ end
+ end
+
+ context 'when success' do
+ it do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+
+ mutation_params.each_key do |key|
+ expect(mutation_response['runner'][key.to_s.camelize(:lower)]).to eq mutation_params[key]
+ end
+
+ expect(mutation_response['runner']['ephemeralAuthenticationToken']).to start_with 'glrt'
+
+ expect(mutation_response['errors']).to eq([])
+ end
+ end
+
+ context 'when failure' do
+ let(:mutation_params) do
+ {
+ description: "",
+ maintenanceNote: "",
+ paused: true,
+ accessLevel: "NOT_PROTECTED",
+ runUntagged: false,
+ tagList:
+ [],
+ maximumTimeout: 1
+ }
+ end
+
+ it do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+
+ expect(mutation_response['errors']).to contain_exactly(
+ "Tags list can not be empty when runner is not allowed to pick untagged jobs",
+ "Maximum timeout needs to be at least 10 minutes"
+ )
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
index ea2ce8a13e2..19a52086f34 100644
--- a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new Custom Emoji', feature_category: :not_owned do
+RSpec.describe 'Creation of a new Custom Emoji', feature_category: :shared do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
index ad7a043909a..2623d3d8410 100644
--- a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Deletion of custom emoji', feature_category: :not_owned do
+RSpec.describe 'Deletion of custom emoji', feature_category: :shared do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/mutations/design_management/update_spec.rb b/spec/requests/api/graphql/mutations/design_management/update_spec.rb
new file mode 100644
index 00000000000..9558f2538f1
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/design_management/update_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe "updating designs", feature_category: :design_management do
+ include GraphqlHelpers
+ include DesignManagementTestHelpers
+
+ let_it_be(:issue) { create(:issue) }
+ let_it_be_with_reload(:design) { create(:design, description: 'old description', issue: issue) }
+ let_it_be(:developer) { create(:user, developer_projects: [issue.project]) }
+
+ let(:user) { developer }
+ let(:description) { 'new description' }
+
+ let(:mutation) do
+ input = {
+ id: design.to_global_id.to_s,
+ description: description
+ }.compact
+
+ graphql_mutation(:design_management_update, input, <<~FIELDS)
+ errors
+ design {
+ description
+ descriptionHtml
+ }
+ FIELDS
+ end
+
+ let(:update_design) { post_graphql_mutation(mutation, current_user: user) }
+ let(:mutation_response) { graphql_mutation_response(:design_management_update) }
+
+ before do
+ enable_design_management
+ end
+
+ it 'updates design' do
+ update_design
+
+ expect(graphql_errors).not_to be_present
+ expect(mutation_response).to eq(
+ 'errors' => [],
+ 'design' => {
+ 'description' => description,
+ 'descriptionHtml' => "<p data-sourcepos=\"1:1-1:15\" dir=\"auto\">#{description}</p>"
+ }
+ )
+ end
+
+ context 'when the user is not allowed to update designs' do
+ let(:user) { create(:user) }
+
+ it 'returns an error' do
+ update_design
+
+ expect(graphql_errors).to be_present
+ end
+ end
+
+ context 'when update fails' do
+ let(:description) { 'x' * 1_000_001 }
+
+ it 'returns an error' do
+ update_design
+
+ expect(graphql_errors).not_to be_present
+ expect(mutation_response).to eq(
+ 'errors' => ["Description is too long (maximum is 1000000 characters)"],
+ 'design' => {
+ 'description' => 'old description',
+ 'descriptionHtml' => '<p data-sourcepos="1:1-1:15" dir="auto">old description</p>'
+ }
+ )
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/issues/bulk_update_spec.rb b/spec/requests/api/graphql/mutations/issues/bulk_update_spec.rb
index b9c83311908..b729585a89b 100644
--- a/spec/requests/api/graphql/mutations/issues/bulk_update_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/bulk_update_spec.rb
@@ -8,7 +8,9 @@ RSpec.describe 'Bulk update issues', feature_category: :team_planning do
let_it_be(:developer) { create(:user) }
let_it_be(:group) { create(:group).tap { |group| group.add_developer(developer) } }
let_it_be(:project) { create(:project, group: group) }
- let_it_be(:updatable_issues, reload: true) { create_list(:issue, 2, project: project) }
+ let_it_be(:label1) { create(:group_label, group: group) }
+ let_it_be(:label2) { create(:group_label, group: group) }
+ let_it_be(:updatable_issues, reload: true) { create_list(:issue, 2, project: project, label_ids: [label1.id]) }
let_it_be(:milestone) { create(:milestone, group: group) }
let(:parent) { project }
@@ -21,10 +23,36 @@ RSpec.describe 'Bulk update issues', feature_category: :team_planning do
let(:additional_arguments) do
{
assignee_ids: [current_user.to_gid.to_s],
- milestone_id: milestone.to_gid.to_s
+ milestone_id: milestone.to_gid.to_s,
+ state_event: :CLOSE,
+ add_label_ids: [label2.to_gid.to_s],
+ remove_label_ids: [label1.to_gid.to_s],
+ subscription_event: :UNSUBSCRIBE
}
end
+ before_all do
+ updatable_issues.each { |i| i.subscribe(developer, project) }
+ end
+
+ context 'when Gitlab is FOSS only' do
+ unless Gitlab.ee?
+ context 'when parent is a group' do
+ let(:parent) { group }
+
+ it 'does not allow bulk updating issues at the group level' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_errors).to contain_exactly(
+ hash_including(
+ 'message' => match(/does not represent an instance of IssueParent/)
+ )
+ )
+ end
+ end
+ end
+ end
+
context 'when the `bulk_update_issues_mutation` feature flag is disabled' do
before do
stub_feature_flags(bulk_update_issues_mutation: false)
@@ -67,6 +95,11 @@ RSpec.describe 'Bulk update issues', feature_category: :team_planning do
updatable_issues.each(&:reload)
end.to change { updatable_issues.flat_map(&:assignee_ids) }.from([]).to([current_user.id] * 2)
.and(change { updatable_issues.map(&:milestone_id) }.from([nil] * 2).to([milestone.id] * 2))
+ .and(change { updatable_issues.map(&:state) }.from(['opened'] * 2).to(['closed'] * 2))
+ .and(change { updatable_issues.flat_map(&:label_ids) }.from([label1.id] * 2).to([label2.id] * 2))
+ .and(
+ change { updatable_issues.map { |i| i.subscribed?(developer, project) } }.from([true] * 2).to([false] * 2)
+ )
expect(mutation_response).to include(
'updatedIssueCount' => updatable_issues.count
@@ -88,37 +121,6 @@ RSpec.describe 'Bulk update issues', feature_category: :team_planning do
end
end
- context 'when scoping to a parent group' do
- let(:parent) { group }
-
- it 'updates all issues' do
- expect do
- post_graphql_mutation(mutation, current_user: current_user)
- updatable_issues.each(&:reload)
- end.to change { updatable_issues.flat_map(&:assignee_ids) }.from([]).to([current_user.id] * 2)
- .and(change { updatable_issues.map(&:milestone_id) }.from([nil] * 2).to([milestone.id] * 2))
-
- expect(mutation_response).to include(
- 'updatedIssueCount' => updatable_issues.count
- )
- end
-
- context 'when current user cannot read the specified group' do
- let(:parent) { create(:group, :private) }
-
- it 'returns a resource not found error' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(graphql_errors).to contain_exactly(
- hash_including(
- 'message' => "The resource that you are attempting to access does not exist or you don't have " \
- 'permission to perform this action'
- )
- )
- end
- end
- end
-
context 'when setting arguments to null or none' do
let(:additional_arguments) { { assignee_ids: [], milestone_id: nil } }
diff --git a/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb b/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb
index ad70129a7bc..f15b52f53a3 100644
--- a/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb
+++ b/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb
@@ -5,126 +5,14 @@ require 'spec_helper'
RSpec.describe 'GroupMemberBulkUpdate', feature_category: :subgroups do
include GraphqlHelpers
- let_it_be(:current_user) { create(:user) }
- let_it_be(:user1) { create(:user) }
- let_it_be(:user2) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:group_member1) { create(:group_member, group: group, user: user1) }
- let_it_be(:group_member2) { create(:group_member, group: group, user: user2) }
+ let_it_be(:parent_group) { create(:group) }
+ let_it_be(:parent_group_member) { create(:group_member, group: parent_group) }
+ let_it_be(:group) { create(:group, parent: parent_group) }
+ let_it_be(:source) { group }
+ let_it_be(:member_type) { :group_member }
let_it_be(:mutation_name) { :group_member_bulk_update }
+ let_it_be(:source_id_key) { 'group_id' }
+ let_it_be(:response_member_field) { 'groupMembers' }
- let(:input) do
- {
- 'group_id' => group.to_global_id.to_s,
- 'user_ids' => [user1.to_global_id.to_s, user2.to_global_id.to_s],
- 'access_level' => 'GUEST'
- }
- end
-
- let(:extra_params) { { expires_at: 10.days.from_now } }
- let(:input_params) { input.merge(extra_params) }
- let(:mutation) { graphql_mutation(mutation_name, input_params) }
- let(:mutation_response) { graphql_mutation_response(mutation_name) }
-
- context 'when user is not logged-in' do
- it_behaves_like 'a mutation that returns a top-level access error'
- end
-
- context 'when user is not an owner' do
- before do
- group.add_maintainer(current_user)
- end
-
- it_behaves_like 'a mutation that returns a top-level access error'
- end
-
- context 'when user is an owner' do
- before do
- group.add_owner(current_user)
- end
-
- shared_examples 'updates the user access role' do
- specify do
- post_graphql_mutation(mutation, current_user: current_user)
-
- new_access_levels = mutation_response['groupMembers'].map { |member| member['accessLevel']['integerValue'] }
- expect(response).to have_gitlab_http_status(:success)
- expect(mutation_response['errors']).to be_empty
- expect(new_access_levels).to all(be Gitlab::Access::GUEST)
- end
- end
-
- it_behaves_like 'updates the user access role'
-
- context 'when inherited members are passed' do
- let_it_be(:subgroup) { create(:group, parent: group) }
- let_it_be(:subgroup_member) { create(:group_member, group: subgroup) }
-
- let(:input) do
- {
- 'group_id' => group.to_global_id.to_s,
- 'user_ids' => [user1.to_global_id.to_s, user2.to_global_id.to_s, subgroup_member.user.to_global_id.to_s],
- 'access_level' => 'GUEST'
- }
- end
-
- it 'does not update the members' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- error = Mutations::Members::Groups::BulkUpdate::INVALID_MEMBERS_ERROR
- expect(json_response['errors'].first['message']).to include(error)
- end
- end
-
- context 'when members count is more than the allowed limit' do
- let(:max_members_update_limit) { 1 }
-
- before do
- stub_const('Mutations::Members::Groups::BulkUpdate::MAX_MEMBERS_UPDATE_LIMIT', max_members_update_limit)
- end
-
- it 'does not update the members' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- error = Mutations::Members::Groups::BulkUpdate::MAX_MEMBERS_UPDATE_ERROR
- expect(json_response['errors'].first['message']).to include(error)
- end
- end
-
- context 'when the update service raises access denied error' do
- before do
- allow_next_instance_of(Members::UpdateService) do |instance|
- allow(instance).to receive(:execute).and_raise(Gitlab::Access::AccessDeniedError)
- end
- end
-
- it 'does not update the members' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(mutation_response['groupMembers']).to be_nil
- expect(mutation_response['errors'])
- .to contain_exactly("Unable to update members, please check user permissions.")
- end
- end
-
- context 'when the update service returns an error message' do
- before do
- allow_next_instance_of(Members::UpdateService) do |instance|
- error_result = {
- message: 'Expires at cannot be a date in the past',
- status: :error,
- members: [group_member1]
- }
- allow(instance).to receive(:execute).and_return(error_result)
- end
- end
-
- it 'will pass through the error' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect(mutation_response['groupMembers'].first['id']).to eq(group_member1.to_global_id.to_s)
- expect(mutation_response['errors']).to contain_exactly('Expires at cannot be a date in the past')
- end
- end
- end
+ it_behaves_like 'members bulk update mutation'
end
diff --git a/spec/requests/api/graphql/mutations/members/projects/bulk_update_spec.rb b/spec/requests/api/graphql/mutations/members/projects/bulk_update_spec.rb
new file mode 100644
index 00000000000..cbef9715cbe
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/members/projects/bulk_update_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'ProjectMemberBulkUpdate', feature_category: :projects do
+ include GraphqlHelpers
+
+ let_it_be(:parent_group) { create(:group) }
+ let_it_be(:parent_group_member) { create(:group_member, group: parent_group) }
+ let_it_be(:project) { create(:project, group: parent_group) }
+ let_it_be(:source) { project }
+ let_it_be(:member_type) { :project_member }
+ let_it_be(:mutation_name) { :project_member_bulk_update }
+ let_it_be(:source_id_key) { 'project_id' }
+ let_it_be(:response_member_field) { 'projectMembers' }
+
+ it_behaves_like 'members bulk update mutation'
+end
diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
index bce57b47aab..3c7f4a030f9 100644
--- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create, feature_categ
graphql_mutation_response(:create_annotation)
end
- specify { expect(described_class).to require_graphql_authorizations(:create_metrics_dashboard_annotation) }
+ specify { expect(described_class).to require_graphql_authorizations(:admin_metrics_dashboard_annotation) }
context 'when annotation source is environment' do
let(:mutation) do
diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
index f505dc25dc0..c104138b725 100644
--- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Delete, feature_categ
graphql_mutation_response(:delete_annotation)
end
- specify { expect(described_class).to require_graphql_authorizations(:delete_metrics_dashboard_annotation) }
+ specify { expect(described_class).to require_graphql_authorizations(:admin_metrics_dashboard_annotation) }
context 'when the user has permission to delete the annotation' do
before do
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 a6253ba424b..2a0b5f291dc 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -104,7 +104,8 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do
end
context 'as work item' do
- let(:noteable) { create(:work_item, :issue, project: project) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:noteable) { create(:work_item, :issue, project: project) }
context 'when using internal param' do
let(:variables_extra) { { internal: true } }
@@ -130,6 +131,19 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do
it_behaves_like 'a mutation that returns top-level errors',
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
end
+
+ context 'when body contains quick actions' do
+ let_it_be(:noteable) { create(:work_item, :task, project: project) }
+
+ let(:variables_extra) { {} }
+
+ it_behaves_like 'work item supports labels widget updates via quick actions'
+ it_behaves_like 'work item does not support labels widget updates via quick actions'
+ it_behaves_like 'work item supports assignee widget updates via quick actions'
+ it_behaves_like 'work item does not support assignee widget updates via quick actions'
+ it_behaves_like 'work item supports start and due date widget updates via quick actions'
+ it_behaves_like 'work item does not support start and due date widget updates via quick actions'
+ end
end
end
diff --git a/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb b/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb
new file mode 100644
index 00000000000..a77c026dd06
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/projects/sync_fork_spec.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "Sync project fork", feature_category: :source_code_management do
+ include GraphqlHelpers
+ include ProjectForksHelper
+ include ExclusiveLeaseHelpers
+
+ let_it_be(:source_project) { create(:project, :repository, :public) }
+ let_it_be(:current_user) { create(:user, maintainer_projects: [source_project]) }
+ let_it_be(:project, refind: true) { fork_project(source_project, current_user, { repository: true }) }
+ let_it_be(:target_branch) { project.default_branch }
+
+ let(:mutation) do
+ params = { project_path: project.full_path, target_branch: target_branch }
+
+ graphql_mutation(:project_sync_fork, params) do
+ <<-QL.strip_heredoc
+ details {
+ ahead
+ behind
+ isSyncing
+ hasConflicts
+ }
+ errors
+ QL
+ end
+ end
+
+ before do
+ source_project.change_head('feature')
+ end
+
+ context 'when synchronize_fork feature flag is disabled' do
+ before do
+ stub_feature_flags(synchronize_fork: false)
+ end
+
+ it 'does not call the sync service' do
+ expect(::Projects::Forks::SyncWorker).not_to receive(:perform_async)
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_mutation_response(:project_sync_fork)).to eq(
+ {
+ 'details' => nil,
+ 'errors' => ['Feature flag is disabled']
+ })
+ end
+ end
+
+ context 'when the user does not have permission' do
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+
+ it 'does not call the sync service' do
+ expect(::Projects::Forks::SyncWorker).not_to receive(:perform_async)
+
+ post_graphql_mutation(mutation, current_user: current_user)
+ end
+ end
+
+ context 'when the user has permission' do
+ context 'and the sync service executes successfully', :sidekiq_inline do
+ it 'calls the sync service' do
+ expect(::Projects::Forks::SyncWorker).to receive(:perform_async).and_call_original
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_mutation_response(:project_sync_fork)).to eq(
+ {
+ 'details' => { 'ahead' => 30, 'behind' => 0, "hasConflicts" => false, "isSyncing" => false },
+ 'errors' => []
+ })
+ end
+ end
+
+ context 'and the sync service fails to execute' do
+ let(:target_branch) { 'markdown' }
+
+ def expect_error_response(message)
+ expect(::Projects::Forks::SyncWorker).not_to receive(:perform_async)
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_mutation_response(:project_sync_fork)['errors']).to eq([message])
+ end
+
+ context 'when fork details cannot be resolved' do
+ let_it_be(:project) { source_project }
+
+ it 'returns an error' do
+ expect_error_response('This branch of this project cannot be updated from the upstream')
+ end
+ end
+
+ context 'when the previous execution resulted in a conflict' do
+ it 'returns an error' do
+ expect_next_instance_of(::Projects::Forks::Details) do |instance|
+ expect(instance).to receive(:has_conflicts?).twice.and_return(true)
+ end
+
+ expect_error_response('The synchronization cannot happen due to the merge conflict')
+ expect(graphql_mutation_response(:project_sync_fork)['details']['hasConflicts']).to eq(true)
+ end
+ end
+
+ context 'when the request is rate limited' do
+ it 'returns an error' do
+ expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
+
+ expect_error_response('This service has been called too many times.')
+ end
+ end
+
+ context 'when another fork sync is in progress' do
+ it 'returns an error' do
+ expect_next_instance_of(Projects::Forks::Details) do |instance|
+ lease = instance_double(Gitlab::ExclusiveLease, try_obtain: false, exists?: true)
+ expect(instance).to receive(:exclusive_lease).twice.and_return(lease)
+ end
+
+ expect_error_response('Another fork sync is already in progress')
+ expect(graphql_mutation_response(:project_sync_fork)['details']['isSyncing']).to eq(true)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index fa087e6773c..3b98ee3c2e9 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -193,7 +193,6 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:user) { current_user }
let(:property) { 'g_edit_by_snippet_ide' }
let(:namespace) { project.namespace }
@@ -203,8 +202,6 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
let(:context) do
[Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context]
end
-
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
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
index 97bf060356a..d2df3e8bda2 100644
--- 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
@@ -23,7 +23,7 @@ RSpec.describe "Create a work item from a task in a work item's description", fe
}
end
- let(:mutation) { graphql_mutation(:workItemCreateFromTask, input) }
+ let(:mutation) { graphql_mutation(:workItemCreateFromTask, input, nil, ['productAnalyticsState']) }
let(:mutation_response) { graphql_mutation_response(:work_item_create_from_task) }
context 'the user is not allowed to update a work item' do
diff --git a/spec/requests/api/graphql/mutations/work_items/create_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_spec.rb
index 16f78b67b5c..7519389ab49 100644
--- a/spec/requests/api/graphql/mutations/work_items/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/create_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
'title' => 'new title',
'description' => 'new description',
'confidential' => true,
- 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_global_id.to_s
+ 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_gid.to_s
}
end
@@ -43,14 +43,14 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
expect(created_work_item.work_item_type.base_type).to eq('task')
expect(mutation_response['workItem']).to include(
input.except('workItemTypeId').merge(
- 'id' => created_work_item.to_global_id.to_s,
+ 'id' => created_work_item.to_gid.to_s,
'workItemType' => hash_including('name' => 'Task')
)
)
end
context 'when input is invalid' do
- let(:input) { { 'title' => '', 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_global_id.to_s } }
+ let(:input) { { 'title' => '', 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_gid.to_s } }
it 'does not create and returns validation errors' do
expect do
@@ -98,8 +98,8 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
let(:input) do
{
title: 'item1',
- workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s,
- hierarchyWidget: { 'parentId' => parent.to_global_id.to_s }
+ workItemTypeId: WorkItems::Type.default_by_type(:task).to_gid.to_s,
+ hierarchyWidget: { 'parentId' => parent.to_gid.to_s }
}
end
@@ -110,7 +110,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
expect(widgets_response).to include(
{
'children' => { 'edges' => [] },
- 'parent' => { 'id' => parent.to_global_id.to_s },
+ 'parent' => { 'id' => parent.to_gid.to_s },
'type' => 'HIERARCHY'
}
)
@@ -137,6 +137,40 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
expect(graphql_errors.first['message']).to include('No object found for `parentId')
end
end
+
+ context 'when adjacent is already in place' do
+ let_it_be(:adjacent) { create(:work_item, :task, project: project) }
+
+ let(:work_item) { WorkItem.last }
+
+ let(:input) do
+ {
+ title: 'item1',
+ workItemTypeId: WorkItems::Type.default_by_type(:task).to_gid.to_s,
+ hierarchyWidget: { 'parentId' => parent.to_gid.to_s }
+ }
+ end
+
+ before(:all) do
+ create(:parent_link, work_item_parent: parent, work_item: adjacent, relative_position: 0)
+ end
+
+ it 'creates work item and sets the relative position to be AFTER adjacent' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change(WorkItem, :count).by(1)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(widgets_response).to include(
+ {
+ 'children' => { 'edges' => [] },
+ 'parent' => { 'id' => parent.to_gid.to_s },
+ 'type' => 'HIERARCHY'
+ }
+ )
+ expect(work_item.parent_link.relative_position).to be > adjacent.parent_link.relative_position
+ end
+ end
end
context 'when unsupported widget input is sent' do
@@ -144,7 +178,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
{
'title' => 'new title',
'description' => 'new description',
- 'workItemTypeId' => WorkItems::Type.default_by_type(:test_case).to_global_id.to_s,
+ 'workItemTypeId' => WorkItems::Type.default_by_type(:test_case).to_gid.to_s,
'hierarchyWidget' => {}
}
end
@@ -181,8 +215,8 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
let(:input) do
{
title: 'some WI',
- workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s,
- milestoneWidget: { 'milestoneId' => milestone.to_global_id.to_s }
+ workItemTypeId: WorkItems::Type.default_by_type(:task).to_gid.to_s,
+ milestoneWidget: { 'milestoneId' => milestone.to_gid.to_s }
}
end
@@ -196,7 +230,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
expect(widgets_response).to include(
{
'type' => 'MILESTONE',
- 'milestone' => { 'id' => milestone.to_global_id.to_s }
+ 'milestone' => { 'id' => milestone.to_gid.to_s }
}
)
end
diff --git a/spec/requests/api/graphql/mutations/work_items/export_spec.rb b/spec/requests/api/graphql/mutations/work_items/export_spec.rb
new file mode 100644
index 00000000000..3cadaab5201
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/work_items/export_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Export work items', feature_category: :team_planning do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:reporter) { create(:user).tap { |user| project.add_reporter(user) } }
+ let_it_be(:guest) { create(:user).tap { |user| project.add_guest(user) } }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+
+ let(:input) { { 'projectPath' => project.full_path } }
+ let(:mutation) { graphql_mutation(:workItemExport, input) }
+ let(:mutation_response) { graphql_mutation_response(:work_item_export) }
+
+ context 'when user is not allowed to export work items' do
+ let(:current_user) { guest }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when import_export_work_items_csv feature flag is disabled' do
+ let(:current_user) { reporter }
+
+ before do
+ stub_feature_flags(import_export_work_items_csv: false)
+ end
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['`import_export_work_items_csv` feature flag is disabled.']
+ end
+
+ context 'when user has permissions to export work items' do
+ let(:current_user) { reporter }
+ let(:input) do
+ super().merge(
+ 'selectedFields' => %w[TITLE AUTHOR TYPE AUTHOR_USERNAME CREATED_AT],
+ 'authorUsername' => 'admin',
+ 'iids' => [work_item.iid.to_s],
+ 'state' => 'opened',
+ 'types' => 'TASK',
+ 'search' => 'any',
+ 'in' => 'TITLE'
+ )
+ end
+
+ it 'schedules export job with given arguments', :aggregate_failures do
+ expected_arguments = {
+ selected_fields: ['title', 'author', 'type', 'author username', 'created_at'],
+ author_username: 'admin',
+ iids: [work_item.iid.to_s],
+ state: 'opened',
+ issue_types: ['task'],
+ search: 'any',
+ in: ['title']
+ }
+
+ expect(IssuableExportCsvWorker)
+ .to receive(:perform_async).with(:work_item, current_user.id, project.id, expected_arguments)
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['errors']).to be_empty
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
index ddd294e8f82..76dc60be799 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
@@ -7,10 +7,11 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:author) { create(:user).tap { |user| project.add_reporter(user) } }
let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
let_it_be(:reporter) { create(:user).tap { |user| project.add_reporter(user) } }
let_it_be(:guest) { create(:user).tap { |user| project.add_guest(user) } }
- let_it_be(:work_item, refind: true) { create(:work_item, project: project) }
+ let_it_be(:work_item, refind: true) { create(:work_item, project: project, author: author) }
let(:work_item_event) { 'CLOSE' }
let(:input) { { 'stateEvent' => work_item_event, 'title' => 'updated title' } }
@@ -843,6 +844,140 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end
end
+ context 'when updating notifications subscription' do
+ let_it_be(:current_user) { reporter }
+ let(:input) { { 'notificationsWidget' => { 'subscribed' => desired_state } } }
+
+ let(:fields) do
+ <<~FIELDS
+ workItem {
+ widgets {
+ type
+ ... on WorkItemWidgetNotifications {
+ subscribed
+ }
+ }
+ }
+ errors
+ FIELDS
+ end
+
+ subject(:update_work_item) { post_graphql_mutation(mutation, current_user: current_user) }
+
+ shared_examples 'subscription updated successfully' do
+ let_it_be(:subscription) do
+ create(
+ :subscription, project: project,
+ user: current_user,
+ subscribable: work_item,
+ subscribed: !desired_state
+ )
+ end
+
+ it "updates existing work item's subscription state" do
+ expect do
+ update_work_item
+ subscription.reload
+ end.to change(subscription, :subscribed).to(desired_state)
+ .and(change { work_item.reload.subscribed?(reporter, project) }.to(desired_state))
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['workItem']['widgets']).to include(
+ {
+ 'subscribed' => desired_state,
+ 'type' => 'NOTIFICATIONS'
+ }
+ )
+ end
+ end
+
+ shared_examples 'subscription update ignored' do
+ context 'when user is subscribed with a subscription record' do
+ let_it_be(:subscription) do
+ create(
+ :subscription, project: project,
+ user: current_user,
+ subscribable: work_item,
+ subscribed: !desired_state
+ )
+ end
+
+ it 'ignores the update request' do
+ expect do
+ update_work_item
+ subscription.reload
+ end.to not_change(subscription, :subscribed)
+ .and(not_change { work_item.subscribed?(current_user, project) })
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+
+ context 'when user is subscribed by being a participant' do
+ let_it_be(:current_user) { author }
+
+ it 'ignores the update request' do
+ expect do
+ update_work_item
+ end.to not_change(Subscription, :count)
+ .and(not_change { work_item.subscribed?(current_user, project) })
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+ end
+
+ context 'when work item update fails' do
+ let_it_be(:desired_state) { false }
+ let(:input) { { 'title' => nil, 'notificationsWidget' => { 'subscribed' => desired_state } } }
+
+ it_behaves_like 'subscription update ignored'
+ end
+
+ context 'when user cannot update work item' do
+ let_it_be(:desired_state) { false }
+
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(current_user, :update_subscription, work_item).and_return(false)
+ end
+
+ it_behaves_like 'subscription update ignored'
+ end
+
+ context 'when user can update work item' do
+ context 'when subscribing to notifications' do
+ let_it_be(:desired_state) { true }
+
+ it_behaves_like 'subscription updated successfully'
+ end
+
+ context 'when unsubscribing from notifications' do
+ let_it_be(:desired_state) { false }
+
+ it_behaves_like 'subscription updated successfully'
+
+ context 'when user is subscribed by being a participant' do
+ let_it_be(:current_user) { author }
+
+ it 'creates a subscription with desired state' do
+ expect { update_work_item }.to change(Subscription, :count).by(1)
+ .and(change { work_item.reload.subscribed?(author, project) }.to(desired_state))
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['workItem']['widgets']).to include(
+ {
+ 'subscribed' => desired_state,
+ 'type' => 'NOTIFICATIONS'
+ }
+ )
+ end
+ end
+ end
+ end
+ end
+
context 'when unsupported widget input is sent' do
let_it_be(:test_case) { create(:work_item_type, :default, :test_case) }
let_it_be(:work_item) { create(:work_item, work_item_type: test_case, project: project) }
diff --git a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb
index 999c685ac6a..717de983871 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'Update a work item task', feature_category: :team_planning do
let(:task_params) { { 'title' => 'UPDATED' } }
let(:task_input) { { 'id' => task.to_global_id.to_s }.merge(task_params) }
let(:input) { { 'id' => work_item.to_global_id.to_s, 'taskData' => task_input } }
- let(:mutation) { graphql_mutation(:workItemUpdateTask, input) }
+ let(:mutation) { graphql_mutation(:workItemUpdateTask, input, nil, ['productAnalyticsState']) }
let(:mutation_response) { graphql_mutation_response(:work_item_update_task) }
context 'the user is not allowed to read a work item' do
diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb
index 4e12da3e3ab..83edacaf831 100644
--- a/spec/requests/api/graphql/namespace/projects_spec.rb
+++ b/spec/requests/api/graphql/namespace/projects_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'getting projects', feature_category: :projects do
projects(includeSubgroups: #{include_subgroups}) {
edges {
node {
- #{all_graphql_fields_for('Project', max_depth: 1)}
+ #{all_graphql_fields_for('Project', max_depth: 1, excluded: ['productAnalyticsState'])}
}
}
}
diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb
index 82fcc5254ad..7610a4aaac1 100644
--- a/spec/requests/api/graphql/packages/package_spec.rb
+++ b/spec/requests/api/graphql/packages/package_spec.rb
@@ -270,6 +270,31 @@ RSpec.describe 'package details', feature_category: :package_registry do
it 'returns composer_config_repository_url correctly' do
expect(graphql_data_at(:package, :composer_config_repository_url)).to eq("localhost/#{group.id}")
end
+
+ context 'with access to package registry for everyone' do
+ before do
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ subject
+ end
+
+ it 'returns pypi_url correctly' do
+ expect(graphql_data_at(:package, :pypi_url)).to eq("http://__token__:<your_personal_token>@localhost/api/v4/projects/#{project.id}/packages/pypi/simple")
+ end
+ end
+
+ context 'when project is public' do
+ let_it_be(:public_project) { create(:project, :public, group: group) }
+ let_it_be(:composer_package) { create(:composer_package, project: public_project) }
+ let(:package_global_id) { global_id_of(composer_package) }
+
+ before do
+ subject
+ end
+
+ it 'returns pypi_url correctly' do
+ expect(graphql_data_at(:package, :pypi_url)).to eq("http://localhost/api/v4/projects/#{public_project.id}/packages/pypi/simple")
+ end
+ end
end
context 'web_path' do
diff --git a/spec/requests/api/graphql/project/base_service_spec.rb b/spec/requests/api/graphql/project/base_service_spec.rb
index 7b1b95eaf58..b27cddea07b 100644
--- a/spec/requests/api/graphql/project/base_service_spec.rb
+++ b/spec/requests/api/graphql/project/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'query Jira service', feature_category: :authentication_and_authorization do
+RSpec.describe 'query Jira service', feature_category: :system_access do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb
index 7ccf8a6f5bf..9a40a972256 100644
--- a/spec/requests/api/graphql/project/container_repositories_spec.rb
+++ b/spec/requests/api/graphql/project/container_repositories_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'getting container repositories in a project', feature_category:
let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten }
let_it_be(:container_expiration_policy) { project.container_expiration_policy }
- let(:excluded_fields) { %w[pipeline jobs] }
+ let(:excluded_fields) { %w[pipeline jobs productAnalyticsState] }
let(:container_repositories_fields) do
<<~GQL
edges {
@@ -155,7 +155,7 @@ RSpec.describe 'getting container repositories in a project', feature_category:
it_behaves_like 'handling graphql network errors with the container registry'
it_behaves_like 'not hitting graphql network errors with the container registry' do
- let(:excluded_fields) { %w[pipeline jobs tags tagsCount] }
+ let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState] }
end
it 'returns the total count of container repositories' do
diff --git a/spec/requests/api/graphql/project/flow_metrics_spec.rb b/spec/requests/api/graphql/project/flow_metrics_spec.rb
new file mode 100644
index 00000000000..3b5758b3a2e
--- /dev/null
+++ b/spec/requests/api/graphql/project/flow_metrics_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting project flow metrics', feature_category: :value_stream_management do
+ include GraphqlHelpers
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project1) { create(:project, :repository, group: group) }
+ # This is done so we can use the same count expectations in the shared examples and
+ # reuse the shared example for the group-level test.
+ let_it_be(:project2) { project1 }
+ let_it_be(:production_environment1) { create(:environment, :production, project: project1) }
+ let_it_be(:production_environment2) { production_environment1 }
+ let_it_be(:current_user) { create(:user, maintainer_projects: [project1]) }
+
+ let(:full_path) { project1.full_path }
+ let(:context) { :project }
+
+ it_behaves_like 'value stream analytics flow metrics issueCount examples'
+
+ it_behaves_like 'value stream analytics flow metrics deploymentCount examples'
+end
diff --git a/spec/requests/api/graphql/project/fork_details_spec.rb b/spec/requests/api/graphql/project/fork_details_spec.rb
index efd48b00833..0baf29b970e 100644
--- a/spec/requests/api/graphql/project/fork_details_spec.rb
+++ b/spec/requests/api/graphql/project/fork_details_spec.rb
@@ -10,12 +10,13 @@ RSpec.describe 'getting project fork details', feature_category: :source_code_ma
let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
let_it_be(:forked_project) { fork_project(project, current_user, repository: true) }
+ let(:ref) { 'feature' }
let(:queried_project) { forked_project }
let(:query) do
graphql_query_for(:project,
{ full_path: queried_project.full_path }, <<~QUERY
- forkDetails(ref: "feature"){
+ forkDetails(ref: "#{ref}"){
ahead
behind
}
@@ -41,6 +42,38 @@ RSpec.describe 'getting project fork details', feature_category: :source_code_ma
end
end
+ context 'when project source is not visible' do
+ it 'does not return fork details' do
+ project.team.truncate
+
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['forkDetails']).to be_nil
+ end
+ end
+
+ context 'when the specified ref does not exist' do
+ let(:ref) { 'non-existent-branch' }
+
+ it 'does not return fork details' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['forkDetails']).to be_nil
+ end
+ end
+
+ context 'when fork_divergence_counts feature flag is disabled' do
+ before do
+ stub_feature_flags(fork_divergence_counts: false)
+ end
+
+ it 'does not return fork details' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['project']['forkDetails']).to be_nil
+ end
+ end
+
context 'when a user cannot read the code' do
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb
index 8407faa967e..156886ca211 100644
--- a/spec/requests/api/graphql/project/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/project/merge_requests_spec.rb
@@ -588,8 +588,9 @@ RSpec.describe 'getting merge request listings nested in a project', feature_cat
end
let(:query) do
+ # Adding a no-op `not` filter to mimic the same query as the frontend does
graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY)
- mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0) {
+ mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0, not: { labels: null }) {
totalTimeToMerge
count
}
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index f49165a88ea..d5dd12de63e 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -313,6 +313,34 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
end
end
+ context 'when fetching work item notifications widget' do
+ let(:fields) do
+ <<~GRAPHQL
+ nodes {
+ widgets {
+ type
+ ... on WorkItemWidgetNotifications {
+ subscribed
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'executes limited number of N+1 queries', :use_sql_query_cache do
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: current_user)
+ end
+
+ create_list(:work_item, 3, project: project)
+
+ # Performs 1 extra query per item to fetch subscriptions
+ expect { post_graphql(query, current_user: current_user) }
+ .not_to exceed_all_query_limit(control).with_threshold(3)
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
def item_ids
graphql_dig_at(items_data, :node, :id)
end
diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb
index 2b9d66ec744..d93077b1c70 100644
--- a/spec/requests/api/graphql/query_spec.rb
+++ b/spec/requests/api/graphql/query_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query', feature_category: :not_owned do
+RSpec.describe 'Query', feature_category: :shared do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/user/user_achievements_query_spec.rb b/spec/requests/api/graphql/user/user_achievements_query_spec.rb
new file mode 100644
index 00000000000..bf9b2b429cc
--- /dev/null
+++ b/spec/requests/api/graphql/user/user_achievements_query_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'UserAchievements', feature_category: :user_profile do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :private) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+ let_it_be(:user_achievements) { create_list(:user_achievement, 2, achievement: achievement, user: user) }
+ let_it_be(:fields) do
+ <<~HEREDOC
+ userAchievements {
+ nodes {
+ id
+ achievement {
+ id
+ }
+ user {
+ id
+ }
+ awardedByUser {
+ id
+ }
+ revokedByUser {
+ id
+ }
+ }
+ }
+ HEREDOC
+ end
+
+ let_it_be(:query) do
+ graphql_query_for('user', { id: user.to_global_id.to_s }, fields)
+ end
+
+ let(:current_user) { user }
+
+ before_all do
+ group.add_guest(user)
+ end
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns all user_achievements' do
+ expect(graphql_data_at(:user, :userAchievements, :nodes)).to contain_exactly(
+ a_graphql_entity_for(user_achievements[0]),
+ a_graphql_entity_for(user_achievements[1])
+ )
+ end
+
+ it 'can lookahead to eliminate N+1 queries', :use_clean_rails_memory_store_caching do
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: user)
+ end.count
+
+ achievement2 = create(:achievement, namespace: group)
+ create_list(:user_achievement, 2, achievement: achievement2, user: user)
+
+ expect { post_graphql(query, current_user: user) }.not_to exceed_all_query_limit(control_count)
+ end
+
+ context 'when the achievements feature flag is disabled for a namespace' do
+ let_it_be(:group2) { create(:group) }
+ let_it_be(:achievement2) { create(:achievement, namespace: group2) }
+ let_it_be(:user_achievement2) { create(:user_achievement, achievement: achievement2, user: user) }
+
+ before do
+ stub_feature_flags(achievements: false)
+ stub_feature_flags(achievements: group2)
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'does not return user_achievements for that namespace' do
+ expect(graphql_data_at(:user, :userAchievements, :nodes)).to contain_exactly(
+ a_graphql_entity_for(user_achievement2)
+ )
+ end
+ end
+
+ context 'when current user is not a member of the private group' do
+ let(:current_user) { create(:user) }
+
+ specify { expect(graphql_data_at(:user, :userAchievements, :nodes)).to be_empty }
+ end
+end
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index 0fad4f4ff3a..24c72a8bb00 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -373,6 +373,32 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
)
end
end
+
+ describe 'notifications widget' do
+ let(:work_item_fields) do
+ <<~GRAPHQL
+ id
+ widgets {
+ type
+ ... on WorkItemWidgetNotifications {
+ subscribed
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'returns widget information' do
+ expect(work_item_data).to include(
+ 'id' => work_item.to_gid.to_s,
+ 'widgets' => include(
+ hash_including(
+ 'type' => 'NOTIFICATIONS',
+ 'subscribed' => work_item.subscribed?(current_user, project)
+ )
+ )
+ )
+ end
+ end
end
context 'when an Issue Global ID is provided' do
diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb
index d7724371cce..8a3c5261eb6 100644
--- a/spec/requests/api/graphql_spec.rb
+++ b/spec/requests/api/graphql_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'GraphQL', feature_category: :not_owned do
+RSpec.describe 'GraphQL', feature_category: :shared do
include GraphqlHelpers
include AfterNextHelpers
diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb
index 91f64d02d43..2f05b0fcf21 100644
--- a/spec/requests/api/group_milestones_spec.rb
+++ b/spec/requests/api/group_milestones_spec.rb
@@ -4,35 +4,41 @@ require 'spec_helper'
RSpec.describe API::GroupMilestones, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
- let_it_be(:group) { create(:group, :private) }
+ let_it_be_with_refind(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, namespace: group) }
let_it_be(:group_member) { create(:group_member, group: group, user: user) }
- let_it_be(:closed_milestone) { create(:closed_milestone, group: group, title: 'version1', description: 'closed milestone') }
- let_it_be(:milestone) { create(:milestone, group: group, title: 'version2', description: 'open milestone') }
+ let_it_be(:closed_milestone) do
+ create(:closed_milestone, group: group, title: 'version1', description: 'closed milestone')
+ end
+
+ let_it_be_with_reload(:milestone) do
+ create(:milestone, group: group, title: 'version2', description: 'open milestone', updated_at: 4.days.ago)
+ end
let(:route) { "/groups/#{group.id}/milestones" }
+ shared_examples 'listing all milestones' do
+ it 'returns correct list of milestones' do
+ get api(route, user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(milestones.size)
+ expect(json_response.map { |entry| entry["id"] }).to eq(milestones.map(&:id))
+ end
+ end
+
it_behaves_like 'group and project milestones', "/groups/:id/milestones"
describe 'GET /groups/:id/milestones' do
- context 'when include_parent_milestones is true' do
- let_it_be(:ancestor_group) { create(:group, :private) }
- let_it_be(:ancestor_group_milestone) { create(:milestone, group: ancestor_group) }
- let_it_be(:params) { { include_parent_milestones: true } }
-
- before_all do
- group.update!(parent: ancestor_group)
- end
+ let_it_be(:ancestor_group) { create(:group, :private) }
+ let_it_be(:ancestor_group_milestone) { create(:milestone, group: ancestor_group, updated_at: 2.days.ago) }
- shared_examples 'listing all milestones' do
- it 'returns correct list of milestones' do
- get api(route, user), params: params
+ before_all do
+ group.update!(parent: ancestor_group)
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.size).to eq(milestones.size)
- expect(json_response.map { |entry| entry["id"] }).to eq(milestones.map(&:id))
- end
- end
+ context 'when include_parent_milestones is true' do
+ let(:params) { { include_parent_milestones: true } }
context 'when user has access to ancestor groups' do
let(:milestones) { [ancestor_group_milestone, milestone, closed_milestone] }
@@ -45,10 +51,26 @@ RSpec.describe API::GroupMilestones, feature_category: :team_planning do
it_behaves_like 'listing all milestones'
context 'when iids param is present' do
- let_it_be(:params) { { include_parent_milestones: true, iids: [milestone.iid] } }
+ let(:params) { { include_parent_milestones: true, iids: [milestone.iid] } }
it_behaves_like 'listing all milestones'
end
+
+ context 'when updated_before param is present' do
+ let(:params) { { updated_before: 1.day.ago.iso8601, include_parent_milestones: true } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [ancestor_group_milestone, milestone] }
+ end
+ end
+
+ context 'when updated_after param is present' do
+ let(:params) { { updated_after: 1.day.ago.iso8601, include_parent_milestones: true } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [closed_milestone] }
+ end
+ end
end
context 'when user has no access to ancestor groups' do
@@ -63,6 +85,22 @@ RSpec.describe API::GroupMilestones, feature_category: :team_planning do
end
end
end
+
+ context 'when updated_before param is present' do
+ let(:params) { { updated_before: 1.day.ago.iso8601 } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [milestone] }
+ end
+ end
+
+ context 'when updated_after param is present' do
+ let(:params) { { updated_after: 1.day.ago.iso8601 } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [closed_milestone] }
+ end
+ end
end
describe 'GET /groups/:id/milestones/:milestone_id/issues' do
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index e3d538d72ba..ff20e7ea9dd 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupVariables, feature_category: :pipeline_authoring do
+RSpec.describe API::GroupVariables, feature_category: :pipeline_composition do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:variable) { create(:ci_group_variable, group: group) }
diff --git a/spec/requests/api/helm_packages_spec.rb b/spec/requests/api/helm_packages_spec.rb
index 584f6e3c7d4..d6afd6f86ff 100644
--- a/spec/requests/api/helm_packages_spec.rb
+++ b/spec/requests/api/helm_packages_spec.rb
@@ -17,7 +17,15 @@ RSpec.describe API::HelmPackages, feature_category: :package_registry do
let_it_be(:package_file2_2) { create(:helm_package_file, package: package2, file_sha256: 'file2', file_name: 'filename2.tgz', channel: 'test', description: 'hello from test channel') }
let_it_be(:other_package) { create(:npm_package, project: project) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_helm_user' } }
+ let(:snowplow_gitlab_standard_context) { snowplow_context }
+
+ def snowplow_context(user_role: :developer)
+ if user_role == :anonymous
+ { project: project, namespace: project.namespace, property: 'i_package_helm_user' }
+ else
+ { project: project, namespace: project.namespace, property: 'i_package_helm_user', user: user }
+ end
+ end
describe 'GET /api/v4/projects/:id/packages/helm/:channel/index.yaml' do
let(:project_id) { project.id }
@@ -65,6 +73,7 @@ RSpec.describe API::HelmPackages, feature_category: :package_registry do
with_them do
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
before do
project.update!(visibility: visibility.to_s)
@@ -75,6 +84,8 @@ RSpec.describe API::HelmPackages, feature_category: :package_registry do
end
context 'with access to package registry for everyone' do
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: :anonymous) }
+
before do
project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE)
project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
@@ -116,6 +127,7 @@ RSpec.describe API::HelmPackages, feature_category: :package_registry do
with_them do
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) }
let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
@@ -178,6 +190,7 @@ RSpec.describe API::HelmPackages, feature_category: :package_registry do
with_them do
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, personal_access_token.token) }
let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 38275ce0057..0be9df41e8f 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require 'raven/transports/dummy'
require_relative '../../../config/initializers/sentry'
-RSpec.describe API::Helpers, :enable_admin_mode, feature_category: :authentication_and_authorization do
+RSpec.describe API::Helpers, :enable_admin_mode, feature_category: :system_access do
include API::APIGuard::HelperMethods
include described_class
include TermsHelper
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index ca32271f573..dff41c4c477 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Base, feature_category: :authentication_and_authorization do
+RSpec.describe API::Internal::Base, feature_category: :system_access do
include GitlabShellHelpers
include APIInternalBaseHelpers
@@ -514,7 +514,7 @@ RSpec.describe API::Internal::Base, feature_category: :authentication_and_author
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(json_response["gl_key_type"]).to eq("key")
expect(json_response["gl_key_id"]).to eq(key.id)
- expect(user.reload.last_activity_on).to be_nil
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
it_behaves_like 'sets hook env' do
@@ -553,7 +553,7 @@ RSpec.describe API::Internal::Base, feature_category: :authentication_and_author
expect(json_response["status"]).to be_truthy
expect(json_response["gl_project_path"]).to eq(personal_snippet.repository.full_path)
expect(json_response["gl_repository"]).to eq("snippet-#{personal_snippet.id}")
- expect(user.reload.last_activity_on).to be_nil
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
it_behaves_like 'sets hook env' do
@@ -585,7 +585,7 @@ RSpec.describe API::Internal::Base, feature_category: :authentication_and_author
expect(json_response["status"]).to be_truthy
expect(json_response["gl_project_path"]).to eq(project_snippet.repository.full_path)
expect(json_response["gl_repository"]).to eq("snippet-#{project_snippet.id}")
- expect(user.reload.last_activity_on).to be_nil
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
it_behaves_like 'sets hook env' do
@@ -703,7 +703,7 @@ RSpec.describe API::Internal::Base, feature_category: :authentication_and_author
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(user.reload.last_activity_on).to be_nil
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
it_behaves_like 'rate limited request' do
@@ -862,7 +862,7 @@ RSpec.describe API::Internal::Base, feature_category: :authentication_and_author
expect(json_response['status']).to be_truthy
expect(json_response['payload']).to eql(payload)
expect(json_response['gl_console_messages']).to eql(console_messages)
- expect(user.reload.last_activity_on).to be_nil
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
end
end
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index be76e55269a..547b9071f94 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -306,4 +306,151 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :kubernetes_manageme
end
end
end
+
+ describe 'POST /internal/kubernetes/authorize_proxy_user', :clean_gitlab_redis_sessions do
+ include SessionHelpers
+
+ def send_request(headers: {}, params: {})
+ post api('/internal/kubernetes/authorize_proxy_user'), params: params, headers: headers.reverse_merge(jwt_auth_headers)
+ end
+
+ def stub_user_session(user, csrf_token)
+ stub_session(
+ {
+ 'warden.user.user.key' => [[user.id], user.authenticatable_salt],
+ '_csrf_token' => csrf_token
+ }
+ )
+ end
+
+ def stub_user_session_with_no_user_id(user, csrf_token)
+ stub_session(
+ {
+ 'warden.user.user.key' => [[nil], user.authenticatable_salt],
+ '_csrf_token' => csrf_token
+ }
+ )
+ end
+
+ def mask_token(encoded_token)
+ controller = ActionController::Base.new
+ raw_token = controller.send(:decode_csrf_token, encoded_token)
+ controller.send(:mask_token, raw_token)
+ end
+
+ def new_token
+ ActionController::Base.new.send(:generate_csrf_token)
+ end
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user_access_config) do
+ {
+ 'user_access' => {
+ 'access_as' => { 'agent' => {} },
+ 'projects' => [{ 'id' => project.full_path }],
+ 'groups' => [{ 'id' => group.full_path }]
+ }
+ }
+ end
+
+ let_it_be(:configuration_project) do
+ create(
+ :project, :custom_repo,
+ files: {
+ ".gitlab/agents/the-agent/config.yaml" => user_access_config.to_yaml
+ }
+ )
+ end
+
+ let_it_be(:agent) { create(:cluster_agent, name: 'the-agent', project: configuration_project) }
+ let_it_be(:another_agent) { create(:cluster_agent) }
+
+ let(:user) { create(:user) }
+
+ before do
+ allow(::Gitlab::Kas).to receive(:enabled?).and_return true
+ end
+
+ it 'returns 400 when cookie is invalid' do
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: '123', csrf_token: mask_token(new_token) })
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it 'returns 401 when session is not found' do
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id('abc')
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(new_token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 401 when CSRF token does not match' do
+ public_id = stub_user_session(user, new_token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(new_token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 404 for non-existent agent' do
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: non_existing_record_id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns 403 when user has no access' do
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 200 when user has access' do
+ project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it 'returns 401 when user has valid KAS cookie and CSRF token but has no access to requested agent' do
+ project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: another_agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 401 when global flag is disabled' do
+ stub_feature_flags(kas_user_access: false)
+
+ project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 401 when user id is not found in session' do
+ project.add_member(user, :developer)
+ token = new_token
+ public_id = stub_user_session_with_no_user_id(user, token)
+ access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
+ send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
end
diff --git a/spec/requests/api/internal/pages_spec.rb b/spec/requests/api/internal/pages_spec.rb
index 56f1089843b..20fb9100ebb 100644
--- a/spec/requests/api/internal/pages_spec.rb
+++ b/spec/requests/api/internal/pages_spec.rb
@@ -3,193 +3,97 @@
require 'spec_helper'
RSpec.describe API::Internal::Pages, feature_category: :pages do
- let(:auth_headers) do
- jwt_token = JWT.encode({ 'iss' => 'gitlab-pages' }, Gitlab::Pages.secret, 'HS256')
- { Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => jwt_token }
+ let_it_be(:group) { create(:group, name: 'mygroup') }
+ let_it_be_with_reload(:project) { create(:project, name: 'myproject', group: group) }
+
+ let(:auth_header) do
+ {
+ Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => JWT.encode(
+ { 'iss' => 'gitlab-pages' },
+ Gitlab::Pages.secret, 'HS256')
+ }
end
- let(:pages_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
-
before do
- allow(Gitlab::Pages).to receive(:secret).and_return(pages_secret)
+ allow(Gitlab::Pages)
+ .to receive(:secret)
+ .and_return(SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH))
+
stub_pages_object_storage(::Pages::DeploymentUploader)
end
- describe "GET /internal/pages/status" do
- def query_enabled(headers = {})
- get api("/internal/pages/status"), headers: headers
- end
-
+ describe 'GET /internal/pages/status' do
it 'responds with 401 Unauthorized' do
- query_enabled
+ get api('/internal/pages/status')
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'responds with 204 no content' do
- query_enabled(auth_headers)
+ get api('/internal/pages/status'), headers: auth_header
expect(response).to have_gitlab_http_status(:no_content)
expect(response.body).to be_empty
end
end
- describe "GET /internal/pages" do
- def query_host(host, headers = {})
- get api("/internal/pages"), headers: headers, params: { host: host }
- end
-
- around do |example|
- freeze_time do
- example.run
- end
- end
-
- context 'not authenticated' do
+ describe 'GET /internal/pages' do
+ context 'when not authenticated' do
it 'responds with 401 Unauthorized' do
- query_host('pages.gitlab.io')
+ get api('/internal/pages')
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
- context 'authenticated' do
- def query_host(host)
- jwt_token = JWT.encode({ 'iss' => 'gitlab-pages' }, Gitlab::Pages.secret, 'HS256')
- headers = { Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => jwt_token }
-
- super(host, headers)
+ context 'when authenticated' do
+ before do
+ project.update_pages_deployment!(create(:pages_deployment, project: project))
end
- def deploy_pages(project)
- deployment = create(:pages_deployment, project: project)
- project.mark_pages_as_deployed
- project.update_pages_deployment!(deployment)
+ around do |example|
+ freeze_time do
+ example.run
+ end
end
- context 'domain does not exist' do
+ context 'when domain does not exist' do
it 'responds with 204 no content' do
- query_host('pages.gitlab.io')
+ get api('/internal/pages'), headers: auth_header, params: { host: 'any-domain.gitlab.io' }
expect(response).to have_gitlab_http_status(:no_content)
expect(response.body).to be_empty
end
end
- context 'serverless domain' do
- let(:namespace) { create(:namespace, name: 'gitlab-org') }
- let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
- let(:environment) { create(:environment, project: project) }
- let(:pages_domain) { create(:pages_domain, domain: 'serverless.gitlab.io') }
- let(:knative_without_ingress) { create(:clusters_applications_knative) }
- let(:knative_with_ingress) { create(:clusters_applications_knative, external_ip: '10.0.0.1') }
-
- context 'without a knative ingress gateway IP' do
- let!(:serverless_domain_cluster) do
- create(
- :serverless_domain_cluster,
- uuid: 'abcdef12345678',
- pages_domain: pages_domain,
- knative: knative_without_ingress
- )
- end
-
- let(:serverless_domain) do
- create(
- :serverless_domain,
- serverless_domain_cluster: serverless_domain_cluster,
- environment: environment
- )
- end
-
- it 'responds with 204 no content' do
- query_host(serverless_domain.uri.host)
-
- expect(response).to have_gitlab_http_status(:no_content)
- expect(response.body).to be_empty
- end
- end
-
- context 'with a knative ingress gateway IP' do
- let!(:serverless_domain_cluster) do
- create(
- :serverless_domain_cluster,
- uuid: 'abcdef12345678',
- pages_domain: pages_domain,
- knative: knative_with_ingress
- )
- end
-
- let(:serverless_domain) do
- create(
- :serverless_domain,
- serverless_domain_cluster: serverless_domain_cluster,
- environment: environment
- )
- end
-
- it 'responds with 204 because of feature deprecation' do
- query_host(serverless_domain.uri.host)
+ context 'when querying a custom domain' do
+ let_it_be(:pages_domain) { create(:pages_domain, domain: 'pages.io', project: project) }
- expect(response).to have_gitlab_http_status(:no_content)
- expect(response.body).to be_empty
-
- ##
- # Serverless serving and reverse proxy to Kubernetes / Knative has
- # been deprecated and disabled, as per
- # https://gitlab.com/gitlab-org/gitlab-pages/-/issues/467
- #
- # expect(response).to match_response_schema('internal/serverless/virtual_domain')
- # expect(json_response['certificate']).to eq(pages_domain.certificate)
- # expect(json_response['key']).to eq(pages_domain.key)
- #
- # expect(json_response['lookup_paths']).to eq(
- # [
- # {
- # 'source' => {
- # 'type' => 'serverless',
- # 'service' => "test-function.#{project.name}-#{project.id}-#{environment.slug}.#{serverless_domain_cluster.knative.hostname}",
- # 'cluster' => {
- # 'hostname' => serverless_domain_cluster.knative.hostname,
- # 'address' => serverless_domain_cluster.knative.external_ip,
- # 'port' => 443,
- # 'cert' => serverless_domain_cluster.certificate,
- # 'key' => serverless_domain_cluster.key
- # }
- # }
- # }
- # ]
- # )
+ context 'when there are no pages deployed for the related project' do
+ before do
+ project.mark_pages_as_not_deployed
end
- end
- end
- context 'custom domain' do
- let(:namespace) { create(:namespace, name: 'gitlab-org') }
- let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
- let!(:pages_domain) { create(:pages_domain, domain: 'pages.io', project: project) }
-
- context 'when there are no pages deployed for the related project' do
it 'responds with 204 No Content' do
- query_host('pages.io')
+ get api('/internal/pages'), headers: auth_header, params: { host: 'pages.io' }
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'when there are pages deployed for the related project' do
- it 'domain lookup is case insensitive' do
- deploy_pages(project)
+ before do
+ project.mark_pages_as_deployed
+ end
- query_host('Pages.IO')
+ it 'domain lookup is case insensitive' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'Pages.IO' }
expect(response).to have_gitlab_http_status(:ok)
end
it 'responds with the correct domain configuration' do
- deploy_pages(project)
-
- query_host('pages.io')
+ get api('/internal/pages'), headers: auth_header, params: { host: 'pages.io' }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
@@ -212,7 +116,8 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do
'sha256' => deployment.file_sha256,
'file_size' => deployment.size,
'file_count' => deployment.file_count
- }
+ },
+ 'unique_domain' => nil
}
]
)
@@ -220,20 +125,67 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do
end
end
- context 'namespaced domain' do
- let(:group) { create(:group, name: 'mygroup') }
+ context 'when querying a unique domain' do
+ before_all do
+ project.project_setting.update!(
+ pages_unique_domain: 'unique-domain',
+ pages_unique_domain_enabled: true
+ )
+ end
- before do
- allow(Settings.pages).to receive(:host).and_return('gitlab-pages.io')
- allow(Gitlab.config.pages).to receive(:url).and_return("http://gitlab-pages.io")
+ context 'when there are no pages deployed for the related project' do
+ before do
+ project.mark_pages_as_not_deployed
+ end
+
+ it 'responds with 204 No Content' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'unique-domain.example.com' }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
end
- context 'regular project' do
- it 'responds with the correct domain configuration' do
- project = create(:project, group: group, name: 'myproject')
- deploy_pages(project)
+ context 'when there are pages deployed for the related project' do
+ before do
+ project.mark_pages_as_deployed
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(pages_unique_domain: false)
+ end
- query_host('mygroup.gitlab-pages.io')
+ context 'when there are no pages deployed for the related project' do
+ it 'responds with 204 No Content' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'unique-domain.example.com' }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+ end
+
+ context 'when the unique domain is disabled' do
+ before do
+ project.project_setting.update!(pages_unique_domain_enabled: false)
+ end
+
+ context 'when there are no pages deployed for the related project' do
+ it 'responds with 204 No Content' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'unique-domain.example.com' }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+ end
+
+ it 'domain lookup is case insensitive' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'Unique-Domain.example.com' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'responds with the correct domain configuration' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'unique-domain.example.com' }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
@@ -245,7 +197,7 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
- 'prefix' => '/myproject/',
+ 'prefix' => '/',
'source' => {
'type' => 'zip',
'path' => deployment.file.url(expire_at: 1.day.from_now),
@@ -253,56 +205,116 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do
'sha256' => deployment.file_sha256,
'file_size' => deployment.size,
'file_count' => deployment.file_count
- }
+ },
+ 'unique_domain' => 'unique-domain'
}
]
)
end
end
+ end
- it 'avoids N+1 queries' do
- project = create(:project, group: group)
- deploy_pages(project)
-
- control = ActiveRecord::QueryRecorder.new { query_host('mygroup.gitlab-pages.io') }
+ context 'when querying a namespaced domain' do
+ before do
+ allow(Settings.pages).to receive(:host).and_return('gitlab-pages.io')
+ allow(Gitlab.config.pages).to receive(:url).and_return("http://gitlab-pages.io")
+ end
- 3.times do
- project = create(:project, group: group)
- deploy_pages(project)
+ context 'when there are no pages deployed for the related project' do
+ before do
+ project.mark_pages_as_not_deployed
end
- expect { query_host('mygroup.gitlab-pages.io') }.not_to exceed_query_limit(control)
+ it 'responds with 204 No Content' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'mygroup.gitlab-pages.io' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('internal/pages/virtual_domain')
+ expect(json_response['lookup_paths']).to eq([])
+ end
end
- context 'group root project' do
- it 'responds with the correct domain configuration' do
- project = create(:project, group: group, name: 'mygroup.gitlab-pages.io')
- deploy_pages(project)
+ context 'when there are pages deployed for the related project' do
+ before do
+ project.mark_pages_as_deployed
+ end
- query_host('mygroup.gitlab-pages.io')
+ context 'with a regular project' do
+ it 'responds with the correct domain configuration' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'mygroup.gitlab-pages.io' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('internal/pages/virtual_domain')
+
+ deployment = project.pages_metadatum.pages_deployment
+ expect(json_response['lookup_paths']).to eq(
+ [
+ {
+ 'project_id' => project.id,
+ 'access_control' => false,
+ 'https_only' => false,
+ 'prefix' => '/myproject/',
+ 'source' => {
+ 'type' => 'zip',
+ 'path' => deployment.file.url(expire_at: 1.day.from_now),
+ 'global_id' => "gid://gitlab/PagesDeployment/#{deployment.id}",
+ 'sha256' => deployment.file_sha256,
+ 'file_size' => deployment.size,
+ 'file_count' => deployment.file_count
+ },
+ 'unique_domain' => nil
+ }
+ ]
+ )
+ end
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('internal/pages/virtual_domain')
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'mygroup.gitlab-pages.io' }
+ end
- deployment = project.pages_metadatum.pages_deployment
- expect(json_response['lookup_paths']).to eq(
- [
- {
- 'project_id' => project.id,
- 'access_control' => false,
- 'https_only' => false,
- 'prefix' => '/',
- 'source' => {
- 'type' => 'zip',
- 'path' => deployment.file.url(expire_at: 1.day.from_now),
- 'global_id' => "gid://gitlab/PagesDeployment/#{deployment.id}",
- 'sha256' => deployment.file_sha256,
- 'file_size' => deployment.size,
- 'file_count' => deployment.file_count
+ 3.times do
+ project = create(:project, group: group)
+ project.mark_pages_as_deployed
+ end
+
+ expect { get api('/internal/pages'), headers: auth_header, params: { host: 'mygroup.gitlab-pages.io' } }
+ .not_to exceed_query_limit(control)
+ end
+
+ context 'with a group root project' do
+ before do
+ project.update!(path: 'mygroup.gitlab-pages.io')
+ end
+
+ it 'responds with the correct domain configuration' do
+ get api('/internal/pages'), headers: auth_header, params: { host: 'mygroup.gitlab-pages.io' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('internal/pages/virtual_domain')
+
+ deployment = project.pages_metadatum.pages_deployment
+ expect(json_response['lookup_paths']).to eq(
+ [
+ {
+ 'project_id' => project.id,
+ 'access_control' => false,
+ 'https_only' => false,
+ 'prefix' => '/',
+ 'source' => {
+ 'type' => 'zip',
+ 'path' => deployment.file.url(expire_at: 1.day.from_now),
+ 'global_id' => "gid://gitlab/PagesDeployment/#{deployment.id}",
+ 'sha256' => deployment.file_sha256,
+ 'file_size' => deployment.size,
+ 'file_count' => deployment.file_count
+ },
+ 'unique_domain' => nil
}
- }
- ]
- )
+ ]
+ )
+ end
end
end
end
diff --git a/spec/requests/api/internal/workhorse_spec.rb b/spec/requests/api/internal/workhorse_spec.rb
index 99d0ecabbb7..2657abffae6 100644
--- a/spec/requests/api/internal/workhorse_spec.rb
+++ b/spec/requests/api/internal/workhorse_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Internal::Workhorse, :allow_forgery_protection, feature_category: :not_owned do
+RSpec.describe API::Internal::Workhorse, :allow_forgery_protection, feature_category: :shared do
include WorkhorseHelpers
context '/authorize_upload' do
diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb
index 0641c2135c1..eaa3c46d0ca 100644
--- a/spec/requests/api/issues/get_group_issues_spec.rb
+++ b/spec/requests/api/issues/get_group_issues_spec.rb
@@ -74,7 +74,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let(:base_url) { "/groups/#{group.id}/issues" }
shared_examples 'group issues statistics' do
- it 'returns issues statistics' do
+ it 'returns issues statistics', :aggregate_failures do
get api("/groups/#{group.id}/issues_statistics", user), params: params
expect(response).to have_gitlab_http_status(:ok)
@@ -346,7 +346,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
group_project.add_reporter(user)
end
- it 'exposes known attributes' do
+ it 'exposes known attributes', :aggregate_failures do
get api(base_url, admin)
expect(response).to have_gitlab_http_status(:ok)
@@ -355,7 +355,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
it 'returns all group issues (including opened and closed)' do
- get api(base_url, admin)
+ get api(base_url, admin, admin_mode: true)
expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id])
end
@@ -385,7 +385,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
it 'returns group confidential issues for admin' do
- get api(base_url, admin), params: { state: :opened }
+ get api(base_url, admin, admin_mode: true), params: { state: :opened }
expect_paginated_array_response([group_confidential_issue.id, group_issue.id])
end
@@ -403,7 +403,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'labels parameter' do
- it 'returns an array of labeled group issues' do
+ it 'returns an array of labeled group issues', :aggregate_failures do
get api(base_url, user), params: { labels: group_label.title }
expect_paginated_array_response(group_issue.id)
@@ -486,7 +486,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'returns an array of issues found by iids' do
+ it 'returns an array of issues found by iids', :aggregate_failures do
get api(base_url, user), params: { iids: [group_issue.iid] }
expect_paginated_array_response(group_issue.id)
@@ -505,14 +505,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect_paginated_array_response([])
end
- it 'returns an array of group issues with any label' do
+ it 'returns an array of group issues with any label', :aggregate_failures do
get api(base_url, user), params: { labels: IssuableFinder::Params::FILTER_ANY }
expect_paginated_array_response(group_issue.id)
expect(json_response.first['id']).to eq(group_issue.id)
end
- it 'returns an array of group issues with any label with labels param as array' do
+ it 'returns an array of group issues with any label with labels param as array', :aggregate_failures do
get api(base_url, user), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
expect_paginated_array_response(group_issue.id)
@@ -555,7 +555,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect_paginated_array_response(group_closed_issue.id)
end
- it 'returns an array of issues with no milestone' do
+ it 'returns an array of issues with no milestone', :aggregate_failures do
get api(base_url, user), params: { milestone: no_milestone_title }
expect(response).to have_gitlab_http_status(:ok)
@@ -688,28 +688,28 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:issue2) { create(:issue, author: user2, project: group_project, created_at: 2.days.ago) }
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: group_project, created_at: 1.day.ago) }
- it 'returns issues with by assignee_username' do
+ it 'returns issues with by assignee_username', :aggregate_failures do
get api(base_url, user), params: { assignee_username: [assignee.username], scope: 'all' }
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
expect_paginated_array_response([issue3.id, group_confidential_issue.id])
end
- it 'returns issues by assignee_username as string' do
+ it 'returns issues by assignee_username as string', :aggregate_failures do
get api(base_url, user), params: { assignee_username: assignee.username, scope: 'all' }
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
expect_paginated_array_response([issue3.id, group_confidential_issue.id])
end
- it 'returns error when multiple assignees are passed' do
+ it 'returns error when multiple assignees are passed', :aggregate_failures do
get api(base_url, user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["error"]).to include("allows one value, but found 2")
end
- it 'returns error when assignee_username and assignee_id are passed together' do
+ it 'returns error when assignee_username and assignee_id are passed together', :aggregate_failures do
get api(base_url, user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -719,7 +719,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe "#to_reference" do
- it 'exposes reference path in context of group' do
+ it 'exposes reference path in context of group', :aggregate_failures do
get api(base_url, user)
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
@@ -735,7 +735,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
group_closed_issue.reload
end
- it 'exposes reference path in context of parent group' do
+ it 'exposes reference path in context of parent group', :aggregate_failures do
get api("/groups/#{parent_group.id}/issues")
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index 6fc3903103b..915b8fff75e 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -99,7 +99,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
shared_examples 'project issues statistics' do
- it 'returns project issues statistics' do
+ it 'returns project issues statistics', :aggregate_failures do
get api("/projects/#{project.id}/issues_statistics", current_user), params: params
expect(response).to have_gitlab_http_status(:ok)
@@ -317,7 +317,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
it 'returns project confidential issues for admin' do
- get api("#{base_url}/issues", admin)
+ get api("#{base_url}/issues", admin, admin_mode: true)
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
end
@@ -526,7 +526,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect_paginated_array_response([closed_issue.id, confidential_issue.id, issue.id])
end
- it 'exposes known attributes' do
+ it 'exposes known attributes', :aggregate_failures do
get api("#{base_url}/issues", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -607,28 +607,28 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:issue2) { create(:issue, author: user2, project: project, created_at: 2.days.ago) }
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, created_at: 1.day.ago) }
- it 'returns issues by assignee_username' do
+ it 'returns issues by assignee_username', :aggregate_failures do
get api("/issues", user), params: { assignee_username: [assignee.username], scope: 'all' }
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
expect_paginated_array_response([confidential_issue.id, issue3.id])
end
- it 'returns issues by assignee_username as string' do
+ it 'returns issues by assignee_username as string', :aggregate_failures do
get api("/issues", user), params: { assignee_username: assignee.username, scope: 'all' }
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
expect_paginated_array_response([confidential_issue.id, issue3.id])
end
- it 'returns error when multiple assignees are passed' do
+ it 'returns error when multiple assignees are passed', :aggregate_failures do
get api("/issues", user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["error"]).to include("allows one value, but found 2")
end
- it 'returns error when assignee_username and assignee_id are passed together' do
+ it 'returns error when assignee_username and assignee_id are passed together', :aggregate_failures do
get api("/issues", user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -646,7 +646,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'exposes known attributes' do
+ it 'exposes known attributes', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -686,7 +686,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'exposes the closed_at attribute' do
+ it 'exposes the closed_at attribute', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{closed_issue.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -694,7 +694,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'links exposure' do
- it 'exposes related resources full URIs' do
+ it 'exposes related resources full URIs', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
links = json_response['_links']
@@ -706,7 +706,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'returns a project issue by internal id' do
+ it 'returns a project issue by internal id', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -738,7 +738,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:not_found)
end
- it 'returns confidential issue for project members' do
+ it 'returns confidential issue for project members', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -746,7 +746,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['iid']).to eq(confidential_issue.iid)
end
- it 'returns confidential issue for author' do
+ it 'returns confidential issue for author', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", author)
expect(response).to have_gitlab_http_status(:ok)
@@ -754,7 +754,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['iid']).to eq(confidential_issue.iid)
end
- it 'returns confidential issue for assignee' do
+ it 'returns confidential issue for assignee', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", assignee)
expect(response).to have_gitlab_http_status(:ok)
@@ -762,8 +762,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['iid']).to eq(confidential_issue.iid)
end
- it 'returns confidential issue for admin' do
- get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", admin)
+ it 'returns confidential issue for admin', :aggregate_failures do
+ get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['title']).to eq(confidential_issue.title)
@@ -829,7 +829,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:related_mr) { create_referencing_mr(user, project, issue) }
context 'when unauthenticated' do
- it 'return list of referenced merge requests from issue' do
+ it 'return list of referenced merge requests from issue', :aggregate_failures do
get_related_merge_requests(project.id, issue.iid)
expect_paginated_array_response(related_mr.id)
@@ -898,8 +898,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'exposes known attributes' do
- get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", admin)
+ it 'exposes known attributes', :aggregate_failures do
+ get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
@@ -936,7 +936,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
)
end
- it 'returns a full list of participants' do
+ it 'returns a full list of participants', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue.iid}/participants", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -945,7 +945,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when user cannot see a confidential note' do
- it 'returns a limited list of participants' do
+ it 'returns a limited list of participants', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue.iid}/participants", create(:user))
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb
index 4b60eaadcbc..33f49cefc69 100644
--- a/spec/requests/api/issues/issues_spec.rb
+++ b/spec/requests/api/issues/issues_spec.rb
@@ -78,7 +78,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
shared_examples 'issues statistics' do
- it 'returns issues statistics' do
+ it 'returns issues statistics', :aggregate_failures do
get api("/issues_statistics", user), params: params
expect(response).to have_gitlab_http_status(:ok)
@@ -109,8 +109,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'as an admin' do
context 'when issue exists' do
- it 'returns the issue' do
- get api("/issues/#{issue.id}", admin)
+ it 'returns the issue', :aggregate_failures do
+ get api("/issues/#{issue.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.dig('author', 'id')).to eq(issue.author.id)
@@ -121,7 +121,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'when issue does not exist' do
it 'returns 404' do
- get api("/issues/0", admin)
+ get api("/issues/0", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -132,7 +132,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
describe 'GET /issues' do
context 'when unauthenticated' do
- it 'returns an array of all issues' do
+ it 'returns an array of all issues', :aggregate_failures do
get api('/issues'), params: { scope: 'all' }
expect(response).to have_gitlab_http_status(:ok)
@@ -162,14 +162,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:unauthorized)
end
- it 'returns an array of issues matching state in milestone' do
+ it 'returns an array of issues matching state in milestone', :aggregate_failures do
get api('/issues'), params: { milestone: 'foo', scope: 'all' }
expect(response).to have_gitlab_http_status(:ok)
expect_paginated_array_response([])
end
- it 'returns an array of issues matching state in milestone' do
+ it 'returns an array of issues matching state in milestone', :aggregate_failures do
get api('/issues'), params: { milestone: milestone.title, scope: 'all' }
expect(response).to have_gitlab_http_status(:ok)
@@ -273,7 +273,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when authenticated' do
- it 'returns an array of issues' do
+ it 'returns an array of issues', :aggregate_failures do
get api('/issues', user)
expect_paginated_array_response([issue.id, closed_issue.id])
@@ -532,7 +532,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'with incident issues' do
let_it_be(:incident) { create(:incident, project: project) }
- it 'avoids N+1 queries' do
+ it 'avoids N+1 queries', :aggregate_failures do
get api('/issues', user) # warm up
control = ActiveRecord::QueryRecorder.new do
@@ -553,7 +553,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'with issues closed as duplicates' do
let_it_be(:dup_issue_1) { create(:issue, :closed_as_duplicate, project: project) }
- it 'avoids N+1 queries' do
+ it 'avoids N+1 queries', :aggregate_failures do
get api('/issues', user) # warm up
control = ActiveRecord::QueryRecorder.new do
@@ -639,7 +639,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect_paginated_array_response([])
end
- it 'returns an array of labeled issues matching given state' do
+ it 'returns an array of labeled issues matching given state', :aggregate_failures do
get api('/issues', user), params: { labels: label.title, state: :opened }
expect_paginated_array_response(issue.id)
@@ -647,7 +647,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response.first['state']).to eq('opened')
end
- it 'returns an array of labeled issues matching given state with labels param as array' do
+ it 'returns an array of labeled issues matching given state with labels param as array', :aggregate_failures do
get api('/issues', user), params: { labels: [label.title], state: :opened }
expect_paginated_array_response(issue.id)
@@ -917,14 +917,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'matches V4 response schema' do
+ it 'matches V4 response schema', :aggregate_failures do
get api('/issues', user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/issues')
end
- it 'returns a related merge request count of 0 if there are no related merge requests' do
+ it 'returns a related merge request count of 0 if there are no related merge requests', :aggregate_failures do
get api('/issues', user)
expect(response).to have_gitlab_http_status(:ok)
@@ -932,7 +932,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response.first).to include('merge_requests_count' => 0)
end
- it 'returns a related merge request count > 0 if there are related merge requests' do
+ it 'returns a related merge request count > 0 if there are related merge requests', :aggregate_failures do
create(:merge_requests_closing_issues, issue: issue)
get api('/issues', user)
@@ -1013,28 +1013,28 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:issue2) { create(:issue, author: user2, project: project, created_at: 2.days.ago) }
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, created_at: 1.day.ago) }
- it 'returns issues with by assignee_username' do
+ it 'returns issues with by assignee_username', :aggregate_failures do
get api("/issues", user), params: { assignee_username: [assignee.username], scope: 'all' }
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
expect_paginated_array_response([confidential_issue.id, issue3.id])
end
- it 'returns issues by assignee_username as string' do
+ it 'returns issues by assignee_username as string', :aggregate_failures do
get api("/issues", user), params: { assignee_username: assignee.username, scope: 'all' }
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
expect_paginated_array_response([confidential_issue.id, issue3.id])
end
- it 'returns error when multiple assignees are passed' do
+ it 'returns error when multiple assignees are passed', :aggregate_failures do
get api("/issues", user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["error"]).to include("allows one value, but found 2")
end
- it 'returns error when assignee_username and assignee_id are passed together' do
+ it 'returns error when assignee_username and assignee_id are passed together', :aggregate_failures do
get api("/issues", user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -1088,7 +1088,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe 'GET /projects/:id/issues/:issue_iid' do
- it 'exposes full reference path' do
+ it 'exposes full reference path', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -1106,7 +1106,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'user does not have permission to view new issue' do
- it 'does not return the issue as closed_as_duplicate_of' do
+ it 'does not return the issue as closed_as_duplicate_of', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue_closed_as_dup.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -1119,7 +1119,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
new_issue.project.add_guest(user)
end
- it 'returns the issue as closed_as_duplicate_of' do
+ it 'returns the issue as closed_as_duplicate_of', :aggregate_failures do
get api("/projects/#{project.id}/issues/#{issue_closed_as_dup.iid}", user)
expect(response).to have_gitlab_http_status(:ok)
@@ -1131,7 +1131,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe "POST /projects/:id/issues" do
- it 'creates a new project issue' do
+ it 'creates a new project issue', :aggregate_failures do
post api("/projects/#{project.id}/issues", user), params: { title: 'new issue' }
expect(response).to have_gitlab_http_status(:created)
@@ -1139,6 +1139,15 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['issue_type']).to eq('issue')
end
+ context 'when confidential is null' do
+ it 'responds with 400 error', :aggregate_failures do
+ post api("/projects/#{project.id}/issues", user), params: { title: 'issue', confidential: nil }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('confidential is empty')
+ end
+ end
+
context 'when issue create service returns an unrecoverable error' do
before do
allow_next_instance_of(Issues::CreateService) do |create_service|
@@ -1146,7 +1155,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'returns and error message and status code from the service' do
+ it 'returns and error message and status code from the service', :aggregate_failures do
post api("/projects/#{project.id}/issues", user), params: { title: 'new issue' }
expect(response).to have_gitlab_http_status(:forbidden)
@@ -1168,15 +1177,15 @@ RSpec.describe API::Issues, feature_category: :team_planning do
travel_to fixed_time
end
- it 'allows admins to set the timestamp' do
- put api("/projects/#{project.id}/issues/#{issue.iid}", admin), params: { labels: 'label1', updated_at: updated_at }
+ it 'allows admins to set the timestamp', :aggregate_failures do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", admin, admin_mode: true), params: { labels: 'label1', updated_at: updated_at }
expect(response).to have_gitlab_http_status(:ok)
expect(Time.parse(json_response['updated_at'])).to be_like_time(updated_at)
expect(ResourceLabelEvent.last.created_at).to be_like_time(updated_at)
end
- it 'does not allow other users to set the timestamp' do
+ it 'does not allow other users to set the timestamp', :aggregate_failures do
reporter = create(:user)
project.add_developer(reporter)
@@ -1259,7 +1268,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'with valid params' do
- it 'reorders issues and returns a successful 200 response' do
+ it 'reorders issues and returns a successful 200 response', :aggregate_failures do
put api("/projects/#{project.id}/issues/#{issue1.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
expect(response).to have_gitlab_http_status(:ok)
@@ -1286,7 +1295,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let(:other_project) { create(:project, group: group) }
let(:other_issue) { create(:issue, project: other_project, relative_position: 80) }
- it 'reorders issues and returns a successful 200 response' do
+ it 'reorders issues and returns a successful 200 response', :aggregate_failures do
put api("/projects/#{other_project.id}/issues/#{other_issue.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index 265091fa698..a17c1389e83 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -75,7 +75,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
describe 'POST /projects/:id/issues' do
context 'support for deprecated assignee_id' do
- it 'creates a new project issue' do
+ it 'creates a new project issue', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', assignee_id: user2.id }
@@ -85,7 +85,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['assignees'].first['name']).to eq(user2.name)
end
- it 'creates a new project issue when assignee_id is empty' do
+ it 'creates a new project issue when assignee_id is empty', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', assignee_id: '' }
@@ -96,7 +96,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'single assignee restrictions' do
- it 'creates a new project issue with no more than one assignee' do
+ it 'creates a new project issue with no more than one assignee', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', assignee_ids: [user2.id, guest.id] }
@@ -122,8 +122,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'an internal ID is provided' do
context 'by an admin' do
- it 'sets the internal ID on the new issue' do
- post api("/projects/#{project.id}/issues", admin),
+ it 'sets the internal ID on the new issue', :aggregate_failures do
+ post api("/projects/#{project.id}/issues", admin, admin_mode: true),
params: { title: 'new issue', iid: 9001 }
expect(response).to have_gitlab_http_status(:created)
@@ -132,7 +132,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'by an owner' do
- it 'sets the internal ID on the new issue' do
+ it 'sets the internal ID on the new issue', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', iid: 9001 }
@@ -145,7 +145,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let(:group) { create(:group) }
let(:group_project) { create(:project, :public, namespace: group) }
- it 'sets the internal ID on the new issue' do
+ it 'sets the internal ID on the new issue', :aggregate_failures do
group.add_owner(user2)
post api("/projects/#{group_project.id}/issues", user2),
params: { title: 'new issue', iid: 9001 }
@@ -156,7 +156,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'by another user' do
- it 'ignores the given internal ID' do
+ it 'ignores the given internal ID', :aggregate_failures do
post api("/projects/#{project.id}/issues", user2),
params: { title: 'new issue', iid: 9001 }
@@ -166,8 +166,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when an issue with the same IID exists on database' do
- it 'returns 409' do
- post api("/projects/#{project.id}/issues", admin),
+ it 'returns 409', :aggregate_failures do
+ post api("/projects/#{project.id}/issues", admin, admin_mode: true),
params: { title: 'new issue', iid: issue.iid }
expect(response).to have_gitlab_http_status(:conflict)
@@ -176,7 +176,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'creates a new project issue' do
+ it 'creates a new project issue', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
@@ -189,7 +189,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['assignees'].first['name']).to eq(user2.name)
end
- it 'creates a new project issue with labels param as array' do
+ it 'creates a new project issue with labels param as array', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', labels: %w(label label2), weight: 3, assignee_ids: [user2.id] }
@@ -202,7 +202,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['assignees'].first['name']).to eq(user2.name)
end
- it 'creates a new confidential project issue' do
+ it 'creates a new confidential project issue', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', confidential: true }
@@ -211,7 +211,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['confidential']).to be_truthy
end
- it 'creates a new confidential project issue with a different param' do
+ it 'creates a new confidential project issue with a different param', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', confidential: 'y' }
@@ -220,7 +220,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['confidential']).to be_truthy
end
- it 'creates a public issue when confidential param is false' do
+ it 'creates a public issue when confidential param is false', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', confidential: false }
@@ -229,7 +229,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['confidential']).to be_falsy
end
- it 'creates a public issue when confidential param is invalid' do
+ it 'creates a public issue when confidential param is invalid', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', confidential: 'foo' }
@@ -242,7 +242,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'allows special label names' do
+ it 'allows special label names', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: {
title: 'new issue',
@@ -256,7 +256,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['labels']).to include '&'
end
- it 'allows special label names with labels param as array' do
+ it 'allows special label names with labels param as array', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: {
title: 'new issue',
@@ -270,7 +270,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['labels']).to include '&'
end
- it 'returns 400 if title is too long' do
+ it 'returns 400 if title is too long', :aggregate_failures do
post api("/projects/#{project.id}/issues", user),
params: { title: 'g' * 256 }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -313,7 +313,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'with due date' do
- it 'creates a new project issue' do
+ it 'creates a new project issue', :aggregate_failures do
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
post api("/projects/#{project.id}/issues", user),
@@ -336,8 +336,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'by an admin' do
- it 'sets the creation time on the new issue' do
- post api("/projects/#{project.id}/issues", admin), params: params
+ it 'sets the creation time on the new issue', :aggregate_failures do
+ post api("/projects/#{project.id}/issues", admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
@@ -346,7 +346,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'by a project owner' do
- it 'sets the creation time on the new issue' do
+ it 'sets the creation time on the new issue', :aggregate_failures do
post api("/projects/#{project.id}/issues", user), params: params
expect(response).to have_gitlab_http_status(:created)
@@ -356,7 +356,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'by a group owner' do
- it 'sets the creation time on the new issue' do
+ it 'sets the creation time on the new issue', :aggregate_failures do
group = create(:group)
group_project = create(:project, :public, namespace: group)
group.add_owner(user2)
@@ -370,7 +370,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'by another user' do
- it 'ignores the given creation time' do
+ it 'ignores the given creation time', :aggregate_failures do
project.add_developer(user2)
post api("/projects/#{project.id}/issues", user2), params: params
@@ -397,7 +397,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when request exceeds the rate limit' do
- it 'prevents users from creating more issues' do
+ it 'prevents users from creating more issues', :aggregate_failures do
allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
post api("/projects/#{project.id}/issues", user),
@@ -437,7 +437,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect { post_issue }.not_to change(Issue, :count)
end
- it 'returns correct status and message' do
+ it 'returns correct status and message', :aggregate_failures do
post_issue
expect(response).to have_gitlab_http_status(:bad_request)
@@ -476,7 +476,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace) }
- it 'moves an issue' do
+ it 'moves an issue', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
params: { to_project_id: target_project.id }
@@ -485,7 +485,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when source and target projects are the same' do
- it 'returns 400 when trying to move an issue' do
+ it 'returns 400 when trying to move an issue', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
params: { to_project_id: project.id }
@@ -495,7 +495,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when the user does not have the permission to move issues' do
- it 'returns 400 when trying to move an issue' do
+ it 'returns 400 when trying to move an issue', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
params: { to_project_id: target_project2.id }
@@ -504,8 +504,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'moves the issue to another namespace if I am admin' do
- post api("/projects/#{project.id}/issues/#{issue.iid}/move", admin),
+ it 'moves the issue to another namespace if I am admin', :aggregate_failures do
+ post api("/projects/#{project.id}/issues/#{issue.iid}/move", admin, admin_mode: true),
params: { to_project_id: target_project2.id }
expect(response).to have_gitlab_http_status(:created)
@@ -513,7 +513,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when using the issue ID instead of iid' do
- it 'returns 404 when trying to move an issue', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
+ it 'returns 404 when trying to move an issue', :aggregate_failures, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
params: { to_project_id: target_project.id }
@@ -523,7 +523,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when issue does not exist' do
- it 'returns 404 when trying to move an issue' do
+ it 'returns 404 when trying to move an issue', :aggregate_failures do
post api("/projects/#{project.id}/issues/123/move", user),
params: { to_project_id: target_project.id }
@@ -533,7 +533,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when source project does not exist' do
- it 'returns 404 when trying to move an issue' do
+ it 'returns 404 when trying to move an issue', :aggregate_failures do
post api("/projects/0/issues/#{issue.iid}/move", user),
params: { to_project_id: target_project.id }
@@ -562,7 +562,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'when user can admin the issue' do
context 'when the user can admin the target project' do
- it 'clones the issue' do
+ it 'clones the issue', :aggregate_failures do
expect do
post_clone_issue(user, issue, valid_target_project)
end.to change { valid_target_project.issues.count }.by(1)
@@ -577,7 +577,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when target project is the same source project' do
- it 'clones the issue' do
+ it 'clones the issue', :aggregate_failures do
expect do
post_clone_issue(user, issue, issue.project)
end.to change { issue.reset.project.issues.count }.by(1)
@@ -595,7 +595,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when the user does not have the permission to clone issues' do
- it 'returns 400' do
+ it 'returns 400', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
params: { to_project_id: invalid_target_project.id }
@@ -605,7 +605,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when using the issue ID instead of iid' do
- it 'returns 404', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
+ it 'returns 404', :aggregate_failures, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
post api("/projects/#{project.id}/issues/#{issue.id}/clone", user),
params: { to_project_id: valid_target_project.id }
@@ -615,7 +615,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when issue does not exist' do
- it 'returns 404' do
+ it 'returns 404', :aggregate_failures do
post api("/projects/#{project.id}/issues/12300/clone", user),
params: { to_project_id: valid_target_project.id }
@@ -625,7 +625,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when source project does not exist' do
- it 'returns 404' do
+ it 'returns 404', :aggregate_failures do
post api("/projects/0/issues/#{issue.iid}/clone", user),
params: { to_project_id: valid_target_project.id }
@@ -635,7 +635,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'when target project does not exist' do
- it 'returns 404' do
+ it 'returns 404', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
params: { to_project_id: 0 }
@@ -644,7 +644,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'clones the issue with notes when with_notes is true' do
+ it 'clones the issue with notes when with_notes is true', :aggregate_failures do
expect do
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
params: { to_project_id: valid_target_project.id, with_notes: true }
@@ -661,7 +661,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe 'POST :id/issues/:issue_iid/subscribe' do
- it 'subscribes to an issue' do
+ it 'subscribes to an issue', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/subscribe", user2)
expect(response).to have_gitlab_http_status(:created)
@@ -694,7 +694,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe 'POST :id/issues/:issue_id/unsubscribe' do
- it 'unsubscribes from an issue' do
+ it 'unsubscribes from an issue', :aggregate_failures do
post api("/projects/#{project.id}/issues/#{issue.iid}/unsubscribe", user)
expect(response).to have_gitlab_http_status(:created)
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb
index f0d174c9e78..6cc639c0bcc 100644
--- a/spec/requests/api/issues/put_projects_issues_spec.rb
+++ b/spec/requests/api/issues/put_projects_issues_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe 'PUT /projects/:id/issues/:issue_iid to update only title' do
- it 'updates a project issue' do
+ it 'updates a project issue', :aggregate_failures do
put api_for_user, params: { title: updated_title }
expect(response).to have_gitlab_http_status(:ok)
@@ -109,7 +109,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:ok)
end
- it 'allows special label names with labels param as array' do
+ it 'allows special label names with labels param as array', :aggregate_failures do
put api_for_user,
params: {
title: updated_title,
@@ -135,42 +135,42 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:forbidden)
end
- it 'updates a confidential issue for project members' do
+ it 'updates a confidential issue for project members', :aggregate_failures do
put api(confidential_issue_path, user), params: { title: updated_title }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['title']).to eq(updated_title)
end
- it 'updates a confidential issue for author' do
+ it 'updates a confidential issue for author', :aggregate_failures do
put api(confidential_issue_path, author), params: { title: updated_title }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['title']).to eq(updated_title)
end
- it 'updates a confidential issue for admin' do
- put api(confidential_issue_path, admin), params: { title: updated_title }
+ it 'updates a confidential issue for admin', :aggregate_failures do
+ put api(confidential_issue_path, admin, admin_mode: true), params: { title: updated_title }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['title']).to eq(updated_title)
end
- it 'sets an issue to confidential' do
+ it 'sets an issue to confidential', :aggregate_failures do
put api_for_user, params: { confidential: true }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['confidential']).to be_truthy
end
- it 'makes a confidential issue public' do
+ it 'makes a confidential issue public', :aggregate_failures do
put api(confidential_issue_path, user), params: { confidential: false }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['confidential']).to be_falsy
end
- it 'does not update a confidential issue with wrong confidential flag' do
+ it 'does not update a confidential issue with wrong confidential flag', :aggregate_failures do
put api(confidential_issue_path, user), params: { confidential: 'foo' }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -209,7 +209,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect { update_issue }.not_to change { issue.reload.title }
end
- it 'returns correct status and message' do
+ it 'returns correct status and message', :aggregate_failures do
update_issue
expect(response).to have_gitlab_http_status(:bad_request)
@@ -246,14 +246,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
describe 'PUT /projects/:id/issues/:issue_iid to update assignee' do
context 'support for deprecated assignee_id' do
- it 'removes assignee' do
+ it 'removes assignee', :aggregate_failures do
put api_for_user, params: { assignee_id: 0 }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['assignee']).to be_nil
end
- it 'updates an issue with new assignee' do
+ it 'updates an issue with new assignee', :aggregate_failures do
put api_for_user, params: { assignee_id: user2.id }
expect(response).to have_gitlab_http_status(:ok)
@@ -261,21 +261,21 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'removes assignee' do
+ it 'removes assignee', :aggregate_failures do
put api_for_user, params: { assignee_ids: [0] }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['assignees']).to be_empty
end
- it 'updates an issue with new assignee' do
+ it 'updates an issue with new assignee', :aggregate_failures do
put api_for_user, params: { assignee_ids: [user2.id] }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['assignees'].first['name']).to eq(user2.name)
end
- context 'single assignee restrictions' do
+ context 'single assignee restrictions', :aggregate_failures do
it 'updates an issue with several assignees but only one has been applied' do
put api_for_user, params: { assignee_ids: [user2.id, guest.id] }
@@ -289,7 +289,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:label) { create(:label, title: 'dummy', project: project) }
let!(:label_link) { create(:label_link, label: label, target: issue) }
- it 'adds relevant labels' do
+ it 'adds relevant labels', :aggregate_failures do
put api_for_user, params: { add_labels: '1, 2' }
expect(response).to have_gitlab_http_status(:ok)
@@ -300,14 +300,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let!(:label2) { create(:label, title: 'a-label', project: project) }
let!(:label_link2) { create(:label_link, label: label2, target: issue) }
- it 'removes relevant labels' do
+ it 'removes relevant labels', :aggregate_failures do
put api_for_user, params: { remove_labels: label2.title }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to eq([label.title])
end
- it 'removes all labels' do
+ it 'removes all labels', :aggregate_failures do
put api_for_user, params: { remove_labels: "#{label.title}, #{label2.title}" }
expect(response).to have_gitlab_http_status(:ok)
@@ -315,14 +315,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
end
- it 'does not update labels if not present' do
+ it 'does not update labels if not present', :aggregate_failures do
put api_for_user, params: { title: updated_title }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to eq([label.title])
end
- it 'removes all labels and touches the record' do
+ it 'removes all labels and touches the record', :aggregate_failures do
travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: '' }
end
@@ -332,7 +332,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['updated_at']).to be > Time.current
end
- it 'removes all labels and touches the record with labels param as array' do
+ it 'removes all labels and touches the record with labels param as array', :aggregate_failures do
travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: [''] }
end
@@ -342,7 +342,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['updated_at']).to be > Time.current
end
- it 'updates labels and touches the record' do
+ it 'updates labels and touches the record', :aggregate_failures do
travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: 'foo,bar' }
end
@@ -352,7 +352,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['updated_at']).to be > Time.current
end
- it 'updates labels and touches the record with labels param as array' do
+ it 'updates labels and touches the record with labels param as array', :aggregate_failures do
travel_to(2.minutes.from_now) do
put api_for_user, params: { labels: %w(foo bar) }
end
@@ -363,21 +363,21 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['updated_at']).to be > Time.current
end
- it 'allows special label names' do
+ it 'allows special label names', :aggregate_failures do
put api_for_user, params: { labels: 'label:foo, label-bar,label_bar,label/bar,label?bar,label&bar,?,&' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to contain_exactly('label:foo', 'label-bar', 'label_bar', 'label/bar', 'label?bar', 'label&bar', '?', '&')
end
- it 'allows special label names with labels param as array' do
+ it 'allows special label names with labels param as array', :aggregate_failures do
put api_for_user, params: { labels: ['label:foo', 'label-bar', 'label_bar', 'label/bar,label?bar,label&bar,?,&'] }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['labels']).to contain_exactly('label:foo', 'label-bar', 'label_bar', 'label/bar', 'label?bar', 'label&bar', '?', '&')
end
- it 'returns 400 if title is too long' do
+ it 'returns 400 if title is too long', :aggregate_failures do
put api_for_user, params: { title: 'g' * 256 }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -386,7 +386,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe 'PUT /projects/:id/issues/:issue_iid to update state and label' do
- it 'updates a project issue' do
+ it 'updates a project issue', :aggregate_failures do
put api_for_user, params: { labels: 'label2', state_event: 'close' }
expect(response).to have_gitlab_http_status(:ok)
@@ -394,7 +394,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(json_response['state']).to eq 'closed'
end
- it 'reopens a project isssue' do
+ it 'reopens a project isssue', :aggregate_failures do
put api(issue_path, user), params: { state_event: 'reopen' }
expect(response).to have_gitlab_http_status(:ok)
@@ -404,7 +404,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
describe 'PUT /projects/:id/issues/:issue_iid to update updated_at param' do
context 'when reporter makes request' do
- it 'accepts the update date to be set' do
+ it 'accepts the update date to be set', :aggregate_failures do
update_time = 2.weeks.ago
put api_for_user, params: { title: 'some new title', updated_at: update_time }
@@ -436,7 +436,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'accepts the update date to be set' do
+ it 'accepts the update date to be set', :aggregate_failures do
update_time = 2.weeks.ago
put api_for_owner, params: { title: 'some new title', updated_at: update_time }
@@ -448,7 +448,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
describe 'PUT /projects/:id/issues/:issue_iid to update due date' do
- it 'creates a new project issue' do
+ it 'creates a new project issue', :aggregate_failures do
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
put api_for_user, params: { due_date: due_date }
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index d9a0f061156..0ca1a7d030f 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Keys, feature_category: :authentication_and_authorization do
+RSpec.describe API::Keys, feature_category: :system_access do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:email) { create(:email, user: user) }
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb
index 82b87007a9b..3f131862a41 100644
--- a/spec/requests/api/lint_spec.rb
+++ b/spec/requests/api/lint_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Lint, feature_category: :pipeline_authoring do
+RSpec.describe API::Lint, feature_category: :pipeline_composition do
describe 'POST /ci/lint' do
context 'when signup settings are disabled' do
before do
@@ -245,8 +245,8 @@ RSpec.describe API::Lint, feature_category: :pipeline_authoring do
it 'passes validation' do
ci_lint
- included_config = YAML.safe_load(included_content, [Symbol])
- root_config = YAML.safe_load(yaml_content, [Symbol])
+ included_config = YAML.safe_load(included_content, permitted_classes: [Symbol])
+ root_config = YAML.safe_load(yaml_content, permitted_classes: [Symbol])
expected_yaml = included_config.merge(root_config).except(:include).deep_stringify_keys.to_yaml
expect(response).to have_gitlab_http_status(:ok)
@@ -535,8 +535,8 @@ RSpec.describe API::Lint, feature_category: :pipeline_authoring do
it 'passes validation' do
ci_lint
- included_config = YAML.safe_load(included_content, [Symbol])
- root_config = YAML.safe_load(yaml_content, [Symbol])
+ included_config = YAML.safe_load(included_content, permitted_classes: [Symbol])
+ root_config = YAML.safe_load(yaml_content, permitted_classes: [Symbol])
expected_yaml = included_config.merge(root_config).except(:include).deep_stringify_keys.to_yaml
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index 20aa660d95b..7b850fed79c 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) }
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_maven_user' } }
let(:package_name) { 'com/example/my-app' }
let(:headers) { workhorse_headers }
@@ -285,6 +285,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
describe 'GET /api/v4/packages/maven/*path/:file_name' do
context 'a public project' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } }
+
subject { download_file(file_name: package_file.file_name) }
shared_examples 'getting a file' do
@@ -451,6 +453,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'forwarding package requests'
context 'a public project' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } }
+
subject { download_file(file_name: package_file.file_name) }
shared_examples 'getting a file for a group' do
@@ -660,6 +664,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
describe 'GET /api/v4/projects/:id/packages/maven/*path/:file_name' do
context 'a public project' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } }
+
subject { download_file(file_name: package_file.file_name) }
it_behaves_like 'tracking the file download event'
@@ -901,8 +907,6 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'package workhorse uploads'
context 'event tracking' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_maven_user' } }
-
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
context 'when the package file fails to be created' do
@@ -962,6 +966,17 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
expect(response).to have_gitlab_http_status(:forbidden)
end
+ context 'file name is too long' do
+ let(:file_name) { 'a' * (Packages::Maven::FindOrCreatePackageService::MAX_FILE_NAME_LENGTH + 1) }
+
+ it 'rejects request' do
+ expect { upload_file_with_token(params: params, file_name: file_name) }.not_to change { project.packages.count }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to include('File name is too long')
+ end
+ end
+
context 'version is not correct' do
let(:version) { '$%123' }
@@ -981,9 +996,9 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
package_settings.update!(maven_duplicates_allowed: false)
end
- shared_examples 'storing the package file' do
+ shared_examples 'storing the package file' do |file_name: 'my-app-1.0-20180724.124855-1'|
it 'stores the file', :aggregate_failures do
- expect { upload_file_with_token(params: params) }.to change { package.package_files.count }.by(1)
+ expect { upload_file_with_token(params: params, file_name: file_name) }.to change { package.package_files.count }.by(1)
expect(response).to have_gitlab_http_status(:ok)
expect(jar_file.file_name).to eq(file_upload.original_filename)
@@ -1023,6 +1038,10 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'storing the package file'
end
+
+ context 'when uploading a similar package file name with a classifier' do
+ it_behaves_like 'storing the package file', file_name: 'my-app-1.0-20180724.124855-1-javadoc'
+ end
end
context 'for sha1 file' do
@@ -1088,8 +1107,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
end
end
- def upload_file(params: {}, request_headers: headers, file_extension: 'jar')
- url = "/projects/#{project.id}/packages/maven/#{param_path}/my-app-1.0-20180724.124855-1.#{file_extension}"
+ def upload_file(params: {}, request_headers: headers, file_extension: 'jar', file_name: 'my-app-1.0-20180724.124855-1')
+ url = "/projects/#{project.id}/packages/maven/#{param_path}/#{file_name}.#{file_extension}"
workhorse_finalize(
api(url),
method: :put,
@@ -1100,8 +1119,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
)
end
- def upload_file_with_token(params: {}, request_headers: headers_with_token, file_extension: 'jar')
- upload_file(params: params, request_headers: request_headers, file_extension: file_extension)
+ def upload_file_with_token(params: {}, request_headers: headers_with_token, file_extension: 'jar', file_name: 'my-app-1.0-20180724.124855-1')
+ upload_file(params: params, request_headers: request_headers, file_name: file_name, file_extension: file_extension)
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 19a630e5218..81815fdab62 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -168,6 +168,17 @@ RSpec.describe API::MergeRequests, feature_category: :source_code_management do
end
end
+ context 'when DB timeouts occur' do
+ it 'returns a :request_timeout status' do
+ allow(MergeRequestsFinder).to receive(:new).and_raise(ActiveRecord::QueryCanceled)
+
+ path = endpoint_path + '?view=simple'
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:request_timeout)
+ end
+ end
+
it 'returns an array of all merge_requests using simple mode' do
path = endpoint_path + '?view=simple'
diff --git a/spec/requests/api/metadata_spec.rb b/spec/requests/api/metadata_spec.rb
index b9bdadb01cc..e15186c48a5 100644
--- a/spec/requests/api/metadata_spec.rb
+++ b/spec/requests/api/metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Metadata, feature_category: :not_owned do
+RSpec.describe API::Metadata, feature_category: :shared do
shared_examples_for 'GET /metadata' do
context 'when unauthenticated' do
it 'returns authentication error' do
diff --git a/spec/requests/api/npm_instance_packages_spec.rb b/spec/requests/api/npm_instance_packages_spec.rb
index dcd2e4ae677..591a8ee68dc 100644
--- a/spec/requests/api/npm_instance_packages_spec.rb
+++ b/spec/requests/api/npm_instance_packages_spec.rb
@@ -11,8 +11,38 @@ RSpec.describe API::NpmInstancePackages, feature_category: :package_registry do
include_context 'npm api setup'
describe 'GET /api/v4/packages/npm/*package_name' do
- it_behaves_like 'handling get metadata requests', scope: :instance do
- let(:url) { api("/packages/npm/#{package_name}") }
+ let(:url) { api("/packages/npm/#{package_name}") }
+
+ it_behaves_like 'handling get metadata requests', scope: :instance
+
+ context 'with a duplicate package name in another project' do
+ subject { get(url) }
+
+ let_it_be(:project2) { create(:project, :public, namespace: namespace) }
+ let_it_be(:package2) do
+ create(:npm_package,
+ project: project2,
+ name: "@#{group.path}/scoped_package",
+ version: '1.2.0')
+ end
+
+ it 'includes all matching package versions in the response' do
+ subject
+
+ expect(json_response['versions'].keys).to match_array([package.version, package2.version])
+ end
+
+ context 'with the feature flag disabled' do
+ before do
+ stub_feature_flags(npm_allow_packages_in_multiple_projects: false)
+ end
+
+ it 'returns matching package versions from only one project' do
+ subject
+
+ expect(json_response['versions'].keys).to match_array([package2.version])
+ end
+ end
end
end
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index c62c0849776..2f67e1e8eea 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -115,6 +115,8 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
end
context 'private project' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_npm_user' } }
+
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
@@ -143,6 +145,8 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
end
context 'internal project' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_npm_user' } }
+
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
diff --git a/spec/requests/api/nuget_group_packages_spec.rb b/spec/requests/api/nuget_group_packages_spec.rb
index 4335ad75ab6..facbc01220d 100644
--- a/spec/requests/api/nuget_group_packages_spec.rb
+++ b/spec/requests/api/nuget_group_packages_spec.rb
@@ -12,8 +12,17 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
let_it_be(:deploy_token) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) }
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token, group: group) }
- let(:snowplow_gitlab_standard_context) { { namespace: project.group, property: 'i_package_nuget_user' } }
let(:target_type) { 'groups' }
+ let(:snowplow_gitlab_standard_context) { snowplow_context }
+ let(:target) { subgroup }
+
+ def snowplow_context(user_role: :developer)
+ if user_role == :anonymous
+ { namespace: target, property: 'i_package_nuget_user' }
+ else
+ { namespace: target, property: 'i_package_nuget_user', user: user }
+ end
+ end
shared_examples 'handling all endpoints' do
describe 'GET /api/v4/groups/:id/-/packages/nuget' do
@@ -84,7 +93,6 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
context 'a group' do
let(:target) { group }
- let(:snowplow_gitlab_standard_context) { { namespace: target, property: 'i_package_nuget_user' } }
it_behaves_like 'handling all endpoints'
diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb
index 1e0d35ad451..887dfd4beeb 100644
--- a/spec/requests/api/nuget_project_packages_spec.rb
+++ b/spec/requests/api/nuget_project_packages_spec.rb
@@ -13,7 +13,15 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
let(:target) { project }
let(:target_type) { 'projects' }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_nuget_user' } }
+ let(:snowplow_gitlab_standard_context) { snowplow_context }
+
+ def snowplow_context(user_role: :developer)
+ if user_role == :anonymous
+ { project: target, namespace: target.namespace, property: 'i_package_nuget_user' }
+ else
+ { project: target, namespace: target.namespace, property: 'i_package_nuget_user', user: user }
+ end
+ end
shared_examples 'accept get request on private project with access to package registry for everyone' do
subject { get api(url) }
@@ -149,6 +157,7 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
subject { get api(url), headers: headers }
diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb
index b29f1e9e661..19a943477d2 100644
--- a/spec/requests/api/oauth_tokens_spec.rb
+++ b/spec/requests/api/oauth_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'OAuth tokens', feature_category: :authentication_and_authorization do
+RSpec.describe 'OAuth tokens', feature_category: :system_access do
include HttpBasicAuthHelpers
context 'Resource Owner Password Credentials' do
@@ -124,6 +124,8 @@ RSpec.describe 'OAuth tokens', feature_category: :authentication_and_authorizati
context 'when user account is not confirmed' do
before do
+ stub_application_setting_enum('email_confirmation_setting', 'soft')
+
user.update!(confirmed_at: nil)
request_oauth_token(user, client_basic_auth_header(client))
diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb
index fdc25ecdcd3..3e7837866ae 100644
--- a/spec/requests/api/pages/internal_access_spec.rb
+++ b/spec/requests/api/pages/internal_access_spec.rb
@@ -35,39 +35,39 @@ RSpec.describe "Internal Project Pages Access", feature_category: :pages do
describe "GET /projects/:id/pages_access" do
context 'access depends on the level' do
- where(:pages_access_level, :with_user, :expected_result) do
- ProjectFeature::DISABLED | "admin" | 403
- ProjectFeature::DISABLED | "owner" | 403
- ProjectFeature::DISABLED | "master" | 403
- ProjectFeature::DISABLED | "developer" | 403
- ProjectFeature::DISABLED | "reporter" | 403
- ProjectFeature::DISABLED | "guest" | 403
- ProjectFeature::DISABLED | "user" | 403
- ProjectFeature::DISABLED | nil | 404
- ProjectFeature::PUBLIC | "admin" | 200
- ProjectFeature::PUBLIC | "owner" | 200
- ProjectFeature::PUBLIC | "master" | 200
- ProjectFeature::PUBLIC | "developer" | 200
- ProjectFeature::PUBLIC | "reporter" | 200
- ProjectFeature::PUBLIC | "guest" | 200
- ProjectFeature::PUBLIC | "user" | 200
- ProjectFeature::PUBLIC | nil | 404
- ProjectFeature::ENABLED | "admin" | 200
- ProjectFeature::ENABLED | "owner" | 200
- ProjectFeature::ENABLED | "master" | 200
- ProjectFeature::ENABLED | "developer" | 200
- ProjectFeature::ENABLED | "reporter" | 200
- ProjectFeature::ENABLED | "guest" | 200
- ProjectFeature::ENABLED | "user" | 200
- ProjectFeature::ENABLED | nil | 404
- ProjectFeature::PRIVATE | "admin" | 200
- ProjectFeature::PRIVATE | "owner" | 200
- ProjectFeature::PRIVATE | "master" | 200
- ProjectFeature::PRIVATE | "developer" | 200
- ProjectFeature::PRIVATE | "reporter" | 200
- ProjectFeature::PRIVATE | "guest" | 200
- ProjectFeature::PRIVATE | "user" | 403
- ProjectFeature::PRIVATE | nil | 404
+ where(:pages_access_level, :with_user, :admin_mode, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | true | 403
+ ProjectFeature::DISABLED | "owner" | false | 403
+ ProjectFeature::DISABLED | "master" | false | 403
+ ProjectFeature::DISABLED | "developer" | false | 403
+ ProjectFeature::DISABLED | "reporter" | false | 403
+ ProjectFeature::DISABLED | "guest" | false | 403
+ ProjectFeature::DISABLED | "user" | false | 403
+ ProjectFeature::DISABLED | nil | false | 404
+ ProjectFeature::PUBLIC | "admin" | false | 200
+ ProjectFeature::PUBLIC | "owner" | false | 200
+ ProjectFeature::PUBLIC | "master" | false | 200
+ ProjectFeature::PUBLIC | "developer" | false | 200
+ ProjectFeature::PUBLIC | "reporter" | false | 200
+ ProjectFeature::PUBLIC | "guest" | false | 200
+ ProjectFeature::PUBLIC | "user" | false | 200
+ ProjectFeature::PUBLIC | nil | false | 404
+ ProjectFeature::ENABLED | "admin" | false | 200
+ ProjectFeature::ENABLED | "owner" | false | 200
+ ProjectFeature::ENABLED | "master" | false | 200
+ ProjectFeature::ENABLED | "developer" | false | 200
+ ProjectFeature::ENABLED | "reporter" | false | 200
+ ProjectFeature::ENABLED | "guest" | false | 200
+ ProjectFeature::ENABLED | "user" | false | 200
+ ProjectFeature::ENABLED | nil | false | 404
+ ProjectFeature::PRIVATE | "admin" | true | 200
+ ProjectFeature::PRIVATE | "owner" | false | 200
+ ProjectFeature::PRIVATE | "master" | false | 200
+ ProjectFeature::PRIVATE | "developer" | false | 200
+ ProjectFeature::PRIVATE | "reporter" | false | 200
+ ProjectFeature::PRIVATE | "guest" | false | 200
+ ProjectFeature::PRIVATE | "user" | false | 403
+ ProjectFeature::PRIVATE | nil | false | 404
end
with_them do
@@ -77,7 +77,7 @@ RSpec.describe "Internal Project Pages Access", feature_category: :pages do
it "correct return value" do
if !with_user.nil?
user = public_send(with_user)
- get api("/projects/#{project.id}/pages_access", user)
+ get api("/projects/#{project.id}/pages_access", user, admin_mode: admin_mode)
else
get api("/projects/#{project.id}/pages_access")
end
diff --git a/spec/requests/api/pages/pages_spec.rb b/spec/requests/api/pages/pages_spec.rb
index c426f2a433c..0f6675799ad 100644
--- a/spec/requests/api/pages/pages_spec.rb
+++ b/spec/requests/api/pages/pages_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe API::Pages, feature_category: :pages do
- let_it_be(:project) { create(:project, path: 'my.project', pages_https_only: false) }
+ let_it_be_with_reload(:project) { create(:project, path: 'my.project', pages_https_only: false) }
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
@@ -19,7 +19,7 @@ RSpec.describe API::Pages, feature_category: :pages do
end
it_behaves_like '404 response' do
- let(:request) { delete api("/projects/#{project.id}/pages", admin) }
+ let(:request) { delete api("/projects/#{project.id}/pages", admin, admin_mode: true) }
end
end
@@ -30,13 +30,13 @@ RSpec.describe API::Pages, feature_category: :pages do
context 'when Pages are deployed' do
it 'returns 204' do
- delete api("/projects/#{project.id}/pages", admin)
+ delete api("/projects/#{project.id}/pages", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:no_content)
end
it 'removes the pages' do
- delete api("/projects/#{project.id}/pages", admin)
+ delete api("/projects/#{project.id}/pages", admin, admin_mode: true)
expect(project.reload.pages_metadatum.deployed?).to be(false)
end
@@ -48,7 +48,7 @@ RSpec.describe API::Pages, feature_category: :pages do
end
it 'returns 204' do
- delete api("/projects/#{project.id}/pages", admin)
+ delete api("/projects/#{project.id}/pages", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:no_content)
end
@@ -58,7 +58,7 @@ RSpec.describe API::Pages, feature_category: :pages do
it 'returns 404' do
id = -1
- delete api("/projects/#{id}/pages", admin)
+ delete api("/projects/#{id}/pages", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb
index 5cc1b8f9a69..602eff73b0a 100644
--- a/spec/requests/api/pages/private_access_spec.rb
+++ b/spec/requests/api/pages/private_access_spec.rb
@@ -35,39 +35,39 @@ RSpec.describe "Private Project Pages Access", feature_category: :pages do
describe "GET /projects/:id/pages_access" do
context 'access depends on the level' do
- where(:pages_access_level, :with_user, :expected_result) do
- ProjectFeature::DISABLED | "admin" | 403
- ProjectFeature::DISABLED | "owner" | 403
- ProjectFeature::DISABLED | "master" | 403
- ProjectFeature::DISABLED | "developer" | 403
- ProjectFeature::DISABLED | "reporter" | 403
- ProjectFeature::DISABLED | "guest" | 403
- ProjectFeature::DISABLED | "user" | 404
- ProjectFeature::DISABLED | nil | 404
- ProjectFeature::PUBLIC | "admin" | 200
- ProjectFeature::PUBLIC | "owner" | 200
- ProjectFeature::PUBLIC | "master" | 200
- ProjectFeature::PUBLIC | "developer" | 200
- ProjectFeature::PUBLIC | "reporter" | 200
- ProjectFeature::PUBLIC | "guest" | 200
- ProjectFeature::PUBLIC | "user" | 404
- ProjectFeature::PUBLIC | nil | 404
- ProjectFeature::ENABLED | "admin" | 200
- ProjectFeature::ENABLED | "owner" | 200
- ProjectFeature::ENABLED | "master" | 200
- ProjectFeature::ENABLED | "developer" | 200
- ProjectFeature::ENABLED | "reporter" | 200
- ProjectFeature::ENABLED | "guest" | 200
- ProjectFeature::ENABLED | "user" | 404
- ProjectFeature::ENABLED | nil | 404
- ProjectFeature::PRIVATE | "admin" | 200
- ProjectFeature::PRIVATE | "owner" | 200
- ProjectFeature::PRIVATE | "master" | 200
- ProjectFeature::PRIVATE | "developer" | 200
- ProjectFeature::PRIVATE | "reporter" | 200
- ProjectFeature::PRIVATE | "guest" | 200
- ProjectFeature::PRIVATE | "user" | 404
- ProjectFeature::PRIVATE | nil | 404
+ where(:pages_access_level, :with_user, :admin_mode, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | true | 403
+ ProjectFeature::DISABLED | "owner" | false | 403
+ ProjectFeature::DISABLED | "master" | false | 403
+ ProjectFeature::DISABLED | "developer" | false | 403
+ ProjectFeature::DISABLED | "reporter" | false | 403
+ ProjectFeature::DISABLED | "guest" | false | 403
+ ProjectFeature::DISABLED | "user" | false | 404
+ ProjectFeature::DISABLED | nil | false | 404
+ ProjectFeature::PUBLIC | "admin" | true | 200
+ ProjectFeature::PUBLIC | "owner" | false | 200
+ ProjectFeature::PUBLIC | "master" | false | 200
+ ProjectFeature::PUBLIC | "developer" | false | 200
+ ProjectFeature::PUBLIC | "reporter" | false | 200
+ ProjectFeature::PUBLIC | "guest" | false | 200
+ ProjectFeature::PUBLIC | "user" | false | 404
+ ProjectFeature::PUBLIC | nil | false | 404
+ ProjectFeature::ENABLED | "admin" | true | 200
+ ProjectFeature::ENABLED | "owner" | false | 200
+ ProjectFeature::ENABLED | "master" | false | 200
+ ProjectFeature::ENABLED | "developer" | false | 200
+ ProjectFeature::ENABLED | "reporter" | false | 200
+ ProjectFeature::ENABLED | "guest" | false | 200
+ ProjectFeature::ENABLED | "user" | false | 404
+ ProjectFeature::ENABLED | nil | false | 404
+ ProjectFeature::PRIVATE | "admin" | true | 200
+ ProjectFeature::PRIVATE | "owner" | false | 200
+ ProjectFeature::PRIVATE | "master" | false | 200
+ ProjectFeature::PRIVATE | "developer" | false | 200
+ ProjectFeature::PRIVATE | "reporter" | false | 200
+ ProjectFeature::PRIVATE | "guest" | false | 200
+ ProjectFeature::PRIVATE | "user" | false | 404
+ ProjectFeature::PRIVATE | nil | false | 404
end
with_them do
@@ -77,7 +77,7 @@ RSpec.describe "Private Project Pages Access", feature_category: :pages do
it "correct return value" do
if !with_user.nil?
user = public_send(with_user)
- get api("/projects/#{project.id}/pages_access", user)
+ get api("/projects/#{project.id}/pages_access", user, admin_mode: admin_mode)
else
get api("/projects/#{project.id}/pages_access")
end
diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb
index 1137f91f4b0..8b0ed7c59ab 100644
--- a/spec/requests/api/pages/public_access_spec.rb
+++ b/spec/requests/api/pages/public_access_spec.rb
@@ -35,39 +35,39 @@ RSpec.describe "Public Project Pages Access", feature_category: :pages do
describe "GET /projects/:id/pages_access" do
context 'access depends on the level' do
- where(:pages_access_level, :with_user, :expected_result) do
- ProjectFeature::DISABLED | "admin" | 403
- ProjectFeature::DISABLED | "owner" | 403
- ProjectFeature::DISABLED | "master" | 403
- ProjectFeature::DISABLED | "developer" | 403
- ProjectFeature::DISABLED | "reporter" | 403
- ProjectFeature::DISABLED | "guest" | 403
- ProjectFeature::DISABLED | "user" | 403
- ProjectFeature::DISABLED | nil | 403
- ProjectFeature::PUBLIC | "admin" | 200
- ProjectFeature::PUBLIC | "owner" | 200
- ProjectFeature::PUBLIC | "master" | 200
- ProjectFeature::PUBLIC | "developer" | 200
- ProjectFeature::PUBLIC | "reporter" | 200
- ProjectFeature::PUBLIC | "guest" | 200
- ProjectFeature::PUBLIC | "user" | 200
- ProjectFeature::PUBLIC | nil | 200
- ProjectFeature::ENABLED | "admin" | 200
- ProjectFeature::ENABLED | "owner" | 200
- ProjectFeature::ENABLED | "master" | 200
- ProjectFeature::ENABLED | "developer" | 200
- ProjectFeature::ENABLED | "reporter" | 200
- ProjectFeature::ENABLED | "guest" | 200
- ProjectFeature::ENABLED | "user" | 200
- ProjectFeature::ENABLED | nil | 200
- ProjectFeature::PRIVATE | "admin" | 200
- ProjectFeature::PRIVATE | "owner" | 200
- ProjectFeature::PRIVATE | "master" | 200
- ProjectFeature::PRIVATE | "developer" | 200
- ProjectFeature::PRIVATE | "reporter" | 200
- ProjectFeature::PRIVATE | "guest" | 200
- ProjectFeature::PRIVATE | "user" | 403
- ProjectFeature::PRIVATE | nil | 403
+ where(:pages_access_level, :with_user, :admin_mode, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | false | 403
+ ProjectFeature::DISABLED | "owner" | false | 403
+ ProjectFeature::DISABLED | "master" | false | 403
+ ProjectFeature::DISABLED | "developer" | false | 403
+ ProjectFeature::DISABLED | "reporter" | false | 403
+ ProjectFeature::DISABLED | "guest" | false | 403
+ ProjectFeature::DISABLED | "user" | false | 403
+ ProjectFeature::DISABLED | nil | false | 403
+ ProjectFeature::PUBLIC | "admin" | false | 200
+ ProjectFeature::PUBLIC | "owner" | false | 200
+ ProjectFeature::PUBLIC | "master" | false | 200
+ ProjectFeature::PUBLIC | "developer" | false | 200
+ ProjectFeature::PUBLIC | "reporter" | false | 200
+ ProjectFeature::PUBLIC | "guest" | false | 200
+ ProjectFeature::PUBLIC | "user" | false | 200
+ ProjectFeature::PUBLIC | nil | false | 200
+ ProjectFeature::ENABLED | "admin" | false | 200
+ ProjectFeature::ENABLED | "owner" | false | 200
+ ProjectFeature::ENABLED | "master" | false | 200
+ ProjectFeature::ENABLED | "developer" | false | 200
+ ProjectFeature::ENABLED | "reporter" | false | 200
+ ProjectFeature::ENABLED | "guest" | false | 200
+ ProjectFeature::ENABLED | "user" | false | 200
+ ProjectFeature::ENABLED | nil | false | 200
+ ProjectFeature::PRIVATE | "admin" | true | 200
+ ProjectFeature::PRIVATE | "owner" | false | 200
+ ProjectFeature::PRIVATE | "master" | false | 200
+ ProjectFeature::PRIVATE | "developer" | false | 200
+ ProjectFeature::PRIVATE | "reporter" | false | 200
+ ProjectFeature::PRIVATE | "guest" | false | 200
+ ProjectFeature::PRIVATE | "user" | false | 403
+ ProjectFeature::PRIVATE | nil | false | 403
end
with_them do
@@ -77,7 +77,7 @@ RSpec.describe "Public Project Pages Access", feature_category: :pages do
it "correct return value" do
if !with_user.nil?
user = public_send(with_user)
- get api("/projects/#{project.id}/pages_access", user)
+ get api("/projects/#{project.id}/pages_access", user, admin_mode: admin_mode)
else
get api("/projects/#{project.id}/pages_access")
end
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index ba1fb5105b8..ea83fa384af 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -41,14 +41,14 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
end
it_behaves_like '404 response' do
- let(:request) { get api('/pages/domains', admin) }
+ let(:request) { get api('/pages/domains', admin, admin_mode: true) }
end
end
context 'when pages is enabled' do
context 'when authenticated as an admin' do
- it 'returns paginated all pages domains' do
- get api('/pages/domains', admin)
+ it 'returns paginated all pages domains', :aggregate_failures do
+ get api('/pages/domains', admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/pages_domain_basics')
@@ -74,7 +74,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
describe 'GET /projects/:project_id/pages/domains' do
shared_examples_for 'get pages domains' do
- it 'returns paginated pages domains' do
+ it 'returns paginated pages domains', :aggregate_failures do
get api(route, user)
expect(response).to have_gitlab_http_status(:ok)
@@ -145,7 +145,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
describe 'GET /projects/:project_id/pages/domains/:domain' do
shared_examples_for 'get pages domain' do
- it 'returns pages domain' do
+ it 'returns pages domain', :aggregate_failures do
get api(route_domain, user)
expect(response).to have_gitlab_http_status(:ok)
@@ -155,7 +155,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(json_response['certificate']).to be_nil
end
- it 'returns pages domain with project path' do
+ it 'returns pages domain with project path', :aggregate_failures do
get api(route_domain_path, user)
expect(response).to have_gitlab_http_status(:ok)
@@ -165,7 +165,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(json_response['certificate']).to be_nil
end
- it 'returns pages domain with a certificate' do
+ it 'returns pages domain with a certificate', :aggregate_failures do
get api(route_secure_domain, user)
expect(response).to have_gitlab_http_status(:ok)
@@ -177,7 +177,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(json_response['auto_ssl_enabled']).to be false
end
- it 'returns pages domain with an expired certificate' do
+ it 'returns pages domain with an expired certificate', :aggregate_failures do
get api(route_expired_domain, user)
expect(response).to have_gitlab_http_status(:ok)
@@ -185,7 +185,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(json_response['certificate']['expired']).to be true
end
- it 'returns pages domain with letsencrypt' do
+ it 'returns pages domain with letsencrypt', :aggregate_failures do
get api(route_letsencrypt_domain, user)
expect(response).to have_gitlab_http_status(:ok)
@@ -258,7 +258,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
let(:params_secure) { pages_domain_secure_params.slice(:domain, :certificate, :key) }
shared_examples_for 'post pages domains' do
- it 'creates a new pages domain' do
+ it 'creates a new pages domain', :aggregate_failures do
expect { post api(route, user), params: params }
.to publish_event(PagesDomains::PagesDomainCreatedEvent)
.with(
@@ -279,7 +279,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain.auto_ssl_enabled).to be false
end
- it 'creates a new secure pages domain' do
+ it 'creates a new secure pages domain', :aggregate_failures do
post api(route, user), params: params_secure
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
@@ -291,7 +291,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain.auto_ssl_enabled).to be false
end
- it 'creates domain with letsencrypt enabled' do
+ it 'creates domain with letsencrypt enabled', :aggregate_failures do
post api(route, user), params: pages_domain_with_letsencrypt_params
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
@@ -301,7 +301,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain.auto_ssl_enabled).to be true
end
- it 'creates domain with letsencrypt enabled and provided certificate' do
+ it 'creates domain with letsencrypt enabled and provided certificate', :aggregate_failures do
post api(route, user), params: params_secure.merge(auto_ssl_enabled: true)
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
@@ -376,7 +376,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
let(:params_secure_nokey) { pages_domain_secure_params.slice(:certificate) }
shared_examples_for 'put pages domain' do
- it 'updates pages domain removing certificate' do
+ it 'updates pages domain removing certificate', :aggregate_failures do
put api(route_secure_domain, user), params: { certificate: nil, key: nil }
pages_domain_secure.reload
@@ -399,7 +399,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
)
end
- it 'updates pages domain adding certificate' do
+ it 'updates pages domain adding certificate', :aggregate_failures do
put api(route_domain, user), params: params_secure
pages_domain.reload
@@ -409,7 +409,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain.key).to eq(params_secure[:key])
end
- it 'updates pages domain adding certificate with letsencrypt' do
+ it 'updates pages domain adding certificate with letsencrypt', :aggregate_failures do
put api(route_domain, user), params: params_secure.merge(auto_ssl_enabled: true)
pages_domain.reload
@@ -420,7 +420,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain.auto_ssl_enabled).to be true
end
- it 'updates pages domain enabling letsencrypt' do
+ it 'updates pages domain enabling letsencrypt', :aggregate_failures do
put api(route_domain, user), params: { auto_ssl_enabled: true }
pages_domain.reload
@@ -429,7 +429,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain.auto_ssl_enabled).to be true
end
- it 'updates pages domain disabling letsencrypt while preserving the certificate' do
+ it 'updates pages domain disabling letsencrypt while preserving the certificate', :aggregate_failures do
put api(route_letsencrypt_domain, user), params: { auto_ssl_enabled: false }
pages_domain_with_letsencrypt.reload
@@ -440,7 +440,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain_with_letsencrypt.certificate).to be
end
- it 'updates pages domain with expired certificate' do
+ it 'updates pages domain with expired certificate', :aggregate_failures do
put api(route_expired_domain, user), params: params_secure
pages_domain_expired.reload
@@ -450,7 +450,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
expect(pages_domain_expired.key).to eq(params_secure[:key])
end
- it 'updates pages domain with expired certificate not updating key' do
+ it 'updates pages domain with expired certificate not updating key', :aggregate_failures do
put api(route_secure_domain, user), params: params_secure_nokey
pages_domain_secure.reload
diff --git a/spec/requests/api/personal_access_tokens/self_information_spec.rb b/spec/requests/api/personal_access_tokens/self_information_spec.rb
index 4a3c0ad8904..2a7af350054 100644
--- a/spec/requests/api/personal_access_tokens/self_information_spec.rb
+++ b/spec/requests/api/personal_access_tokens/self_information_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::PersonalAccessTokens::SelfInformation, feature_category: :authentication_and_authorization do
+RSpec.describe API::PersonalAccessTokens::SelfInformation, feature_category: :system_access do
let(:path) { '/personal_access_tokens/self' }
let(:token) { create(:personal_access_token, user: current_user) }
diff --git a/spec/requests/api/personal_access_tokens_spec.rb b/spec/requests/api/personal_access_tokens_spec.rb
index 32adc7ebd61..cca94c7a012 100644
--- a/spec/requests/api/personal_access_tokens_spec.rb
+++ b/spec/requests/api/personal_access_tokens_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::PersonalAccessTokens, feature_category: :authentication_and_authorization do
+RSpec.describe API::PersonalAccessTokens, feature_category: :system_access do
let_it_be(:path) { '/personal_access_tokens' }
describe 'GET /personal_access_tokens' do
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
index 9d722e4a445..978ac28ef73 100644
--- a/spec/requests/api/project_milestones_spec.rb
+++ b/spec/requests/api/project_milestones_spec.rb
@@ -6,8 +6,12 @@ RSpec.describe API::ProjectMilestones, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, namespace: user.namespace) }
let_it_be(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
- let_it_be(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
let_it_be(:route) { "/projects/#{project.id}/milestones" }
+ let_it_be(:milestone) do
+ create(:milestone, project: project, title: 'version2', description: 'open milestone', updated_at: 5.days.ago)
+ end
+
+ let(:params) { {} }
before_all do
project.add_reporter(user)
@@ -15,38 +19,43 @@ RSpec.describe API::ProjectMilestones, feature_category: :team_planning do
it_behaves_like 'group and project milestones', "/projects/:id/milestones"
+ shared_examples 'listing all milestones' do
+ it 'returns correct list of milestones' do
+ get api(route, user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(milestones.size)
+ expect(json_response.map { |entry| entry["id"] }).to match_array(milestones.map(&:id))
+ end
+ end
+
describe 'GET /projects/:id/milestones' do
- context 'when include_parent_milestones is true' do
- let_it_be(:ancestor_group) { create(:group, :private) }
- let_it_be(:group) { create(:group, :private, parent: ancestor_group) }
- let_it_be(:ancestor_group_milestone) { create(:milestone, group: ancestor_group) }
- let_it_be(:group_milestone) { create(:milestone, group: group) }
+ let_it_be(:ancestor_group) { create(:group, :private) }
+ let_it_be(:group) { create(:group, :private, parent: ancestor_group) }
+ let_it_be(:ancestor_group_milestone) { create(:milestone, group: ancestor_group, updated_at: 1.day.ago) }
+ let_it_be(:group_milestone) { create(:milestone, group: group, updated_at: 3.days.ago) }
- let(:params) { { include_parent_milestones: true } }
+ context 'when project parent is a namespace' do
+ let(:milestones) { [milestone, closed_milestone] }
- shared_examples 'listing all milestones' do
- it 'returns correct list of milestones' do
- get api(route, user), params: params
+ it_behaves_like 'listing all milestones'
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.size).to eq(milestones.size)
- expect(json_response.map { |entry| entry["id"] }).to eq(milestones.map(&:id))
- end
+ context 'when include_parent_milestones is true' do
+ let(:params) { { include_parent_milestones: true } }
+
+ it_behaves_like 'listing all milestones'
end
+ end
- context 'when project parent is a namespace' do
- it_behaves_like 'listing all milestones' do
- let(:milestones) { [milestone, closed_milestone] }
- end
+ context 'when project parent is a group' do
+ before_all do
+ project.update!(namespace: group)
end
- context 'when project parent is a group' do
+ context 'when include_parent_milestones is true' do
+ let(:params) { { include_parent_milestones: true } }
let(:milestones) { [group_milestone, ancestor_group_milestone, milestone, closed_milestone] }
- before_all do
- project.update!(namespace: group)
- end
-
it_behaves_like 'listing all milestones'
context 'when iids param is present' do
@@ -64,6 +73,38 @@ RSpec.describe API::ProjectMilestones, feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'when updated_before param is present' do
+ let(:params) { { updated_before: 12.hours.ago.iso8601, include_parent_milestones: true } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [group_milestone, ancestor_group_milestone, milestone] }
+ end
+ end
+
+ context 'when updated_after param is present' do
+ let(:params) { { updated_after: 2.days.ago.iso8601, include_parent_milestones: true } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [ancestor_group_milestone, closed_milestone] }
+ end
+ end
+ end
+
+ context 'when updated_before param is present' do
+ let(:params) { { updated_before: 12.hours.ago.iso8601 } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [milestone] }
+ end
+ end
+
+ context 'when updated_after param is present' do
+ let(:params) { { updated_after: 2.days.ago.iso8601 } }
+
+ it_behaves_like 'listing all milestones' do
+ let(:milestones) { [closed_milestone] }
+ end
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index e78ef2f7630..d755a4231da 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1109,6 +1109,66 @@ RSpec.describe API::Projects, feature_category: :projects do
end.not_to exceed_query_limit(control)
end
end
+
+ context 'rate limiting' do
+ let_it_be(:current_user) { create(:user) }
+
+ shared_examples_for 'does not log request and does not block the request' do
+ specify do
+ request
+ request
+
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ expect(Gitlab::AuthLogger).not_to receive(:error)
+ end
+ end
+
+ before do
+ stub_application_setting(projects_api_rate_limit_unauthenticated: 1)
+ end
+
+ context 'when the user is signed in' do
+ it_behaves_like 'does not log request and does not block the request' do
+ def request
+ get api('/projects', current_user)
+ end
+ end
+ end
+
+ context 'when the user is not signed in' do
+ let_it_be(:current_user) { nil }
+
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :projects_api_rate_limit_unauthenticated do
+ def request
+ get api('/projects', current_user)
+ end
+ end
+ end
+
+ context 'when the feature flag `rate_limit_for_unauthenticated_projects_api_access` is disabled' do
+ before do
+ stub_feature_flags(rate_limit_for_unauthenticated_projects_api_access: false)
+ end
+
+ context 'when the user is not signed in' do
+ let_it_be(:current_user) { nil }
+
+ it_behaves_like 'does not log request and does not block the request' do
+ def request
+ get api('/projects', current_user)
+ end
+ end
+ end
+
+ context 'when the user is signed in' do
+ it_behaves_like 'does not log request and does not block the request' do
+ def request
+ get api('/projects', current_user)
+ end
+ end
+ end
+ end
+ end
end
describe 'POST /projects' do
@@ -2006,19 +2066,6 @@ RSpec.describe API::Projects, feature_category: :projects do
end
end
- context 'with upload size enforcement disabled' do
- before do
- stub_feature_flags(enforce_max_attachment_size_upload_api: false)
- end
-
- it "returns 200" do
- post api("/projects/#{project.id}/uploads/authorize", user), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['MaximumSize']).to eq(1.gigabyte)
- end
- end
-
context 'with no Workhorse headers' do
it "returns 403" do
post api("/projects/#{project.id}/uploads/authorize", user)
@@ -2095,14 +2142,6 @@ RSpec.describe API::Projects, feature_category: :projects do
it_behaves_like 'capped upload attachments', true
end
-
- context 'with upload size enforcement disabled' do
- before do
- stub_feature_flags(enforce_max_attachment_size_upload_api: false)
- end
-
- it_behaves_like 'capped upload attachments', false
- end
end
describe "GET /projects/:id/groups" do
@@ -2228,9 +2267,9 @@ RSpec.describe API::Projects, feature_category: :projects do
end
describe 'GET /project/:id/share_locations' do
- let_it_be(:root_group) { create(:group, :public, name: 'root group') }
- let_it_be(:project_group1) { create(:group, :public, parent: root_group, name: 'group1') }
- let_it_be(:project_group2) { create(:group, :public, parent: root_group, name: 'group2') }
+ let_it_be(:root_group) { create(:group, :public, name: 'root group', path: 'root-group-path') }
+ let_it_be(:project_group1) { create(:group, :public, parent: root_group, name: 'group1', path: 'group-1-path') }
+ let_it_be(:project_group2) { create(:group, :public, parent: root_group, name: 'group2', path: 'group-2-path') }
let_it_be(:project) { create(:project, :private, group: project_group1) }
shared_examples_for 'successful groups response' do
@@ -2280,10 +2319,22 @@ RSpec.describe API::Projects, feature_category: :projects do
end
context 'when searching by group name' do
- let(:params) { { search: 'group1' } }
+ context 'searching by group name' do
+ it_behaves_like 'successful groups response' do
+ let(:params) { { search: 'group1' } }
+ let(:expected_groups) { [project_group1] }
+ end
+ end
- it_behaves_like 'successful groups response' do
- let(:expected_groups) { [project_group1] }
+ context 'searching by full group path' do
+ let_it_be(:project_group2_subgroup) do
+ create(:group, :public, parent: project_group2, name: 'subgroup', path: 'subgroup-path')
+ end
+
+ it_behaves_like 'successful groups response' do
+ let(:params) { { search: 'root-group-path/group-2-path/subgroup-path' } }
+ let(:expected_groups) { [project_group2_subgroup] }
+ end
end
end
end
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 8e8a25a8dc2..463893afd13 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -266,6 +266,15 @@ RSpec.describe API::ProtectedBranches, feature_category: :source_code_management
end.to change { protected_branch.reload.allow_force_push }.from(false).to(true)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ context 'when allow_force_push is not set' do
+ it 'responds with a bad request error' do
+ patch api(route, user), params: { allow_force_push: nil }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq 'allow_force_push is empty'
+ end
+ end
end
context 'when returned protected branch is invalid' do
diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb
index 978d4f72a4a..0b2641b062c 100644
--- a/spec/requests/api/pypi_packages_spec.rb
+++ b/spec/requests/api/pypi_packages_spec.rb
@@ -14,10 +14,18 @@ RSpec.describe API::PypiPackages, feature_category: :package_registry do
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
+ let(:snowplow_gitlab_standard_context) { snowplow_context }
let(:headers) { {} }
+ def snowplow_context(user_role: :developer)
+ if user_role == :anonymous
+ { project: project, namespace: project.namespace, property: 'i_package_pypi_user' }
+ else
+ { project: project, namespace: project.namespace, property: 'i_package_pypi_user', user: user }
+ end
+ end
+
context 'simple index API endpoint' do
let_it_be(:package) { create(:pypi_package, project: project) }
let_it_be(:package2) { create(:pypi_package, project: project) }
@@ -26,7 +34,6 @@ RSpec.describe API::PypiPackages, feature_category: :package_registry do
describe 'GET /api/v4/groups/:id/-/packages/pypi/simple' do
let(:url) { "/groups/#{group.id}/-/packages/pypi/simple" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
it_behaves_like 'pypi simple index API endpoint'
it_behaves_like 'rejects PyPI access with unknown group id'
@@ -82,13 +89,13 @@ RSpec.describe API::PypiPackages, feature_category: :package_registry do
context 'simple package API endpoint' do
let_it_be(:package) { create(:pypi_package, project: project) }
- let(:snowplow_gitlab_standard_context) { { project: nil, namespace: group, property: 'i_package_pypi_user' } }
subject { get api(url), headers: headers }
describe 'GET /api/v4/groups/:id/-/packages/pypi/simple/:package_name' do
let(:package_name) { package.name }
let(:url) { "/groups/#{group.id}/-/packages/pypi/simple/#{package_name}" }
+ let(:snowplow_context) { { project: nil, namespace: project.namespace, property: 'i_package_pypi_user' } }
it_behaves_like 'pypi simple API endpoint'
it_behaves_like 'rejects PyPI access with unknown group id'
@@ -126,7 +133,7 @@ RSpec.describe API::PypiPackages, feature_category: :package_registry do
describe 'GET /api/v4/projects/:id/packages/pypi/simple/:package_name' do
let(:package_name) { package.name }
let(:url) { "/projects/#{project.id}/packages/pypi/simple/#{package_name}" }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
+ let(:snowplow_context) { { project: project, namespace: project.namespace, property: 'i_package_pypi_user' } }
it_behaves_like 'pypi simple API endpoint'
it_behaves_like 'rejects PyPI access with unknown project id'
@@ -242,6 +249,13 @@ RSpec.describe API::PypiPackages, feature_category: :package_registry do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) do
+ if user_role == :anonymous || (visibility_level == :public && !user_token)
+ { project: project, namespace: project.namespace, property: 'i_package_pypi_user' }
+ else
+ { project: project, namespace: project.namespace, property: 'i_package_pypi_user', user: user }
+ end
+ end
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
@@ -379,6 +393,14 @@ RSpec.describe API::PypiPackages, feature_category: :package_registry do
let_it_be(:package_name) { 'Dummy-Package' }
let_it_be(:package) { create(:pypi_package, project: project, name: package_name, version: '1.0.0') }
+ let(:snowplow_gitlab_standard_context) do
+ if user_role == :anonymous || (visibility_level == :public && !user_token)
+ { project: project, namespace: project.namespace, property: 'i_package_pypi_user' }
+ else
+ { project: project, namespace: project.namespace, property: 'i_package_pypi_user', user: user }
+ end
+ end
+
subject { get api(url), headers: headers }
describe 'GET /api/v4/groups/:id/-/packages/pypi/files/:sha256/*file_identifier' do
diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb
index 462cc1e3b5d..4b388304621 100644
--- a/spec/requests/api/release/links_spec.rb
+++ b/spec/requests/api/release/links_spec.rb
@@ -377,6 +377,15 @@ RSpec.describe API::Release::Links, feature_category: :release_orchestration do
expect(response).to match_response_schema('release/link')
end
+ context 'when params are invalid' do
+ it 'returns 400 error' do
+ put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", maintainer),
+ params: params.merge(url: 'wrong_url')
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
context 'when using `direct_asset_path`' do
it 'updates the release link' do
put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", maintainer),
@@ -534,6 +543,21 @@ RSpec.describe API::Release::Links, feature_category: :release_orchestration do
end
end
+ context 'when destroy process fails' do
+ before do
+ allow_next_instance_of(::Releases::Links::DestroyService) do |service|
+ allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'error'))
+ end
+ end
+
+ it_behaves_like '400 response' do
+ let(:message) { 'error' }
+ let(:request) do
+ delete api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", maintainer)
+ end
+ end
+ end
+
context 'when there are no corresponding release link' do
let!(:release_link) {}
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 555ba2bc978..b146dda5030 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -236,7 +236,6 @@ RSpec.describe API::Repositories, feature_category: :source_code_management do
get api(route, current_user)
expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
- expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
diff --git a/spec/requests/api/resource_access_tokens_spec.rb b/spec/requests/api/resource_access_tokens_spec.rb
index 6a89e9a56df..9277fa18219 100644
--- a/spec/requests/api/resource_access_tokens_spec.rb
+++ b/spec/requests/api/resource_access_tokens_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe API::ResourceAccessTokens, feature_category: :authentication_and_authorization do
+RSpec.describe API::ResourceAccessTokens, feature_category: :system_access do
let_it_be(:user) { create(:user) }
let_it_be(:user_non_priviledged) { create(:user) }
diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb
index 34cf6033811..1774b43ccb3 100644
--- a/spec/requests/api/rubygem_packages_spec.rb
+++ b/spec/requests/api/rubygem_packages_spec.rb
@@ -8,6 +8,14 @@ RSpec.describe API::RubygemPackages, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:project) { create(:project) }
+ let(:tokens) do
+ {
+ personal_access_token: personal_access_token.token,
+ deploy_token: deploy_token.token,
+ job_token: job.token
+ }
+ end
+
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:user) { personal_access_token.user }
let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
@@ -15,14 +23,14 @@ RSpec.describe API::RubygemPackages, feature_category: :package_registry do
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:headers) { {} }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user, property: 'i_package_rubygems_user' } }
+ let(:snowplow_gitlab_standard_context) { snowplow_context }
- let(:tokens) do
- {
- personal_access_token: personal_access_token.token,
- deploy_token: deploy_token.token,
- job_token: job.token
- }
+ def snowplow_context(user_role: :developer)
+ if user_role == :anonymous
+ { project: project, namespace: project.namespace, property: 'i_package_rubygems_user' }
+ else
+ { project: project, namespace: project.namespace, property: 'i_package_rubygems_user', user: user }
+ end
end
shared_examples 'when feature flag is disabled' do
@@ -164,7 +172,13 @@ RSpec.describe API::RubygemPackages, feature_category: :package_registry do
with_them do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_rubygems_user' } }
+ let(:snowplow_gitlab_standard_context) do
+ if token_type == :deploy_token
+ snowplow_context.merge(user: deploy_token)
+ else
+ snowplow_context(user_role: user_role)
+ end
+ end
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 035f53db12e..eb0f3b3eaee 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -174,6 +174,23 @@ RSpec.describe API::Search, feature_category: :global_search do
end
end
+ context 'when there is a search error' do
+ let(:results) { instance_double('Gitlab::SearchResults', failed?: true, error: 'failed to parse query') }
+
+ before do
+ allow_next_instance_of(SearchService) do |service|
+ allow(service).to receive(:search_objects).and_return([])
+ allow(service).to receive(:search_results).and_return(results)
+ end
+ end
+
+ it 'returns 400 error' do
+ get api(endpoint, user), params: { scope: 'issues', search: 'expected to fail' }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
context 'with correct params' do
context 'for projects scope' do
before do
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 4d85849cff3..e91d777bfb0 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, feature_category: :not_owned do
+RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, feature_category: :shared do
let(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
@@ -66,6 +66,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
expect(json_response['jira_connect_application_key']).to eq(nil)
expect(json_response['jira_connect_proxy_url']).to eq(nil)
expect(json_response['user_defaults_to_private_profile']).to eq(false)
+ expect(json_response['default_syntax_highlighting_theme']).to eq(1)
+ expect(json_response['projects_api_rate_limit_unauthenticated']).to eq(400)
end
end
@@ -169,7 +171,9 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
jira_connect_proxy_url: 'http://example.com',
bulk_import_enabled: false,
allow_runner_registration_token: true,
- user_defaults_to_private_profile: true
+ user_defaults_to_private_profile: true,
+ default_syntax_highlighting_theme: 2,
+ projects_api_rate_limit_unauthenticated: 100
}
expect(response).to have_gitlab_http_status(:ok)
@@ -237,6 +241,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
expect(json_response['bulk_import_enabled']).to be(false)
expect(json_response['allow_runner_registration_token']).to be(true)
expect(json_response['user_defaults_to_private_profile']).to be(true)
+ expect(json_response['default_syntax_highlighting_theme']).to eq(2)
+ expect(json_response['projects_api_rate_limit_unauthenticated']).to be(100)
end
end
diff --git a/spec/requests/api/sidekiq_metrics_spec.rb b/spec/requests/api/sidekiq_metrics_spec.rb
index 1085df97cc7..32c4c323923 100644
--- a/spec/requests/api/sidekiq_metrics_spec.rb
+++ b/spec/requests/api/sidekiq_metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::SidekiqMetrics, feature_category: :not_owned do
+RSpec.describe API::SidekiqMetrics, feature_category: :shared do
let(:admin) { create(:user, :admin) }
describe 'GET sidekiq/*' do
diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb
index 2bd7cb027aa..f479ca25f3c 100644
--- a/spec/requests/api/terraform/modules/v1/packages_spec.rb
+++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb
@@ -415,12 +415,15 @@ RSpec.describe API::Terraform::Modules::V1::Packages, feature_category: :package
with_them do
let(:snowplow_gitlab_standard_context) do
- {
+ context = {
project: project,
- user: user_role == :anonymous ? nil : user,
namespace: project.namespace,
property: 'i_package_terraform_module_user'
}
+
+ context[:user] = user if user_role != :anonymous
+
+ context
end
before do
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index fd34345d814..c94643242c9 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
before do
stub_terraform_state_object_storage
+ stub_config(terraform_state: { enabled: true })
end
shared_examples 'endpoint with unique user tracking' do
@@ -51,7 +52,6 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
subject(:api_request) { request }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'terraform_state_api_request' }
let(:label) { 'redis_hll_counters.terraform.p_terraform_state_api_unique_users_monthly' }
@@ -82,6 +82,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
subject(:request) { get api(state_path), headers: auth_header }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
context 'without authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
@@ -194,6 +195,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
subject(:request) { post api(state_path), headers: auth_header, as: :json, params: params }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
context 'when terraform state with a given name is already present' do
context 'with maintainer permissions' do
@@ -372,6 +374,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
subject(:request) { delete api(state_path), headers: auth_header }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
shared_examples 'schedules the state for deletion' do
it 'returns empty body' do
@@ -553,6 +556,10 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
let(:lock_id) { 'irrelevant to this test, just needs to be present' }
end
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config' do
+ let(:lock_id) { '123.456' }
+ end
+
where(given_state_name: %w[test-state test.state test%2Ffoo])
with_them do
let(:state_name) { given_state_name }
diff --git a/spec/requests/api/terraform/state_version_spec.rb b/spec/requests/api/terraform/state_version_spec.rb
index 28abbb5749d..24b3ca94581 100644
--- a/spec/requests/api/terraform/state_version_spec.rb
+++ b/spec/requests/api/terraform/state_version_spec.rb
@@ -22,9 +22,15 @@ RSpec.describe API::Terraform::StateVersion, feature_category: :infrastructure_a
let(:version_serial) { version.version }
let(:state_version_path) { "/projects/#{project_id}/terraform/state/#{state_name}/versions/#{version_serial}" }
+ before do
+ stub_config(terraform_state: { enabled: true })
+ end
+
describe 'GET /projects/:id/terraform/state/:name/versions/:serial' do
subject(:request) { get api(state_version_path), headers: auth_header }
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
+
context 'with invalid authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
@@ -147,6 +153,8 @@ RSpec.describe API::Terraform::StateVersion, feature_category: :infrastructure_a
describe 'DELETE /projects/:id/terraform/state/:name/versions/:serial' do
subject(:request) { delete api(state_version_path), headers: auth_header }
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config', { success_status: :no_content }
+
context 'with invalid authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
diff --git a/spec/requests/api/unleash_spec.rb b/spec/requests/api/unleash_spec.rb
index 5daf7cd7b75..75b26b98228 100644
--- a/spec/requests/api/unleash_spec.rb
+++ b/spec/requests/api/unleash_spec.rb
@@ -88,6 +88,14 @@ RSpec.describe API::Unleash, feature_category: :feature_flags do
end
end
+ describe 'GET /feature_flags/unleash/:project_id/client/features', :use_clean_rails_redis_caching do
+ specify do
+ get api("/feature_flags/unleash/#{project_id}/client/features"), params: params, headers: headers
+
+ is_expected.to have_request_urgency(:medium)
+ end
+ end
+
%w(/feature_flags/unleash/:project_id/features /feature_flags/unleash/:project_id/client/features).each do |features_endpoint|
describe "GET #{features_endpoint}", :use_clean_rails_redis_caching do
let(:features_url) { features_endpoint.sub(':project_id', project_id.to_s) }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 34867b13db2..c924f529e11 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1288,7 +1288,7 @@ RSpec.describe API::Users, feature_category: :user_profile do
expect(json_response['message']['projects_limit'])
.to eq(['must be greater than or equal to 0'])
expect(json_response['message']['username'])
- .to eq([Gitlab::PathRegex.namespace_format_message])
+ .to match_array([Gitlab::PathRegex.namespace_format_message, Gitlab::Regex.oci_repository_path_regex_message])
end
it 'tracks weak password errors' do
@@ -1823,7 +1823,7 @@ RSpec.describe API::Users, feature_category: :user_profile do
expect(json_response['message']['projects_limit'])
.to eq(['must be greater than or equal to 0'])
expect(json_response['message']['username'])
- .to eq([Gitlab::PathRegex.namespace_format_message])
+ .to match_array([Gitlab::PathRegex.namespace_format_message, Gitlab::Regex.oci_repository_path_regex_message])
end
it 'returns 400 if provider is missing for identity update' do
@@ -4150,7 +4150,7 @@ RSpec.describe API::Users, feature_category: :user_profile do
set_user_status
expect(response).to have_gitlab_http_status(:success)
- expect(user_with_status.status).to be_nil
+ expect(user_with_status.reset.status).to be_nil
end
end
end
@@ -4178,7 +4178,7 @@ RSpec.describe API::Users, feature_category: :user_profile do
set_user_status
expect(response).to have_gitlab_http_status(:success)
- expect(user_with_status.status.clear_status_at).to be_nil
+ expect(user_with_status.reset.status.clear_status_at).to be_nil
end
end
@@ -4217,7 +4217,7 @@ RSpec.describe API::Users, feature_category: :user_profile do
set_user_status
expect(response).to have_gitlab_http_status(:success)
- expect(user_with_status.status).to be_nil
+ expect(user_with_status.reset.status).to be_nil
end
end
@@ -4229,7 +4229,7 @@ RSpec.describe API::Users, feature_category: :user_profile do
set_user_status
expect(response).to have_gitlab_http_status(:success)
- expect(user_with_status.status.clear_status_at).to be_nil
+ expect(user_with_status.reset.status.clear_status_at).to be_nil
end
end
end