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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-07-19 17:16:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-07-19 17:16:28 +0300
commite4384360a16dd9a19d4d2d25d0ef1f2b862ed2a6 (patch)
tree2fcdfa7dcdb9db8f5208b2562f4b4e803d671243 /spec/requests
parentffda4e7bcac36987f936b4ba515995a6698698f0 (diff)
Add latest changes from gitlab-org/gitlab@16-2-stable-eev16.2.0-rc42
Diffstat (limited to 'spec/requests')
-rw-r--r--spec/requests/admin/users_controller_spec.rb20
-rw-r--r--spec/requests/api/admin/instance_clusters_spec.rb2
-rw-r--r--spec/requests/api/admin/plan_limits_spec.rb10
-rw-r--r--spec/requests/api/ci/job_artifacts_spec.rb2
-rw-r--r--spec/requests/api/ci/pipeline_schedules_spec.rb18
-rw-r--r--spec/requests/api/ci/variables_spec.rb7
-rw-r--r--spec/requests/api/container_repositories_spec.rb2
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb29
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb39
-rw-r--r--spec/requests/api/deployments_spec.rb2
-rw-r--r--spec/requests/api/discussions_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb68
-rw-r--r--spec/requests/api/error_tracking/project_settings_spec.rb60
-rw-r--r--spec/requests/api/files_spec.rb8
-rw-r--r--spec/requests/api/graphql/boards/board_list_issues_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/boards/board_lists_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/inherited_ci_variables_spec.rb178
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb15
-rw-r--r--spec/requests/api/graphql/container_repository/container_repository_details_spec.rb2
-rw-r--r--spec/requests/api/graphql/gitlab_schema_spec.rb4
-rw-r--r--spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb104
-rw-r--r--spec/requests/api/graphql/metrics/dashboard_query_spec.rb114
-rw-r--r--spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb19
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb15
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule/create_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb)17
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule/delete_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb)4
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule/play_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb)4
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule/take_ownership_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb)0
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_schedule/update_spec.rb (renamed from spec/requests/api/graphql/mutations/ci/pipeline_schedule_update_spec.rb)44
-rw-r--r--spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb20
-rw-r--r--spec/requests/api/graphql/mutations/ci/runner/create_spec.rb32
-rw-r--r--spec/requests/api/graphql/mutations/issues/update_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/notes/update/note_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/snippets/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb85
-rw-r--r--spec/requests/api/graphql/project/alert_management/alerts_spec.rb1
-rw-r--r--spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/jobs_spec.rb31
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb36
-rw-r--r--spec/requests/api/graphql/user_query_spec.rb47
-rw-r--r--spec/requests/api/group_clusters_spec.rb2
-rw-r--r--spec/requests/api/group_export_spec.rb120
-rw-r--r--spec/requests/api/group_variables_spec.rb7
-rw-r--r--spec/requests/api/groups_spec.rb99
-rw-r--r--spec/requests/api/helpers_spec.rb6
-rw-r--r--spec/requests/api/import_github_spec.rb27
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb54
-rw-r--r--spec/requests/api/lint_spec.rb10
-rw-r--r--spec/requests/api/merge_requests_spec.rb73
-rw-r--r--spec/requests/api/ml_model_packages_spec.rb146
-rw-r--r--spec/requests/api/npm_group_packages_spec.rb30
-rw-r--r--spec/requests/api/npm_instance_packages_spec.rb27
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb99
-rw-r--r--spec/requests/api/project_attributes.yml3
-rw-r--r--spec/requests/api/project_clusters_spec.rb2
-rw-r--r--spec/requests/api/project_export_spec.rb124
-rw-r--r--spec/requests/api/project_hooks_spec.rb1
-rw-r--r--spec/requests/api/project_packages_spec.rb14
-rw-r--r--spec/requests/api/projects_spec.rb78
-rw-r--r--spec/requests/api/protected_branches_spec.rb13
-rw-r--r--spec/requests/api/protected_tags_spec.rb13
-rw-r--r--spec/requests/api/search_spec.rb12
-rw-r--r--spec/requests/api/settings_spec.rb105
-rw-r--r--spec/requests/api/statistics_spec.rb2
-rw-r--r--spec/requests/api/usage_data_spec.rb55
-rw-r--r--spec/requests/api/user_runners_spec.rb243
-rw-r--r--spec/requests/api/users_spec.rb165
-rw-r--r--spec/requests/git_http_spec.rb5
-rw-r--r--spec/requests/groups/observability_controller_spec.rb4
-rw-r--r--spec/requests/lfs_http_spec.rb2
-rw-r--r--spec/requests/openid_connect_spec.rb11
-rw-r--r--spec/requests/organizations/organizations_controller_spec.rb16
-rw-r--r--spec/requests/projects/alert_management_controller_spec.rb69
-rw-r--r--spec/requests/projects/incidents_controller_spec.rb116
-rw-r--r--spec/requests/projects/issues_controller_spec.rb7
-rw-r--r--spec/requests/projects/merge_requests/creations_spec.rb20
-rw-r--r--spec/requests/projects/merge_requests_controller_spec.rb1
-rw-r--r--spec/requests/projects/ml/candidates_controller_spec.rb23
-rw-r--r--spec/requests/projects/ml/experiments_controller_spec.rb27
-rw-r--r--spec/requests/projects/ml/models_controller_spec.rb67
-rw-r--r--spec/requests/projects/packages/package_files_controller_spec.rb2
-rw-r--r--spec/requests/projects/service_desk/custom_email_controller_spec.rb380
-rw-r--r--spec/requests/projects/service_desk_controller_spec.rb109
-rw-r--r--spec/requests/projects/tracing_controller_spec.rb68
-rw-r--r--spec/requests/search_controller_spec.rb6
-rw-r--r--spec/requests/users_controller_spec.rb28
-rw-r--r--spec/requests/verifies_with_email_spec.rb2
90 files changed, 2557 insertions, 999 deletions
diff --git a/spec/requests/admin/users_controller_spec.rb b/spec/requests/admin/users_controller_spec.rb
index 5344a2c2bb7..21cf8ab2c79 100644
--- a/spec/requests/admin/users_controller_spec.rb
+++ b/spec/requests/admin/users_controller_spec.rb
@@ -6,12 +6,12 @@ RSpec.describe Admin::UsersController, :enable_admin_mode, feature_category: :us
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
+ before do
+ sign_in(admin)
+ end
+
describe 'PUT #block' do
context 'when request format is :json' do
- before do
- sign_in(admin)
- end
-
subject(:request) { put block_admin_user_path(user, format: :json) }
context 'when user was blocked' do
@@ -39,4 +39,16 @@ RSpec.describe Admin::UsersController, :enable_admin_mode, feature_category: :us
end
end
end
+
+ describe 'PUT #unlock' do
+ before do
+ user.lock_access!
+ end
+
+ subject(:request) { put unlock_admin_user_path(user) }
+
+ it 'unlocks the user' do
+ expect { request }.to change { user.reload.access_locked? }.from(true).to(false)
+ end
+ end
end
diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb
index f2e62533b78..6fad020150c 100644
--- a/spec/requests/api/admin/instance_clusters_spec.rb
+++ b/spec/requests/api/admin/instance_clusters_spec.rb
@@ -363,7 +363,7 @@ RSpec.describe ::API::Admin::InstanceClusters, feature_category: :deployment_man
end
it 'returns validation error' do
- expect(json_response['message']['platform_kubernetes.base'].first).to eq(_('Cannot modify managed Kubernetes cluster'))
+ expect(json_response['message']['platform_kubernetes'].first).to eq(_('Cannot modify managed Kubernetes cluster'))
end
end
diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb
index cad1111b76b..97eb8a2b13f 100644
--- a/spec/requests/api/admin/plan_limits_spec.rb
+++ b/spec/requests/api/admin/plan_limits_spec.rb
@@ -26,6 +26,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d
expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size)
expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size)
expect(json_response['helm_max_file_size']).to eq(Plan.default.actual_limits.helm_max_file_size)
+ expect(json_response['limits_history']).to eq(Plan.default.actual_limits.limits_history)
expect(json_response['maven_max_file_size']).to eq(Plan.default.actual_limits.maven_max_file_size)
expect(json_response['npm_max_file_size']).to eq(Plan.default.actual_limits.npm_max_file_size)
expect(json_response['nuget_max_file_size']).to eq(Plan.default.actual_limits.nuget_max_file_size)
@@ -86,7 +87,9 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d
let(:params) { { 'plan_name': 'default' } }
end
- context 'as an admin user' do
+ context 'as an admin user', :freeze_time do
+ let(:current_timestamp) { Time.current.utc.to_i }
+
context 'correct params' do
it 'updates multiple plan limits', :aggregate_failures do
put api(path, admin, admin_mode: true), params: {
@@ -124,6 +127,11 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d
expect(json_response['enforcement_limit']).to eq(15)
expect(json_response['generic_packages_max_file_size']).to eq(20)
expect(json_response['helm_max_file_size']).to eq(25)
+ expect(json_response['limits_history']).to eq(
+ { "enforcement_limit" => [{ "user_id" => admin.id, "username" => admin.username, "timestamp" => current_timestamp, "value" => 15 }],
+ "notification_limit" => [{ "user_id" => admin.id, "username" => admin.username, "timestamp" => current_timestamp, "value" => 90 }],
+ "storage_size_limit" => [{ "user_id" => admin.id, "username" => admin.username, "timestamp" => current_timestamp, "value" => 80 }] }
+ )
expect(json_response['maven_max_file_size']).to eq(30)
expect(json_response['notification_limit']).to eq(90)
expect(json_response['npm_max_file_size']).to eq(40)
diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb
index 7cea744cdb9..6f4e7fd66ed 100644
--- a/spec/requests/api/ci/job_artifacts_spec.rb
+++ b/spec/requests/api/ci/job_artifacts_spec.rb
@@ -541,7 +541,7 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' =>
- %Q(attachment; filename="#{job_with_artifacts.artifacts_file.filename}"; filename*=UTF-8''#{job.artifacts_file.filename}) }
+ %(attachment; filename="#{job_with_artifacts.artifacts_file.filename}"; filename*=UTF-8''#{job.artifacts_file.filename}) }
end
it { expect(response).to have_gitlab_http_status(:ok) }
diff --git a/spec/requests/api/ci/pipeline_schedules_spec.rb b/spec/requests/api/ci/pipeline_schedules_spec.rb
index d760e4ddf28..d5f60e62b06 100644
--- a/spec/requests/api/ci/pipeline_schedules_spec.rb
+++ b/spec/requests/api/ci/pipeline_schedules_spec.rb
@@ -311,7 +311,8 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
end
end
- describe 'POST /projects/:id/pipeline_schedules' do
+ # Move this from `shared_context` to `describe` when `ci_refactoring_pipeline_schedule_create_service` is removed.
+ shared_context 'POST /projects/:id/pipeline_schedules' do # rubocop:disable RSpec/ContextWording
let(:params) { attributes_for(:ci_pipeline_schedule) }
context 'authenticated user with valid permissions' do
@@ -368,7 +369,8 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
end
end
- describe 'PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
+ # Move this from `shared_context` to `describe` when `ci_refactoring_pipeline_schedule_create_service` is removed.
+ shared_context 'PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
let(:pipeline_schedule) do
create(:ci_pipeline_schedule, project: project, owner: developer)
end
@@ -437,6 +439,18 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
end
end
+ it_behaves_like 'POST /projects/:id/pipeline_schedules'
+ it_behaves_like 'PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id'
+
+ context 'when the FF ci_refactoring_pipeline_schedule_create_service is disabled' do
+ before do
+ stub_feature_flags(ci_refactoring_pipeline_schedule_create_service: false)
+ end
+
+ it_behaves_like 'POST /projects/:id/pipeline_schedules'
+ it_behaves_like 'PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id'
+ end
+
describe 'POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do
let(:pipeline_schedule) do
create(:ci_pipeline_schedule, project: project, owner: developer)
diff --git a/spec/requests/api/ci/variables_spec.rb b/spec/requests/api/ci/variables_spec.rb
index e937c4c2b8f..a1446e1040e 100644
--- a/spec/requests/api/ci/variables_spec.rb
+++ b/spec/requests/api/ci/variables_spec.rb
@@ -48,6 +48,7 @@ RSpec.describe API::Ci::Variables, feature_category: :secrets_management do
expect(json_response['masked']).to eq(variable.masked?)
expect(json_response['raw']).to eq(variable.raw?)
expect(json_response['variable_type']).to eq('env_var')
+ expect(json_response['description']).to be_nil
end
it 'responds with 404 Not Found if requesting non-existing variable' do
@@ -140,7 +141,7 @@ RSpec.describe API::Ci::Variables, feature_category: :secrets_management do
it 'creates variable with optional attributes' do
expect do
- post api("/projects/#{project.id}/variables", user), params: { variable_type: 'file', key: 'TEST_VARIABLE_2', value: 'VALUE_2' }
+ post api("/projects/#{project.id}/variables", user), params: { variable_type: 'file', key: 'TEST_VARIABLE_2', value: 'VALUE_2', description: 'description' }
end.to change { project.variables.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
@@ -150,6 +151,7 @@ RSpec.describe API::Ci::Variables, feature_category: :secrets_management do
expect(json_response['masked']).to be_falsey
expect(json_response['raw']).to be_falsey
expect(json_response['variable_type']).to eq('file')
+ expect(json_response['description']).to eq('description')
end
it 'does not allow to duplicate variable key' do
@@ -226,7 +228,7 @@ RSpec.describe API::Ci::Variables, feature_category: :secrets_management do
initial_variable = project.variables.reload.first
value_before = initial_variable.value
- put api("/projects/#{project.id}/variables/#{variable.key}", user), params: { variable_type: 'file', value: 'VALUE_1_UP', protected: true }
+ put api("/projects/#{project.id}/variables/#{variable.key}", user), params: { variable_type: 'file', value: 'VALUE_1_UP', protected: true, description: 'updated' }
updated_variable = project.variables.reload.first
@@ -235,6 +237,7 @@ RSpec.describe API::Ci::Variables, feature_category: :secrets_management do
expect(updated_variable.value).to eq('VALUE_1_UP')
expect(updated_variable).to be_protected
expect(updated_variable.variable_type).to eq('file')
+ expect(updated_variable.description).to eq('updated')
end
it 'masks the new value when logging' do
diff --git a/spec/requests/api/container_repositories_spec.rb b/spec/requests/api/container_repositories_spec.rb
index 4c1e52df4fc..605fa0d92f6 100644
--- a/spec/requests/api/container_repositories_spec.rb
+++ b/spec/requests/api/container_repositories_spec.rb
@@ -119,7 +119,7 @@ RSpec.describe API::ContainerRepositories, feature_category: :container_registry
let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT + 3.months }
before do
- allow(::Gitlab).to receive(:com?).and_return(on_com)
+ allow(::Gitlab).to receive(:com_except_jh?).and_return(on_com)
repository.update_column(:created_at, created_at)
end
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index 9c726e5a5f7..25b99862100 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -6,48 +6,28 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
include WorkhorseHelpers
include_context 'Debian repository shared context', :group, false do
- shared_examples 'a Debian package tracking event' do |action|
- include_context 'Debian repository access', :public, :developer, :basic do
- let(:snowplow_gitlab_standard_context) do
- { project: nil, namespace: container, user: user, property: 'i_package_debian_user' }
- end
-
- it_behaves_like 'a package tracking event', described_class.name, action
- end
- end
-
- shared_examples 'not a Debian package tracking event' do
- include_context 'Debian repository access', :public, :developer, :basic do
- it_behaves_like 'not a package tracking event', described_class.name, /.*/
- end
- end
-
context 'with invalid parameter' do
let(:url) { "/groups/1/-/packages/debian/dists/with+space/InRelease" }
it_behaves_like 'Debian packages GET request', :bad_request, /^distribution is invalid$/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNATURE-----/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Codename: fixture-distribution\n$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
@@ -56,14 +36,12 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete Packages file/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages.gz' do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages.gz" }
it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
@@ -73,7 +51,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
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 index sha256 endpoint', /^Other SHA256$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/Sources' do
@@ -82,7 +59,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/Sources" }
it_behaves_like 'Debian packages index endpoint', /^Description: This is an incomplete Sources file$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
@@ -92,7 +68,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/by-hash/SHA256/#{target_sha256}" }
it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
@@ -101,14 +76,12 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete D-I Packages file/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages.gz' do
let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages.gz" }
it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
@@ -118,7 +91,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
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 index sha256 endpoint', /^Other SHA256$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
end
describe 'GET groups/:id/-/packages/debian/pool/:codename/:project_id/:letter/:package_name/:package_version/:file_name' do
@@ -139,7 +111,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
with_them do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body]
- it_behaves_like 'a Debian package tracking event', 'pull_package'
context 'for bumping last downloaded at' do
include_context 'Debian repository access', :public, :developer, :basic do
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index b1566860ffc..7f3f633a35c 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -7,22 +7,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
include WorkhorseHelpers
include_context 'Debian repository shared context', :project, false do
- shared_examples 'a Debian package tracking event' do |action|
- include_context 'Debian repository access', :public, :developer, :basic do
- let(:snowplow_gitlab_standard_context) do
- { project: container, namespace: container.namespace, user: user, property: 'i_package_debian_user' }
- end
-
- it_behaves_like 'a package tracking event', described_class.name, action
- end
- end
-
- shared_examples 'not a Debian package tracking event' do
- include_context 'Debian repository access', :public, :developer, :basic do
- it_behaves_like 'not a package tracking event', described_class.name, /.*/
- end
- end
-
shared_examples 'accept GET request on private project with access to package registry for everyone' do
include_context 'Debian repository access', :private, :anonymous, :basic do
before do
@@ -37,14 +21,12 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/1/packages/debian/dists/with+space/InRelease" }
it_behaves_like 'Debian packages GET request', :bad_request, /^distribution is invalid$/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNATURE-----/
- it_behaves_like 'not a Debian package tracking event'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -52,7 +34,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Codename: fixture-distribution\n$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -60,7 +41,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -70,7 +50,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete Packages file/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -78,7 +57,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages.gz" }
it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
@@ -88,7 +66,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
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 index sha256 endpoint', /^Other SHA256$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -98,7 +75,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/Sources" }
it_behaves_like 'Debian packages index endpoint', /^Description: This is an incomplete Sources file$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -109,7 +85,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/by-hash/SHA256/#{target_sha256}" }
it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -119,7 +94,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete D-I Packages file/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -127,7 +101,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages.gz" }
it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/
- it_behaves_like 'not a Debian package tracking event'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
@@ -137,7 +110,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
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 index sha256 endpoint', /^Other SHA256$/
- it_behaves_like 'a Debian package tracking event', 'list_package'
it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
@@ -159,7 +131,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
with_them do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body]
- it_behaves_like 'a Debian package tracking event', 'pull_package'
context 'for bumping last downloaded at' do
include_context 'Debian repository access', :public, :developer, :basic do
@@ -182,13 +153,11 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil
it_behaves_like 'Debian packages endpoint catching ObjectStorage::RemoteStoreError'
- it_behaves_like 'a Debian package tracking event', 'push_package'
context 'with codename and component' do
let(:extra_params) { { distribution: distribution.codename, component: 'main' } }
it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil
- it_behaves_like 'a Debian package tracking event', 'push_package'
end
context 'with codename and without component' do
@@ -197,8 +166,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
include_context 'Debian repository access', :public, :developer, :basic do
it_behaves_like 'Debian packages GET request', :bad_request, /component is missing/
end
-
- it_behaves_like 'not a Debian package tracking event'
end
end
@@ -209,8 +176,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
it_behaves_like "Debian packages upload request", :created, nil
end
- it_behaves_like 'a Debian package tracking event', 'push_package'
-
context 'with codename and component' do
let(:extra_params) { { distribution: distribution.codename, component: 'main' } }
@@ -218,8 +183,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
it_behaves_like "Debian packages upload request", :bad_request,
/^file_name Only debs, udebs and ddebs can be directly added to a distribution$/
end
-
- it_behaves_like 'not a Debian package tracking event'
end
end
@@ -227,7 +190,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:file_name) { 'sample_1.2.3~alpha2_amd64.changes' }
it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil
- it_behaves_like 'a Debian package tracking event', 'push_package'
end
end
@@ -237,7 +199,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/#{file_name}/authorize" }
it_behaves_like 'Debian packages write endpoint', 'upload authorize', :created, nil
- it_behaves_like 'not a Debian package tracking event'
end
end
end
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index d7056adfcb6..82ac2eed83d 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -424,7 +424,7 @@ RSpec.describe API::Deployments, feature_category: :continuous_delivery do
)
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['status']).to include(%Q{cannot transition via \"run\"})
+ expect(json_response['message']['status']).to include(%{cannot transition via \"run\"})
end
it 'links merge requests when the deployment status changes to success', :sidekiq_inline do
diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb
index c5126dbd1c2..a65dc6e0175 100644
--- a/spec/requests/api/discussions_spec.rb
+++ b/spec/requests/api/discussions_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe API::Discussions, feature_category: :team_planning do
end
context 'when noteable is a WorkItem' do
- let!(:work_item) { create(:work_item, :issue, project: project, author: user) }
+ let!(:work_item) { create(:work_item, project: project, author: user) }
let!(:work_item_note) { create(:discussion_note_on_issue, noteable: work_item, project: project, author: user) }
let(:parent) { project }
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index 9a435b3bce9..498e030da0b 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -31,6 +31,14 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(json_response.first).not_to have_key('last_deployment')
end
+ it 'returns 200 HTTP status when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: user)
+
+ get api("/projects/#{project.id}/environments"), params: { job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
context 'when filtering' do
let_it_be(:stopped_environment) { create(:environment, :stopped, project: project) }
@@ -132,6 +140,14 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(json_response['external']).to be nil
end
+ it 'returns 200 HTTP status when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: user)
+
+ post api("/projects/#{project.id}/environments"), params: { name: "mepmep", job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
it 'requires name to be passed' do
post api("/projects/#{project.id}/environments", user), params: { external_url: 'test.gitlab.com' }
@@ -173,6 +189,15 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(response).to have_gitlab_http_status(:ok)
end
+ it 'returns 200 HTTP status when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: user)
+
+ post api("/projects/#{project.id}/environments/stop_stale"),
+ params: { before: 1.week.ago.to_date.to_s, job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
it 'returns a 400 for bad input date' do
post api("/projects/#{project.id}/environments/stop_stale", user), params: { before: 1.day.ago.to_date.to_s }
@@ -229,6 +254,15 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(json_response['tier']).to eq('production')
end
+ it 'returns 200 HTTP status when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: user)
+
+ put api("/projects/#{project.id}/environments/#{environment.id}"),
+ params: { tier: 'production', job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
it "won't allow slug to be changed" do
slug = environment.slug
api_url = api("/projects/#{project.id}/environments/#{environment.id}", user)
@@ -261,6 +295,17 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(response).to have_gitlab_http_status(:no_content)
end
+ it 'returns 204 HTTP status when using JOB-TOKEN auth' do
+ environment.stop
+
+ job = create(:ci_build, :running, project: project, user: user)
+
+ delete api("/projects/#{project.id}/environments/#{environment.id}"),
+ params: { job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+
it 'returns a 404 for non existing id' do
delete api("/projects/#{project.id}/environments/#{non_existing_record_id}", user)
@@ -291,17 +336,23 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
context 'with a stoppable environment' do
before do
environment.update!(state: :available)
-
- post api("/projects/#{project.id}/environments/#{environment.id}/stop", user)
end
it 'returns a 200' do
+ post api("/projects/#{project.id}/environments/#{environment.id}/stop", user)
+
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/environment')
+ expect(environment.reload).to be_stopped
end
- it 'actually stops the environment' do
- expect(environment.reload).to be_stopped
+ it 'returns 200 HTTP status when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: user)
+
+ post api("/projects/#{project.id}/environments/#{environment.id}/stop"),
+ params: { job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:ok)
end
end
@@ -333,6 +384,15 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(response).to match_response_schema('public_api/v4/environment')
expect(json_response['last_deployment']).to be_present
end
+
+ it 'returns 200 HTTP status when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: user)
+
+ get api("/projects/#{project.id}/environments/#{environment.id}"),
+ params: { job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
context 'as non member' do
diff --git a/spec/requests/api/error_tracking/project_settings_spec.rb b/spec/requests/api/error_tracking/project_settings_spec.rb
index bde90627983..93ad0233ca3 100644
--- a/spec/requests/api/error_tracking/project_settings_spec.rb
+++ b/spec/requests/api/error_tracking/project_settings_spec.rb
@@ -3,10 +3,20 @@
require 'spec_helper'
RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tracking do
- let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:setting) { create(:project_error_tracking_setting, project: project) }
let_it_be(:project_without_setting) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:non_member) { create(:user) }
+ let(:user) { maintainer }
+
+ before_all do
+ project.add_developer(developer)
+ project.add_maintainer(maintainer)
+ project_without_setting.add_developer(developer)
+ project_without_setting.add_maintainer(maintainer)
+ end
shared_examples 'returns project settings' do
it 'returns correct project settings' do
@@ -108,10 +118,6 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'when authenticated as maintainer' do
- before do
- project.add_maintainer(user)
- end
-
context 'with integrated_error_tracking feature enabled' do
it_behaves_like 'returns project settings'
end
@@ -179,10 +185,6 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
context 'without a project setting' do
let(:project) { project_without_setting }
- before do
- project.add_maintainer(user)
- end
-
it_behaves_like 'returns no project settings'
end
@@ -208,14 +210,14 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'when authenticated as developer' do
- before do
- project.add_developer(user)
- end
+ let(:user) { developer }
it_behaves_like 'returns 403'
end
context 'when authenticated as non-member' do
+ let(:user) { non_member }
+
it_behaves_like 'returns 404'
end
@@ -232,10 +234,6 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context 'when authenticated as maintainer' do
- before do
- project.add_maintainer(user)
- end
-
it_behaves_like 'returns project settings'
context 'when integrated_error_tracking feature disabled' do
@@ -250,22 +248,18 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
context 'without a project setting' do
let(:project) { project_without_setting }
- before do
- project.add_maintainer(user)
- end
-
it_behaves_like 'returns no project settings'
end
context 'when authenticated as developer' do
- before do
- project.add_developer(user)
- end
+ let(:user) { developer }
it_behaves_like 'returns 403'
end
context 'when authenticated as non-member' do
+ let(:user) { non_member }
+
it_behaves_like 'returns 404'
end
@@ -287,14 +281,8 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
context 'when authenticated' do
context 'as maintainer' do
- before do
- project.add_maintainer(user)
- end
-
context "when integrated" do
context "with existing setting" do
- let(:project) { setting.project }
- let(:setting) { create(:project_error_tracking_setting, :integrated) }
let(:active) { false }
it "updates a setting" do
@@ -302,13 +290,7 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
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
- )
+ expect(json_response).to include("integrated" => true)
end
end
@@ -366,14 +348,14 @@ RSpec.describe API::ErrorTracking::ProjectSettings, feature_category: :error_tra
end
context "as developer" do
- before do
- project.add_developer(user)
- end
+ let(:user) { developer }
it_behaves_like 'returns 403'
end
context 'as non-member' do
+ let(:user) { non_member }
+
it_behaves_like 'returns 404'
end
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index ed84e3e5f48..ea341703301 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -141,11 +141,9 @@ 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_next_instance_of(Gitlab::Cache::Client) do |instance|
+ expect(instance).to receive(:fetch).with(anything, nil, { cache_identifier: 'API::Files#content_sha', backing_resource: :gitaly }).and_call_original
+ end
expect(Rails.cache.fetch("blob_content_sha256:#{project.full_path}:#{response.headers['X-Gitlab-Blob-Id']}"))
.to eq(content_sha256)
diff --git a/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb b/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
index 2775c3d4c5a..86e2b288890 100644
--- a/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe 'get board lists', feature_category: :team_planning do
let(:confidential) { false }
let(:board_parent_type) { board_parent.class.to_s.downcase }
let(:board_data) { graphql_data[board_parent_type]['boards']['nodes'][0] }
- let(:lists_data) { board_data['lists']['nodes'][0] }
+ let(:lists_data) { board_data['lists']['nodes'][1] }
let(:issues_data) { lists_data['issues']['nodes'] }
let(:issue_params) { { filters: { label_name: label2.title, confidential: confidential }, first: 3 } }
diff --git a/spec/requests/api/graphql/boards/board_lists_query_spec.rb b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
index 2f23e93e2c6..c1ad9bc8728 100644
--- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
@@ -90,7 +90,7 @@ RSpec.describe 'get board lists', feature_category: :team_planning do
context 'when using default sorting' do
let!(:label_list) { create(:list, board: board, label: label, position: 10) }
let!(:label_list2) { create(:list, board: board, label: label2, position: 2) }
- let!(:backlog_list) { create(:backlog_list, board: board) }
+ let(:backlog_list) { board.lists.find_by(list_type: :backlog) }
let(:closed_list) { board.lists.find_by(list_type: :closed) }
let(:lists) { [backlog_list, label_list2, label_list, closed_list] }
diff --git a/spec/requests/api/graphql/ci/inherited_ci_variables_spec.rb b/spec/requests/api/graphql/ci/inherited_ci_variables_spec.rb
index 3b4014c178c..defddc64851 100644
--- a/spec/requests/api/graphql/ci/inherited_ci_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/inherited_ci_variables_spec.rb
@@ -12,9 +12,15 @@ RSpec.describe 'Query.project(fullPath).inheritedCiVariables', feature_category:
let(:query) do
%(
- query {
+ query($limit: Int, $sort: CiGroupVariablesSort) {
project(fullPath: "#{project.full_path}") {
- inheritedCiVariables {
+ inheritedCiVariables(first: $limit, sort: $sort) {
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+ }
nodes {
id
key
@@ -32,6 +38,34 @@ RSpec.describe 'Query.project(fullPath).inheritedCiVariables', feature_category:
)
end
+ let(:expected_var_b) do
+ {
+ 'id' => subgroup_var.to_global_id.to_s,
+ 'key' => 'SUBGROUP_VAR_B',
+ 'environmentScope' => '*',
+ 'groupName' => subgroup.name,
+ 'groupCiCdSettingsPath' => subgroup_var.group_ci_cd_settings_path,
+ 'masked' => true,
+ 'protected' => false,
+ 'raw' => false,
+ 'variableType' => 'FILE'
+ }
+ end
+
+ let(:expected_var_a) do
+ {
+ 'id' => group_var.to_global_id.to_s,
+ 'key' => 'GROUP_VAR_A',
+ 'environmentScope' => 'production',
+ 'groupName' => group.name,
+ 'groupCiCdSettingsPath' => group_var.group_ci_cd_settings_path,
+ 'masked' => false,
+ 'protected' => true,
+ 'raw' => true,
+ 'variableType' => 'ENV_VAR'
+ }
+ end
+
def create_variables
create(:ci_group_variable, group: group)
create(:ci_group_variable, group: subgroup)
@@ -50,45 +84,115 @@ RSpec.describe 'Query.project(fullPath).inheritedCiVariables', feature_category:
end
context 'when user is a project maintainer' do
+ let!(:group_var) do
+ create(:ci_group_variable, group: group, key: 'GROUP_VAR_A',
+ environment_scope: 'production', masked: false, protected: true, raw: true, created_at: 1.day.ago)
+ end
+
+ let!(:subgroup_var) do
+ create(:ci_group_variable, group: subgroup, key: 'SUBGROUP_VAR_B',
+ masked: true, protected: false, raw: false, variable_type: 'file')
+ end
+
before do
project.add_maintainer(user)
end
it "returns the project's CI variables inherited from its parent group and ancestors" do
- group_var = create(:ci_group_variable, group: group, key: 'GROUP_VAR_A',
- environment_scope: 'production', masked: false, protected: true, raw: true)
-
- subgroup_var = create(:ci_group_variable, group: subgroup, key: 'SUBGROUP_VAR_B',
- masked: true, protected: false, raw: false, variable_type: 'file')
-
post_graphql(query, current_user: user)
- expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to eq([
- {
- 'id' => group_var.to_global_id.to_s,
- 'key' => 'GROUP_VAR_A',
- 'environmentScope' => 'production',
- 'groupName' => group.name,
- 'groupCiCdSettingsPath' => group_var.group_ci_cd_settings_path,
- 'masked' => false,
- 'protected' => true,
- 'raw' => true,
- 'variableType' => 'ENV_VAR'
- },
- {
- 'id' => subgroup_var.to_global_id.to_s,
- 'key' => 'SUBGROUP_VAR_B',
- 'environmentScope' => '*',
- 'groupName' => subgroup.name,
- 'groupCiCdSettingsPath' => subgroup_var.group_ci_cd_settings_path,
- 'masked' => true,
- 'protected' => false,
- 'raw' => false,
- 'variableType' => 'FILE'
- }
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to match_array([
+ expected_var_b, expected_var_a
])
end
+ context 'when limiting the number of results' do
+ it 'returns pagination information' do
+ post_graphql(query, current_user: user, variables: { limit: 1 })
+
+ expect(has_next_page).to be_truthy
+ expect(has_prev_page).to be_falsey
+
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to match_array([
+ expected_var_b
+ ])
+ end
+ end
+
+ describe 'sorting behaviour' do
+ before do
+ post_graphql(query, current_user: user, variables: { sort: sort })
+ end
+
+ shared_examples_for 'unexpected sort parameter' do
+ it 'raises a NoData exception' do
+ expect { graphql_data }.to raise_error(GraphqlHelpers::NoData)
+ end
+ end
+
+ context 'with sort by created_at ascenidng' do
+ let(:sort) { 'CREATED_ASC' }
+
+ it 'returns variables ordered by created_at in ascending order' do
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to eq([
+ expected_var_a, expected_var_b
+ ])
+ end
+ end
+
+ context 'with not existing sort parameter' do
+ let(:sort) { 'WRONG' }
+
+ it_behaves_like 'unexpected sort parameter'
+ end
+
+ context 'with empty sort parameter' do
+ let(:sort) { '' }
+
+ it_behaves_like 'unexpected sort parameter'
+ end
+
+ context 'with no sort parameter' do
+ let(:sort) { nil }
+
+ it 'returns variables by default in descending order by created_at' do
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to eq([
+ expected_var_b, expected_var_a
+ ])
+ end
+ end
+
+ context 'with sort by created_at descending' do
+ let(:sort) { 'CREATED_DESC' }
+
+ it 'returns variables ordered by created_at in descending order' do
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to eq([
+ expected_var_b, expected_var_a
+ ])
+ end
+ end
+
+ context 'with sort by key ascending' do
+ let(:sort) { 'KEY_ASC' }
+
+ it 'returns variables ordered by key in ascending order' do
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to eq([
+ expected_var_a, expected_var_b
+ ])
+ end
+ end
+
+ context 'with sort by key descending' do
+ let(:sort) { 'KEY_DESC' }
+
+ it 'returns variables ordered by key in descending order' do
+ expect(graphql_data.dig('project', 'inheritedCiVariables', 'nodes')).to eq([
+ expected_var_b, expected_var_a
+ ])
+ end
+ end
+ end
+
it 'avoids N+1 database queries' do
create_variables
@@ -105,4 +209,16 @@ RSpec.describe 'Query.project(fullPath).inheritedCiVariables', feature_category:
expect(multi).not_to exceed_query_limit(baseline)
end
end
+
+ def pagination_info
+ graphql_data_at('project', 'inheritedCiVariables', 'pageInfo')
+ end
+
+ def has_next_page
+ pagination_info['hasNextPage']
+ end
+
+ def has_prev_page
+ pagination_info['hasPreviousPage']
+ end
end
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 63a657f3962..6acd705c982 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -272,12 +272,13 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
let_it_be(:build1) { create(:ci_build, :running, runner: active_project_runner, pipeline: pipeline1) }
let_it_be(:build2) { create(:ci_build, :running, runner: active_project_runner, pipeline: pipeline2) }
- let(:runner_query_fragment) { 'id jobCount' }
let(:query) do
%(
query {
- runner1: runner(id: "#{active_project_runner.to_global_id}") { #{runner_query_fragment} }
- runner2: runner(id: "#{inactive_instance_runner.to_global_id}") { #{runner_query_fragment} }
+ runner1: runner(id: "#{active_project_runner.to_global_id}") { id jobCount(statuses: [RUNNING]) }
+ runner2: runner(id: "#{active_project_runner.to_global_id}") { id jobCount(statuses: FAILED) }
+ runner3: runner(id: "#{active_project_runner.to_global_id}") { id jobCount }
+ runner4: runner(id: "#{inactive_instance_runner.to_global_id}") { id jobCount }
}
)
end
@@ -287,7 +288,9 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
expect(graphql_data).to match a_hash_including(
'runner1' => a_graphql_entity_for(active_project_runner, job_count: 2),
- 'runner2' => a_graphql_entity_for(inactive_instance_runner, job_count: 0)
+ 'runner2' => a_graphql_entity_for(active_project_runner, job_count: 0),
+ 'runner3' => a_graphql_entity_for(active_project_runner, job_count: 2),
+ 'runner4' => a_graphql_entity_for(inactive_instance_runner, job_count: 0)
)
end
@@ -301,7 +304,9 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
expect(graphql_data).to match a_hash_including(
'runner1' => a_graphql_entity_for(active_project_runner, job_count: 1),
- 'runner2' => a_graphql_entity_for(inactive_instance_runner, job_count: 0)
+ 'runner2' => a_graphql_entity_for(active_project_runner, job_count: 0),
+ 'runner3' => a_graphql_entity_for(active_project_runner, job_count: 1),
+ 'runner4' => a_graphql_entity_for(inactive_instance_runner, job_count: 0)
)
end
end
diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
index 88f63fd59d7..118a11851dd 100644
--- a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
+++ b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
@@ -241,7 +241,7 @@ RSpec.describe 'container repository details', feature_category: :container_regi
end
before do
- allow(::Gitlab).to receive(:com?).and_return(on_com)
+ allow(::Gitlab).to receive(:com_except_jh?).and_return(on_com)
container_repository.update_column(:created_at, created_at)
end
diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb
index c5286b93251..ad21006f99a 100644
--- a/spec/requests/api/graphql/gitlab_schema_spec.rb
+++ b/spec/requests/api/graphql/gitlab_schema_spec.rb
@@ -264,8 +264,8 @@ RSpec.describe 'GitlabSchema configurations', feature_category: :integrations do
let(:headers) { {} }
before do
- allow(GitlabSchema).to receive(:execute).and_wrap_original do |method, *args|
- mock_schema.execute(*args)
+ allow(GitlabSchema).to receive(:execute).and_wrap_original do |method, *args, **kwargs|
+ mock_schema.execute(*args, **kwargs)
end
end
diff --git a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
deleted file mode 100644
index 143bc1672f8..00000000000
--- a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Getting Metrics Dashboard Annotations', feature_category: :metrics do
- include GraphqlHelpers
-
- let_it_be(:project) { create(:project) }
- let_it_be(:environment) { create(:environment, project: project) }
- let_it_be(:current_user) { create(:user) }
- let_it_be(:path) { 'config/prometheus/common_metrics.yml' }
- let_it_be(:from) { "2020-04-01T03:29:25Z" }
- let_it_be(:to) { Time.zone.now.advance(minutes: 5) }
- let_it_be(:annotation) { create(:metrics_dashboard_annotation, environment: environment, dashboard_path: path) }
- let_it_be(:annotation_for_different_env) { create(:metrics_dashboard_annotation, dashboard_path: path) }
- let_it_be(:annotation_for_different_dashboard) { create(:metrics_dashboard_annotation, environment: environment, dashboard_path: ".gitlab/dashboards/test.yml") }
- let_it_be(:to_old_annotation) do
- create(:metrics_dashboard_annotation, environment: environment, starting_at: Time.parse(from).advance(minutes: -5), dashboard_path: path)
- end
-
- let_it_be(:to_new_annotation) do
- create(:metrics_dashboard_annotation, environment: environment, starting_at: to.advance(minutes: 5), dashboard_path: path)
- end
-
- let(:remove_monitor_metrics) { false }
- let(:args) { "from: \"#{from}\", to: \"#{to}\"" }
- let(:fields) do
- <<~QUERY
- #{all_graphql_fields_for('MetricsDashboardAnnotation'.classify)}
- QUERY
- end
-
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- environments(name: "#{environment.name}") {
- nodes {
- metricsDashboard(path: "#{path}") {
- annotations(#{args}) {
- nodes {
- #{fields}
- }
- }
- }
- }
- }
- }
- }
- )
- end
-
- before do
- stub_feature_flags(remove_monitor_metrics: remove_monitor_metrics)
- project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
- end
-
- it_behaves_like 'a working graphql query'
-
- it 'returns annotations' do
- annotations = graphql_data.dig('project', 'environments', 'nodes')[0].dig('metricsDashboard', 'annotations', 'nodes')
-
- expect(annotations).to match_array [{
- "description" => annotation.description,
- "id" => annotation.to_global_id.to_s,
- "panelId" => annotation.panel_xid,
- "startingAt" => annotation.starting_at.iso8601,
- "endingAt" => nil
- }]
- end
-
- context 'arguments' do
- context 'from is missing' do
- let(:args) { "to: \"#{from}\"" }
-
- it 'returns error' do
- post_graphql(query, current_user: current_user)
-
- expect(graphql_errors[0]).to include("message" => "Field 'annotations' is missing required arguments: from")
- end
- end
-
- context 'to is missing' do
- let(:args) { "from: \"#{from}\"" }
-
- it_behaves_like 'a working graphql query'
- end
- end
-
- context 'when metrics dashboard feature is unavailable' do
- let(:remove_monitor_metrics) { true }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns nil' do
- annotations = graphql_data.dig(
- 'project', 'environments', 'nodes', 0, 'metricsDashboard', 'annotations'
- )
-
- expect(annotations).to be_nil
- end
- end
-end
diff --git a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
deleted file mode 100644
index b7d9b59f5fe..00000000000
--- a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Getting Metrics Dashboard', feature_category: :metrics do
- include GraphqlHelpers
-
- let_it_be(:current_user) { create(:user) }
-
- let(:project) { create(:project) }
- let(:environment) { create(:environment, project: project) }
-
- let(:query) do
- graphql_query_for(
- 'project', { 'fullPath' => project.full_path },
- query_graphql_field(
- :environments, { 'name' => environment.name },
- query_graphql_field(
- :nodes, nil,
- query_graphql_field(
- :metricsDashboard, { 'path' => path },
- all_graphql_fields_for('MetricsDashboard'.classify)
- )
- )
- )
- )
- end
-
- context 'for anonymous user' do
- before do
- post_graphql(query, current_user: current_user)
- end
-
- context 'requested dashboard is available' do
- let(:path) { 'config/prometheus/common_metrics.yml' }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes')
-
- expect(dashboard).to be_nil
- end
- end
- end
-
- context 'for user with developer access' do
- let(:remove_monitor_metrics) { false }
-
- before do
- stub_feature_flags(remove_monitor_metrics: remove_monitor_metrics)
- project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
- end
-
- context 'requested dashboard is available' do
- let(:path) { 'config/prometheus/common_metrics.yml' }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil)
- end
-
- context 'invalid dashboard' do
- let(:path) { '.gitlab/dashboards/metrics.yml' }
- let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) }
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"])
- end
- end
-
- context 'empty dashboard' do
- let(:path) { '.gitlab/dashboards/metrics.yml' }
- let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) }
-
- it 'returns metrics dashboard' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"])
- end
- end
-
- context 'metrics dashboard feature is unavailable' do
- let(:remove_monitor_metrics) { true }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to be_nil
- end
- end
- end
-
- context 'requested dashboard can not be found' do
- let(:path) { 'config/prometheus/i_am_not_here.yml' }
-
- it_behaves_like 'a working graphql query'
-
- it 'returns nil' do
- dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard')
-
- expect(dashboard).to be_nil
- end
- end
- end
-end
diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
index 3dee7f50af3..ec94760e3f0 100644
--- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb
@@ -8,11 +8,13 @@ RSpec.describe 'Creating a new Prometheus Integration', feature_category: :incid
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
+ let(:api_url) { 'https://prometheus-url.com' }
+
let(:variables) do
{
project_path: project.full_path,
active: false,
- api_url: 'https://prometheus-url.com'
+ api_url: api_url
}
end
@@ -56,7 +58,20 @@ RSpec.describe 'Creating a new Prometheus Integration', feature_category: :incid
expect(integration_response['apiUrl']).to eq(new_integration.api_url)
end
- [:project_path, :active, :api_url].each do |argument|
+ context 'without api url' do
+ let(:api_url) { nil }
+
+ it 'creates a new integration' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ integration_response = mutation_response['integration']
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(integration_response['apiUrl']).to be_nil
+ end
+ end
+
+ [:project_path, :active].each do |argument|
context "without required argument #{argument}" do
before do
variables.delete(argument)
diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb
index 8791d793cb4..947e7dbcb37 100644
--- a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb
@@ -53,7 +53,6 @@ RSpec.describe 'CiJobTokenScopeAddProject', feature_category: :continuous_integr
before do
target_project.add_developer(current_user)
- stub_feature_flags(frozen_outbound_job_token_scopes_override: false)
end
it 'adds the target project to the inbound job token scope' do
@@ -64,20 +63,6 @@ RSpec.describe 'CiJobTokenScopeAddProject', feature_category: :continuous_integr
end.to change { Ci::JobToken::ProjectScopeLink.inbound.count }.by(1)
end
- context 'when FF frozen_outbound_job_token_scopes is disabled' do
- before do
- stub_feature_flags(frozen_outbound_job_token_scopes: false)
- end
-
- it 'adds the target project to the outbound job token scope' do
- expect do
- post_graphql_mutation(mutation, current_user: current_user)
- expect(response).to have_gitlab_http_status(:success)
- expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty
- end.to change { Ci::JobToken::ProjectScopeLink.outbound.count }.by(1)
- end
- end
-
context 'when invalid target project is provided' do
before do
variables[:target_project_path] = 'unknown/project'
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/create_spec.rb
index 4a45d255d99..0d5e5f5d2fb 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'PipelineSchedulecreate' do
+RSpec.describe 'PipelineSchedulecreate', feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
@@ -68,8 +68,9 @@ RSpec.describe 'PipelineSchedulecreate' do
end
end
- context 'when authorized' do
- before do
+ # Move this from `shared_context` to `context` when `ci_refactoring_pipeline_schedule_create_service` is removed.
+ shared_context 'when authorized' do # rubocop:disable RSpec/ContextWording
+ before_all do
project.add_developer(user)
end
@@ -148,4 +149,14 @@ RSpec.describe 'PipelineSchedulecreate' do
end
end
end
+
+ it_behaves_like 'when authorized'
+
+ context 'when the FF ci_refactoring_pipeline_schedule_create_service is disabled' do
+ before do
+ stub_feature_flags(ci_refactoring_pipeline_schedule_create_service: false)
+ end
+
+ it_behaves_like 'when authorized'
+ end
end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/delete_spec.rb
index b846ff0aec8..e79395bb52c 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/delete_spec.rb
@@ -30,13 +30,13 @@ RSpec.describe 'PipelineScheduleDelete', feature_category: :continuous_integrati
expect(graphql_errors[0]['message'])
.to eq(
"The resource that you are attempting to access does not exist " \
- "or you don't have permission to perform this action"
+ "or you don't have permission to perform this action"
)
end
end
context 'when authorized' do
- before do
+ before_all do
project.add_maintainer(user)
end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/play_spec.rb
index 492c6946c99..55ecf8f287e 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/play_spec.rb
@@ -37,13 +37,13 @@ RSpec.describe 'PipelineSchedulePlay', feature_category: :continuous_integration
expect(graphql_errors[0]['message'])
.to eq(
"The resource that you are attempting to access does not exist " \
- "or you don't have permission to perform this action"
+ "or you don't have permission to perform this action"
)
end
end
context 'when authorized', :sidekiq_inline do
- before do
+ before_all do
project.add_maintainer(user)
pipeline_schedule.update_columns(next_run_at: 2.hours.ago)
end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/take_ownership_spec.rb
index 2d1f1565a73..2d1f1565a73 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/take_ownership_spec.rb
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_update_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/update_spec.rb
index c1da231a4a6..ec1595f393f 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_update_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule/update_spec.rb
@@ -9,6 +9,14 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
+ let_it_be(:variable_one) do
+ create(:ci_pipeline_schedule_variable, key: 'foo', value: 'foovalue', pipeline_schedule: pipeline_schedule)
+ end
+
+ let_it_be(:variable_two) do
+ create(:ci_pipeline_schedule_variable, key: 'bar', value: 'barvalue', pipeline_schedule: pipeline_schedule)
+ end
+
let(:mutation) do
variables = {
id: pipeline_schedule.to_global_id.to_s,
@@ -30,6 +38,7 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
nodes {
key
value
+ variableType
}
}
}
@@ -55,7 +64,7 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
end
context 'when authorized' do
- before do
+ before_all do
project.add_developer(user)
end
@@ -88,8 +97,37 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
expect(mutation_response['pipelineSchedule']['refForDisplay']).to eq(pipeline_schedule_parameters[:ref])
- expect(mutation_response['pipelineSchedule']['variables']['nodes'][0]['key']).to eq('AAA')
- expect(mutation_response['pipelineSchedule']['variables']['nodes'][0]['value']).to eq('AAA123')
+ expect(mutation_response['pipelineSchedule']['variables']['nodes'][2]['key']).to eq('AAA')
+ expect(mutation_response['pipelineSchedule']['variables']['nodes'][2]['value']).to eq('AAA123')
+ end
+ end
+
+ context 'when updating and removing variables' do
+ let(:pipeline_schedule_parameters) do
+ {
+ variables: [
+ { key: 'ABC', value: "ABC123", variableType: 'ENV_VAR', destroy: false },
+ { id: variable_one.to_global_id.to_s,
+ key: 'foo', value: "foovalue",
+ variableType: 'ENV_VAR',
+ destroy: true },
+ { id: variable_two.to_global_id.to_s, key: 'newbar', value: "newbarvalue", variableType: 'ENV_VAR' }
+ ]
+ }
+ end
+
+ it 'processes variables correctly' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+
+ expect(mutation_response['pipelineSchedule']['variables']['nodes'])
+ .to match_array(
+ [
+ { "key" => 'newbar', "value" => 'newbarvalue', "variableType" => 'ENV_VAR' },
+ { "key" => 'ABC', "value" => "ABC123", "variableType" => 'ENV_VAR' }
+ ]
+ )
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 fd92ed198e7..6e101d07b9f 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
@@ -5,10 +5,6 @@ require 'spec_helper'
RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integration do
include GraphqlHelpers
- before do
- stub_feature_flags(frozen_outbound_job_token_scopes_override: false)
- end
-
let_it_be(:project) do
create(:project,
keep_latest_artifact: true,
@@ -101,22 +97,6 @@ RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integr
end
end
- context 'when FF frozen_outbound_job_token_scopes is disabled' do
- before do
- stub_feature_flags(frozen_outbound_job_token_scopes: false)
- end
-
- it 'allows setting job_token_scope_enabled to true' do
- project.update!(ci_outbound_job_token_scope_enabled: true)
- post_graphql_mutation(mutation, current_user: user)
-
- project.reload
-
- expect(response).to have_gitlab_http_status(:success)
- expect(project.ci_outbound_job_token_scope_enabled).to eq(false)
- end
- end
-
it 'does not update job_token_scope_enabled if not specified' do
variables.except!(:job_token_scope_enabled)
diff --git a/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb b/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb
index 1658c277ed0..b697b9f73b7 100644
--- a/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/runner/create_spec.rb
@@ -95,18 +95,6 @@ RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
end
end
- shared_context 'when :create_runner_workflow_for_namespace feature flag is disabled' do
- before do
- stub_feature_flags(create_runner_workflow_for_namespace: [other_group])
- end
-
- it 'returns an error' do
- post_graphql_mutation(mutation, current_user: current_user)
-
- expect_graphql_errors_to_include('`create_runner_workflow_for_namespace` feature flag is disabled.')
- end
- end
-
shared_examples 'when runner is created successfully' do
it do
expected_args = { user: current_user, params: anything }
@@ -139,18 +127,6 @@ RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
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_to_include('`create_runner_workflow_for_admin` feature flag is disabled.')
- end
- end
-
it_behaves_like 'when runner is created successfully'
it_behaves_like 'when model is invalid returns error'
end
@@ -164,17 +140,12 @@ RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
}
end
- before do
- stub_feature_flags(create_runner_workflow_for_namespace: [group])
- end
-
it_behaves_like 'when user does not have permissions'
context 'when user has permissions' do
context 'when user is group owner' do
let(:current_user) { group_owner }
- it_behaves_like 'when :create_runner_workflow_for_namespace feature flag is disabled'
it_behaves_like 'when runner is created successfully'
it_behaves_like 'when model is invalid returns error'
@@ -226,7 +197,6 @@ RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
context 'when user is admin in admin mode', :enable_admin_mode do
let(:current_user) { admin }
- it_behaves_like 'when :create_runner_workflow_for_namespace feature flag is disabled'
it_behaves_like 'when runner is created successfully'
it_behaves_like 'when model is invalid returns error'
end
@@ -249,7 +219,6 @@ RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
context 'when user is group owner' do
let(:current_user) { group_owner }
- it_behaves_like 'when :create_runner_workflow_for_namespace feature flag is disabled'
it_behaves_like 'when runner is created successfully'
it_behaves_like 'when model is invalid returns error'
@@ -304,7 +273,6 @@ RSpec.describe 'RunnerCreate', feature_category: :runner_fleet do
context 'when user is admin in admin mode', :enable_admin_mode do
let(:current_user) { admin }
- it_behaves_like 'when :create_runner_workflow_for_namespace feature flag is disabled'
it_behaves_like 'when runner is created successfully'
it_behaves_like 'when model is invalid returns error'
end
diff --git a/spec/requests/api/graphql/mutations/issues/update_spec.rb b/spec/requests/api/graphql/mutations/issues/update_spec.rb
index e51c057c182..97ead687a82 100644
--- a/spec/requests/api/graphql/mutations/issues/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/update_spec.rb
@@ -51,9 +51,7 @@ RSpec.describe 'Update of an existing issue', feature_category: :team_planning d
expect do
post_graphql_mutation(mutation, current_user: current_user)
issue.reload
- end.to change { issue.work_item_type.base_type }.from('issue').to('incident').and(
- change(issue, :issue_type).from('issue').to('incident')
- )
+ end.to change { issue.work_item_type.base_type }.from('issue').to('incident')
end
end
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 e6feba059c4..37bcdf61d23 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -105,7 +105,7 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do
context 'as work item' do
let_it_be(:project) { create(:project) }
- let_it_be(:noteable) { create(:work_item, :issue, project: project) }
+ let_it_be(:noteable) { create(:work_item, project: project) }
context 'when using internal param' do
let(:variables_extra) { { internal: true } }
diff --git a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
index f40518a574b..9d63eed276d 100644
--- a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Destroying a Note', feature_category: :team_planning do
include GraphqlHelpers
- let(:noteable) { create(:work_item, :issue) }
+ let(:noteable) { create(:work_item) }
let!(:note) { create(:note, noteable: noteable, project: noteable.project) }
let(:global_note_id) { GitlabSchema.id_from_object(note).to_s }
let(:variables) { { id: global_note_id } }
diff --git a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb
index 7918bc860fe..7102f817d4c 100644
--- a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe 'Updating a Note', feature_category: :team_planning do
it_behaves_like 'a Note mutation update only with quick actions'
context 'for work item' do
- let(:noteable) { create(:work_item, :issue) }
+ let(:noteable) { create(:work_item) }
let(:note) { create(:note, noteable: noteable, project: noteable.project, note: original_body) }
it_behaves_like 'a Note mutation updates a note successfully'
diff --git a/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb b/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb
index 09e884d9412..c3f818b6627 100644
--- a/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe 'Destroying a Snippet', feature_category: :source_code_management
let!(:snippet_gid) { project.to_gid.to_s }
it 'returns an error' do
- err_message = %Q["#{snippet_gid}" does not represent an instance of Snippet]
+ err_message = %["#{snippet_gid}" does not represent an instance of Snippet]
post_graphql_mutation(mutation, current_user: current_user)
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 60b5795ee9b..ea9516f256c 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
@@ -839,14 +839,14 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
context 'when changing work item type' do
let_it_be(:work_item) { create(:work_item, :task, project: project) }
- let(:description) { "/type Issue" }
+ let(:description) { "/type issue" }
let(:input) { { 'descriptionWidget' => { 'description' => description } } }
context 'with multiple commands' do
let_it_be(:work_item) { create(:work_item, :task, project: project) }
- let(:description) { "Updating work item\n/type Issue\n/due tomorrow\n/title Foo" }
+ let(:description) { "Updating work item\n/type issue\n/due tomorrow\n/title Foo" }
it 'updates the work item type and other attributes' do
expect do
diff --git a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb
deleted file mode 100644
index 3417f9529bd..00000000000
--- a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :incident_management do
- include GraphqlHelpers
-
- let_it_be(:project) { create(:project) }
- let_it_be(:current_user) { create(:user) }
-
- let(:fields) do
- <<~QUERY
- nodes {
- iid
- metricsDashboardUrl
- }
- QUERY
- end
-
- let(:graphql_query) do
- graphql_query_for(
- 'project',
- { 'fullPath' => project.full_path },
- query_graphql_field('alertManagementAlerts', {}, fields)
- )
- end
-
- let(:alerts) { graphql_data.dig('project', 'alertManagementAlerts', 'nodes') }
- let(:first_alert) { alerts.first }
-
- before do
- stub_feature_flags(remove_monitor_metrics: false)
- project.add_developer(current_user)
- end
-
- context 'with self-managed prometheus payload' do
- include_context 'self-managed prometheus alert attributes'
-
- before do
- create(:alert_management_alert, :prometheus, project: project, payload: payload)
- end
-
- it 'includes the correct metrics dashboard url' do
- post_graphql(graphql_query, current_user: current_user)
-
- expect(first_alert).to include('metricsDashboardUrl' => dashboard_url_for_alert)
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns nil' do
- post_graphql(graphql_query, current_user: current_user)
- expect(first_alert['metricsDashboardUrl']).to be_nil
- end
- end
- end
-
- context 'with gitlab-managed prometheus payload' do
- include_context 'gitlab-managed prometheus alert attributes'
-
- before do
- create(:alert_management_alert, :prometheus, project: project, payload: payload, prometheus_alert: prometheus_alert)
- end
-
- it 'includes the correct metrics dashboard url' do
- post_graphql(graphql_query, current_user: current_user)
-
- expect(first_alert).to include('metricsDashboardUrl' => dashboard_url_for_alert)
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns nil' do
- post_graphql(graphql_query, current_user: current_user)
- expect(first_alert['metricsDashboardUrl']).to be_nil
- end
- end
- end
-end
diff --git a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
index 55d223daf27..7f586edd510 100644
--- a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
+++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb
@@ -74,7 +74,6 @@ RSpec.describe 'getting Alert Management Alerts', feature_category: :incident_ma
'details' => { 'custom.alert' => 'payload', 'runbook' => 'runbook' },
'createdAt' => triggered_alert.created_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
'updatedAt' => triggered_alert.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ'),
- 'metricsDashboardUrl' => nil,
'detailsUrl' => triggered_alert.details_url,
'prometheusAlert' => nil,
'runbook' => 'runbook'
diff --git a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
index 7587b227d9f..dd383226e17 100644
--- a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
+++ b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'getting incident timeline events', feature_category: :incident_m
let_it_be(:promoted_from_note) { create(:note, project: project, noteable: incident) }
let_it_be(:issue_url) { project_issue_url(private_project, issue) }
let_it_be(:issue_ref) { "#{private_project.full_path}##{issue.iid}" }
- let_it_be(:issue_link) { %Q(<a href="#{issue_url}">#{issue_url}</a>) }
+ let_it_be(:issue_link) { %(<a href="#{issue_url}">#{issue_url}</a>) }
let_it_be(:timeline_event) do
create(
diff --git a/spec/requests/api/graphql/project/jobs_spec.rb b/spec/requests/api/graphql/project/jobs_spec.rb
index aea6cad9e62..2c45c7e9b79 100644
--- a/spec/requests/api/graphql/project/jobs_spec.rb
+++ b/spec/requests/api/graphql/project/jobs_spec.rb
@@ -11,6 +11,9 @@ RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration d
create(:ci_pipeline, project: project, user: user)
end
+ let!(:job1) { create(:ci_build, pipeline: pipeline, name: 'job 1') }
+ let!(:job2) { create(:ci_build, pipeline: pipeline, name: 'job 2') }
+
let(:query) do
<<~QUERY
{
@@ -18,11 +21,6 @@ RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration d
jobs {
nodes {
name
- previousStageJobsAndNeeds {
- nodes {
- name
- }
- }
}
}
}
@@ -30,27 +28,10 @@ RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration d
QUERY
end
- it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
- build_stage = create(:ci_stage, position: 1, name: 'build', project: project, pipeline: pipeline)
- test_stage = create(:ci_stage, position: 2, name: 'test', project: project, pipeline: pipeline)
- create(:ci_build, pipeline: pipeline, name: 'docker 1 2', ci_stage: build_stage)
- create(:ci_build, pipeline: pipeline, name: 'docker 2 2', ci_stage: build_stage)
- create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', ci_stage: test_stage)
- test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', ci_stage: test_stage)
- create(:ci_build_need, build: test_job, name: 'docker 1 2')
-
+ it 'fetches jobs' do
post_graphql(query, current_user: user)
+ expect_graphql_errors_to_be_empty
- control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
- post_graphql(query, current_user: user)
- end
-
- create(:ci_build, name: 'test-a', ci_stage: test_stage, pipeline: pipeline)
- test_b_job = create(:ci_build, name: 'test-b', ci_stage: test_stage, pipeline: pipeline)
- create(:ci_build_need, build: test_b_job, name: 'docker 2 2')
-
- expect do
- post_graphql(query, current_user: user)
- end.not_to exceed_all_query_limit(control)
+ expect(graphql_data['project']['jobs']['nodes'].pluck('name')).to contain_exactly('job 1', 'job 2')
end
end
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index fb1489372fc..d20ee5bfdff 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -337,16 +337,33 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
end
end
- context 'N+1 queries on pipeline jobs' do
+ context 'N+1 queries on pipeline jobs.previousStageJobsOrNeeds' do
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:fields) do
<<~FIELDS
- jobs {
+ stages {
nodes {
- previousStageJobsAndNeeds {
+ groups {
nodes {
- name
+ jobs {
+ nodes {
+ previousStageJobsOrNeeds {
+ nodes {
+ ... on JobNeedUnion {
+ ... on CiBuildNeed {
+ id
+ name
+ }
+ ... on CiJob {
+ id
+ name
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
@@ -357,13 +374,15 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
build_stage = create(:ci_stage, position: 1, name: 'build', project: project, pipeline: pipeline)
test_stage = create(:ci_stage, position: 2, name: 'test', project: project, pipeline: pipeline)
- create(:ci_build, pipeline: pipeline, name: 'docker 1 2', ci_stage: build_stage)
+
+ docker_1_2 = create(:ci_build, pipeline: pipeline, name: 'docker 1 2', ci_stage: build_stage)
create(:ci_build, pipeline: pipeline, name: 'docker 2 2', ci_stage: build_stage)
create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', ci_stage: test_stage)
- test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', ci_stage: test_stage)
- create(:ci_build_need, build: test_job, name: 'docker 1 2')
+ create(:ci_build, :dependent, needed: docker_1_2, pipeline: pipeline, name: 'rspec 2 2', ci_stage: test_stage)
+ # warm up
post_graphql(query, current_user: current_user)
+ expect_graphql_errors_to_be_empty
control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
post_graphql(query, current_user: current_user)
@@ -371,7 +390,7 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
create(:ci_build, name: 'test-a', ci_stage: test_stage, pipeline: pipeline)
test_b_job = create(:ci_build, name: 'test-b', ci_stage: test_stage, pipeline: pipeline)
- create(:ci_build_need, build: test_b_job, name: 'docker 2 2')
+ create(:ci_build, :dependent, needed: test_b_job, pipeline: pipeline, name: 'docker 2 2', ci_stage: test_stage)
expect do
post_graphql(query, current_user: current_user)
@@ -424,6 +443,7 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
# warm up
post_graphql(query, current_user: current_user)
+ expect_graphql_errors_to_be_empty
control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
post_graphql(query, current_user: current_user)
diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb
index ca319ed1b2e..440eb2f52be 100644
--- a/spec/requests/api/graphql/user_query_spec.rb
+++ b/spec/requests/api/graphql/user_query_spec.rb
@@ -503,4 +503,51 @@ RSpec.describe 'getting user information', feature_category: :user_management do
end
end
end
+
+ context 'authored merge requests' do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:project) { create(:project, :public, group: group) }
+ let_it_be(:merge_request1) do
+ create(:merge_request, source_project: project, source_branch: '1', author: current_user)
+ end
+
+ let_it_be(:merge_request2) do
+ create(:merge_request, source_project: project, source_branch: '2', author: current_user)
+ end
+
+ let_it_be(:merge_request_different_user) do
+ create(:merge_request, source_project: project, source_branch: '3', author: create(:user))
+ end
+
+ let_it_be(:merge_request_different_group) do
+ create(:merge_request, source_project: create(:project, :public), author: current_user)
+ end
+
+ let_it_be(:merge_request_subgroup) do
+ create(:merge_request, source_project: create(:project, :public, group: subgroup), author: current_user)
+ end
+
+ let(:query) do
+ %(
+ query {
+ currentUser {
+ authoredMergeRequests(groupId: "#{group.to_global_id}") {
+ nodes {
+ id
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns merge requests for the current user for the specified group' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data_at(:current_user, :authored_merge_requests, :nodes).pluck('id')).to contain_exactly(
+ merge_request1.to_global_id.to_s, merge_request2.to_global_id.to_s, merge_request_subgroup.to_global_id.to_s)
+ end
+ end
end
diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb
index 58d0e6a1eb5..7c194627f82 100644
--- a/spec/requests/api/group_clusters_spec.rb
+++ b/spec/requests/api/group_clusters_spec.rb
@@ -453,7 +453,7 @@ RSpec.describe API::GroupClusters, feature_category: :deployment_management do
end
it 'returns validation error' do
- expect(json_response['message']['platform_kubernetes.base'].first).to eq(_('Cannot modify managed Kubernetes cluster'))
+ expect(json_response['message']['platform_kubernetes'].first).to eq(_('Cannot modify managed Kubernetes cluster'))
end
end
diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb
index 9dd5fe6f7c4..b4add2494b0 100644
--- a/spec/requests/api/group_export_spec.rb
+++ b/spec/requests/api/group_export_spec.rb
@@ -168,8 +168,9 @@ RSpec.describe API::GroupExport, feature_category: :importers do
end
describe 'relations export' do
+ let(:relation) { 'labels' }
let(:path) { "/groups/#{group.id}/export_relations" }
- let(:download_path) { "/groups/#{group.id}/export_relations/download?relation=labels" }
+ let(:download_path) { "/groups/#{group.id}/export_relations/download?relation=#{relation}" }
let(:status_path) { "/groups/#{group.id}/export_relations/status" }
before do
@@ -196,46 +197,131 @@ RSpec.describe API::GroupExport, feature_category: :importers do
expect(response).to have_gitlab_http_status(:error)
end
end
+
+ context 'when request is to export in batches' do
+ it 'accepts the request' do
+ expect(BulkImports::ExportService)
+ .to receive(:new)
+ .with(portable: group, user: user, batched: true)
+ .and_call_original
+
+ post api(path, user), params: { batched: true }
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+ end
end
describe 'GET /groups/:id/export_relations/download' do
- let(:export) { create(:bulk_import_export, group: group, relation: 'labels') }
- let(:upload) { create(:bulk_import_export_upload, export: export) }
+ context 'when export request is not batched' do
+ let(:export) { create(:bulk_import_export, group: group, relation: 'labels') }
+ let(:upload) { create(:bulk_import_export_upload, export: export) }
+
+ context 'when export file exists' do
+ it 'downloads exported group archive' do
+ upload.update!(export_file: fixture_file_upload('spec/fixtures/bulk_imports/gz/labels.ndjson.gz'))
+
+ get api(download_path, user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'when export_file.file does not exist' do
+ it 'returns 404' do
+ allow(export).to receive(:upload).and_return(nil)
+
+ get api(download_path, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('Export file not found')
+ end
+ end
+
+ context 'when export is batched' do
+ let(:relation) { 'milestones' }
+
+ let_it_be(:export) { create(:bulk_import_export, :batched, group: group, relation: 'milestones') }
+
+ it 'returns 400' do
+ export.update!(batched: true)
+
+ get api(download_path, user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('Export is batched')
+ end
+ end
+ end
- context 'when export file exists' do
- it 'downloads exported group archive' do
+ context 'when export request is batched' do
+ let(:export) { create(:bulk_import_export, :batched, group: group, relation: 'labels') }
+ let(:upload) { create(:bulk_import_export_upload) }
+ let!(:batch) { create(:bulk_import_export_batch, export: export, upload: upload) }
+
+ it 'downloads exported batch' do
upload.update!(export_file: fixture_file_upload('spec/fixtures/bulk_imports/gz/labels.ndjson.gz'))
- get api(download_path, user)
+ get api(download_path, user), params: { batched: true, batch_number: batch.batch_number }
expect(response).to have_gitlab_http_status(:ok)
+ expect(response.header['Content-Disposition'])
+ .to eq("attachment; filename=\"labels.ndjson.gz\"; filename*=UTF-8''labels.ndjson.gz")
end
- end
- context 'when export_file.file does not exist' do
- it 'returns 404' do
- allow(export).to receive(:upload).and_return(nil)
+ context 'when request is to download not batched export' do
+ it 'returns 400' do
+ get api(download_path, user)
- get api(download_path, user)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('Export is batched')
+ end
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- expect(json_response['message']).to eq('404 Not found')
+ context 'when batch cannot be found' do
+ it 'returns 404' do
+ get api(download_path, user), params: { batched: true, batch_number: 0 }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('Batch not found')
+ end
+ end
+
+ context 'when batch file cannot be found' do
+ it 'returns 404' do
+ batch.upload.destroy!
+
+ get api(download_path, user), params: { batched: true, batch_number: batch.batch_number }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('Batch file not found')
+ end
end
end
end
describe 'GET /groups/:id/export_relations/status' do
- it 'returns a list of relation export statuses' do
- create(:bulk_import_export, :started, group: group, relation: 'labels')
- create(:bulk_import_export, :finished, group: group, relation: 'milestones')
- create(:bulk_import_export, :failed, group: group, relation: 'badges')
+ let_it_be(:started_export) { create(:bulk_import_export, :started, group: group, relation: 'labels') }
+ let_it_be(:finished_export) { create(:bulk_import_export, :finished, group: group, relation: 'milestones') }
+ let_it_be(:failed_export) { create(:bulk_import_export, :failed, group: group, relation: 'badges') }
+ it 'returns a list of relation export statuses' do
get api(status_path, user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.pluck('relation')).to contain_exactly('labels', 'milestones', 'badges')
expect(json_response.pluck('status')).to contain_exactly(-1, 0, 1)
end
+
+ context 'when relation is specified' do
+ it 'return a single relation export status' do
+ get api(status_path, user), params: { relation: 'labels' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['relation']).to eq('labels')
+ expect(json_response['status']).to eq(0)
+ end
+ end
end
context 'when bulk import is disabled' do
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index 6849b087211..d0edc181b65 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -56,6 +56,7 @@ RSpec.describe API::GroupVariables, feature_category: :secrets_management do
expect(json_response['protected']).to eq(variable.protected?)
expect(json_response['variable_type']).to eq(variable.variable_type)
expect(json_response['environment_scope']).to eq(variable.environment_scope)
+ expect(json_response['description']).to be_nil
end
it 'responds with 404 Not Found if requesting non-existing variable' do
@@ -115,7 +116,7 @@ RSpec.describe API::GroupVariables, feature_category: :secrets_management do
it 'creates variable with optional attributes' do
expect do
- post api("/groups/#{group.id}/variables", user), params: { variable_type: 'file', key: 'TEST_VARIABLE_2', value: 'VALUE_2' }
+ post api("/groups/#{group.id}/variables", user), params: { variable_type: 'file', key: 'TEST_VARIABLE_2', value: 'VALUE_2', description: 'description' }
end.to change { group.variables.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
@@ -126,6 +127,7 @@ RSpec.describe API::GroupVariables, feature_category: :secrets_management do
expect(json_response['raw']).to be_falsey
expect(json_response['variable_type']).to eq('file')
expect(json_response['environment_scope']).to eq('*')
+ expect(json_response['description']).to eq('description')
end
it 'does not allow to duplicate variable key' do
@@ -182,7 +184,7 @@ RSpec.describe API::GroupVariables, feature_category: :secrets_management do
initial_variable = group.variables.reload.first
value_before = initial_variable.value
- put api("/groups/#{group.id}/variables/#{variable.key}", user), params: { variable_type: 'file', value: 'VALUE_1_UP', protected: true, masked: true, raw: true }
+ put api("/groups/#{group.id}/variables/#{variable.key}", user), params: { variable_type: 'file', value: 'VALUE_1_UP', protected: true, masked: true, raw: true, description: 'updated' }
updated_variable = group.variables.reload.first
@@ -193,6 +195,7 @@ RSpec.describe API::GroupVariables, feature_category: :secrets_management do
expect(json_response['variable_type']).to eq('file')
expect(json_response['masked']).to be_truthy
expect(json_response['raw']).to be_truthy
+ expect(json_response['description']).to eq('updated')
end
it 'masks the new value when logging' do
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 2adf71f2a18..5296a8b3e93 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -270,29 +270,17 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do
end
it "includes statistics if requested", :aggregate_failures do
- attributes = {
- storage_size: 4093,
- repository_size: 123,
- wiki_size: 456,
- lfs_objects_size: 234,
- build_artifacts_size: 345,
- pipeline_artifacts_size: 456,
- packages_size: 567,
- snippets_size: 1234,
- uploads_size: 678
- }.stringify_keys
- exposed_attributes = attributes.dup
- exposed_attributes['job_artifacts_size'] = exposed_attributes.delete('build_artifacts_size')
-
- project1.statistics.update!(attributes)
+ stat_keys = %w[storage_size repository_size wiki_size
+ lfs_objects_size job_artifacts_size pipeline_artifacts_size
+ packages_size snippets_size uploads_size]
get api("/groups", admin, admin_mode: true), params: { statistics: true }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response)
- .to satisfy_one { |group| group['statistics'] == exposed_attributes }
+
+ expect(json_response[0]["statistics"].keys).to match_array(stat_keys)
end
end
@@ -856,6 +844,39 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do
expect(shared_with_groups).to contain_exactly(group_link_1.shared_with_group_id, group_link_2.shared_with_group_id)
end
end
+
+ context "expose shared_runners_setting attribute" do
+ let(:group) { create(:group, shared_runners_enabled: true) }
+
+ before do
+ group.add_owner(user1)
+ end
+
+ it "returns the group with shared_runners_setting as 'enabled'", :aggregate_failures do
+ get api("/groups/#{group.id}", user1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['shared_runners_setting']).to eq("enabled")
+ end
+
+ it "returns the group with shared_runners_setting as 'disabled_and_unoverridable'", :aggregate_failures do
+ group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: false)
+
+ get api("/groups/#{group.id}", user1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['shared_runners_setting']).to eq("disabled_and_unoverridable")
+ end
+
+ it "returns the group with shared_runners_setting as 'disabled_and_overridable'", :aggregate_failures do
+ group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: true)
+
+ get api("/groups/#{group.id}", user1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['shared_runners_setting']).to eq("disabled_and_overridable")
+ end
+ end
end
end
@@ -1070,6 +1091,50 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do
end
end
+ context 'with owned' do
+ let_it_be(:group) { create(:group) }
+
+ let_it_be(:project1) { create(:project, group: group) }
+ let_it_be(:project1_guest) { create(:user) }
+ let_it_be(:project1_owner) { create(:user) }
+ let_it_be(:project1_maintainer) { create(:user) }
+
+ let_it_be(:project2) { create(:project, group: group) }
+
+ before do
+ project1.add_guest(project1_guest)
+ project1.add_owner(project1_owner)
+ project1.add_maintainer(project1_maintainer)
+
+ project2_owner = project1_owner
+ project2.add_owner(project2_owner)
+ end
+
+ context "as a guest" do
+ it 'returns no projects' do
+ get api("/groups/#{group.id}/projects", project1_guest), params: { owned: true }
+ project_ids = json_response.map { |proj| proj['id'] }
+ expect(project_ids).to match_array([])
+ end
+ end
+
+ context "as a maintainer" do
+ it 'returns no projects' do
+ get api("/groups/#{group.id}/projects", project1_maintainer), params: { owned: true }
+ project_ids = json_response.map { |proj| proj['id'] }
+ expect(project_ids).to match_array([])
+ end
+ end
+
+ context "as an owner" do
+ it 'returns projects with owner access level' do
+ get api("/groups/#{group.id}/projects", project1_owner), params: { owned: true }
+ project_ids = json_response.map { |proj| proj['id'] }
+ expect(project_ids).to match_array([project1.id, project2.id])
+ end
+ end
+ end
+
it "returns the group's projects", :aggregate_failures do
get api("/groups/#{group1.id}/projects", user1)
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 0be9df41e8f..7304437bc42 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -32,6 +32,9 @@ RSpec.describe API::Helpers, :enable_admin_mode, feature_category: :system_acces
before do
allow_any_instance_of(self.class).to receive(:options).and_return({})
+
+ allow(env['rack.session']).to receive(:enabled?).and_return(true)
+ allow(env['rack.session']).to receive(:loaded?).and_return(true)
end
def warden_authenticate_returns(value)
@@ -567,6 +570,9 @@ RSpec.describe API::Helpers, :enable_admin_mode, feature_category: :system_acces
context 'using warden authentication' do
before do
+ allow(session).to receive(:enabled?).and_return(true)
+ allow(session).to receive(:loaded?).and_return(true)
+
warden_authenticate_returns admin
env[API::Helpers::SUDO_HEADER] = user.username
end
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index 9b5ae72526c..e394b92c0a2 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe API::ImportGithub, feature_category: :importers do
let(:token) { "asdasd12345" }
let(:provider) { :github }
- let(:access_params) { { github_access_token: token } }
+ let(:access_params) { { github_access_token: token, additional_access_tokens: additional_access_tokens } }
+ let(:additional_access_tokens) { nil }
let(:provider_username) { user.username }
let(:provider_user) { double('provider', login: provider_username).as_null_object }
let(:provider_repo) do
@@ -51,7 +52,7 @@ RSpec.describe API::ImportGithub, feature_category: :importers do
it 'returns 201 response when the project is imported successfully' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
- .and_return(double(execute: project))
+ .and_return(double(execute: project))
post api("/import/github", user), params: {
target_namespace: user.namespace_path,
@@ -120,6 +121,28 @@ RSpec.describe API::ImportGithub, feature_category: :importers do
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
+
+ context 'when additional access tokens are provided' do
+ let(:additional_access_tokens) { 'token1,token2' }
+
+ it 'returns 201' do
+ expected_access_params = { github_access_token: token, additional_access_tokens: %w[token1 token2] }
+
+ expect(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new)
+ .with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **expected_access_params)
+ .and_return(double(execute: project))
+
+ post api("/import/github", user), params: {
+ target_namespace: user.namespace_path,
+ personal_access_token: token,
+ repo_id: non_existing_record_id,
+ additional_access_tokens: 'token1,token2'
+ }
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+ end
end
describe "POST /import/github/cancel" do
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 3c76fba4e2c..09170ca952f 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -122,14 +122,28 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
it 'tracks events and unique events', :aggregate_failures do
request_count = 2
- counters = { gitops_sync: 10, k8s_api_proxy_request: 5, flux_git_push_notifications_total: 42 }
- unique_counters = { agent_users_using_ci_tunnel: [10, 999, 777, 10] }
+ counters = {
+ gitops_sync: 10,
+ k8s_api_proxy_request: 5,
+ flux_git_push_notifications_total: 42,
+ k8s_api_proxy_requests_via_ci_access: 43,
+ k8s_api_proxy_requests_via_user_access: 44
+ }
+ unique_counters = {
+ agent_users_using_ci_tunnel: [10, 999, 777, 10],
+ k8s_api_proxy_requests_unique_users_via_ci_access: [10, 999, 777, 10],
+ k8s_api_proxy_requests_unique_agents_via_ci_access: [10, 999, 777, 10],
+ k8s_api_proxy_requests_unique_users_via_user_access: [10, 999, 777, 10],
+ k8s_api_proxy_requests_unique_agents_via_user_access: [10, 999, 777, 10],
+ flux_git_push_notified_unique_projects: [10, 999, 777, 10]
+ }
expected_counters = {
kubernetes_agent_gitops_sync: request_count * counters[:gitops_sync],
kubernetes_agent_k8s_api_proxy_request: request_count * counters[:k8s_api_proxy_request],
- kubernetes_agent_flux_git_push_notifications_total: request_count * counters[:flux_git_push_notifications_total]
+ kubernetes_agent_flux_git_push_notifications_total: request_count * counters[:flux_git_push_notifications_total],
+ kubernetes_agent_k8s_api_proxy_requests_via_ci_access: request_count * counters[:k8s_api_proxy_requests_via_ci_access],
+ kubernetes_agent_k8s_api_proxy_requests_via_user_access: request_count * counters[:k8s_api_proxy_requests_via_user_access]
}
- expected_hll_count = unique_counters[:agent_users_using_ci_tunnel].uniq.count
request_count.times do
send_request(params: { counters: counters, unique_counters: unique_counters })
@@ -137,13 +151,15 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
expect(Gitlab::UsageDataCounters::KubernetesAgentCounter.totals).to eq(expected_counters)
- expect(
- Gitlab::UsageDataCounters::HLLRedisCounter
- .unique_events(
- event_names: 'agent_users_using_ci_tunnel',
- start_date: Date.current, end_date: Date.current + 10
- )
- ).to eq(expected_hll_count)
+ unique_counters.each do |c, xs|
+ expect(
+ Gitlab::UsageDataCounters::HLLRedisCounter
+ .unique_events(
+ event_names: c.to_s,
+ start_date: Date.current, end_date: Date.current + 10
+ )
+ ).to eq(xs.uniq.count)
+ end
end
end
end
@@ -231,7 +247,7 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
'gitaly_info' => a_hash_including(
'address' => match(/\.socket$/),
'token' => 'secret',
- 'features' => {}
+ 'features' => Feature::Gitaly.server_feature_flags
),
'gitaly_repository' => a_hash_including(
'storage_name' => project.repository_storage,
@@ -274,7 +290,7 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
'gitaly_info' => a_hash_including(
'address' => match(/\.socket$/),
'token' => 'secret',
- 'features' => {}
+ 'features' => Feature::Gitaly.server_feature_flags
),
'gitaly_repository' => a_hash_including(
'storage_name' => project.repository_storage,
@@ -531,18 +547,6 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
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)
-
- deployment_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
deployment_project.add_member(user, :developer)
token = new_token
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb
index 05a9d98a9d0..7fe17760220 100644
--- a/spec/requests/api/lint_spec.rb
+++ b/spec/requests/api/lint_spec.rb
@@ -3,16 +3,6 @@
require 'spec_helper'
RSpec.describe API::Lint, feature_category: :pipeline_composition do
- describe 'POST /ci/lint' do
- it 'responds with a 410' do
- user = create(:user)
-
- post api('/ci/lint', user), params: { content: "test_job:\n script: ls" }
-
- expect(response).to have_gitlab_http_status(:gone)
- end
- end
-
describe 'GET /projects/:id/ci/lint' do
subject(:ci_lint) { get api("/projects/#{project.id}/ci/lint", api_user), params: { dry_run: dry_run, include_jobs: include_jobs } }
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 50e70a9dc0f..f4cac0854e7 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -2,7 +2,8 @@
require "spec_helper"
-RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :source_code_management do
+RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :source_code_management,
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/418757' do
include ProjectForksHelper
let_it_be(:base_time) { Time.now }
@@ -60,13 +61,14 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
end
context 'with merge status recheck projection' do
- it 'does not enqueue a merge status recheck' do
+ it 'does not check mergeability', :sidekiq_inline do
expect(check_service_class).not_to receive(:new)
- get(api(endpoint_path), params: { with_merge_status_recheck: true })
+ get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
expect_successful_response_with_paginated_array
expect(mr_entity['merge_status']).to eq('unchecked')
+ expect(merge_request.reload.merge_status).to eq('unchecked')
end
end
end
@@ -112,16 +114,32 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
end
context 'with merge status recheck projection' do
- it 'checks mergeability asynchronously' do
- expect_next_instances_of(check_service_class, (1..2)) do |service|
- expect(service).not_to receive(:execute)
- expect(service).to receive(:async_execute).and_call_original
+ context 'with batched_api_mergeability_checks FF on' do
+ it 'checks mergeability asynchronously in batch', :sidekiq_inline do
+ get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
+
+ expect_successful_response_with_paginated_array
+
+ expect(merge_request.reload.merge_status).to eq('can_be_merged')
end
+ end
- get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
+ context 'with batched_api_mergeability_checks FF off' do
+ before do
+ stub_feature_flags(batched_api_mergeability_checks: false)
+ end
- expect_successful_response_with_paginated_array
- expect(mr_entity['merge_status']).to eq('checking')
+ it 'checks mergeability asynchronously' do
+ expect_next_instances_of(check_service_class, (1..2)) do |service|
+ expect(service).not_to receive(:execute)
+ expect(service).to receive(:async_execute).and_call_original
+ end
+
+ get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
+
+ expect_successful_response_with_paginated_array
+ expect(mr_entity['merge_status']).to eq('checking')
+ end
end
end
@@ -139,13 +157,14 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
context 'with a reporter role' do
context 'with merge status recheck projection' do
- it 'does not enqueue a merge status recheck' do
+ it 'does not check mergeability', :sidekiq_inline do
expect(check_service_class).not_to receive(:new)
get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
expect_successful_response_with_paginated_array
expect(mr_entity['merge_status']).to eq('unchecked')
+ expect(merge_request.reload.merge_status).to eq('unchecked')
end
end
@@ -154,17 +173,33 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
stub_feature_flags(restrict_merge_status_recheck: false)
end
- context 'with merge status recheck projection' do
- it 'does enqueue a merge status recheck' do
- expect_next_instances_of(check_service_class, (1..2)) do |service|
- expect(service).not_to receive(:execute)
- expect(service).to receive(:async_execute).and_call_original
- end
-
+ context 'with batched_api_mergeability_checks FF on' do
+ it 'checks mergeability asynchronously in batch', :sidekiq_inline do
get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
expect_successful_response_with_paginated_array
- expect(mr_entity['merge_status']).to eq('checking')
+
+ expect(merge_request.reload.merge_status).to eq('can_be_merged')
+ end
+ end
+
+ context 'with batched_api_mergeability_checks FF off' do
+ before do
+ stub_feature_flags(batched_api_mergeability_checks: false)
+ end
+
+ context 'with merge status recheck projection' do
+ it 'does enqueue a merge status recheck' do
+ expect_next_instances_of(check_service_class, (1..2)) do |service|
+ expect(service).not_to receive(:execute)
+ expect(service).to receive(:async_execute).and_call_original
+ end
+
+ get(api(endpoint_path, user2), params: { with_merge_status_recheck: true })
+
+ expect_successful_response_with_paginated_array
+ expect(mr_entity['merge_status']).to eq('checking')
+ end
end
end
end
diff --git a/spec/requests/api/ml_model_packages_spec.rb b/spec/requests/api/ml_model_packages_spec.rb
index 9c19f522e46..3166298b430 100644
--- a/spec/requests/api/ml_model_packages_spec.rb
+++ b/spec/requests/api/ml_model_packages_spec.rb
@@ -75,6 +75,48 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
:private | :developer | true | :deploy_token | true | :success
:private | :developer | true | :deploy_token | false | :unauthorized
end
+
+ # :visibility, :user_role, :member, :token_type, :valid_token, :expected_status
+ def download_permissions_tables
+ :public | :developer | true | :personal_access_token | true | :success
+ :public | :guest | true | :personal_access_token | true | :success
+ :public | :developer | true | :personal_access_token | false | :unauthorized
+ :public | :guest | true | :personal_access_token | false | :unauthorized
+ :public | :developer | false | :personal_access_token | true | :success
+ :public | :guest | false | :personal_access_token | true | :success
+ :public | :developer | false | :personal_access_token | false | :unauthorized
+ :public | :guest | false | :personal_access_token | false | :unauthorized
+ :public | :anonymous | false | :personal_access_token | true | :success
+ :private | :developer | true | :personal_access_token | true | :success
+ :private | :guest | true | :personal_access_token | true | :forbidden
+ :private | :developer | true | :personal_access_token | false | :unauthorized
+ :private | :guest | true | :personal_access_token | false | :unauthorized
+ :private | :developer | false | :personal_access_token | true | :not_found
+ :private | :guest | false | :personal_access_token | true | :not_found
+ :private | :developer | false | :personal_access_token | false | :unauthorized
+ :private | :guest | false | :personal_access_token | false | :unauthorized
+ :private | :anonymous | false | :personal_access_token | true | :not_found
+ :public | :developer | true | :job_token | true | :success
+ :public | :guest | true | :job_token | true | :success
+ :public | :developer | true | :job_token | false | :unauthorized
+ :public | :guest | true | :job_token | false | :unauthorized
+ :public | :developer | false | :job_token | true | :success
+ :public | :guest | false | :job_token | true | :success
+ :public | :developer | false | :job_token | false | :unauthorized
+ :public | :guest | false | :job_token | false | :unauthorized
+ :private | :developer | true | :job_token | true | :success
+ :private | :guest | true | :job_token | true | :forbidden
+ :private | :developer | true | :job_token | false | :unauthorized
+ :private | :guest | true | :job_token | false | :unauthorized
+ :private | :developer | false | :job_token | true | :not_found
+ :private | :guest | false | :job_token | true | :not_found
+ :private | :developer | false | :job_token | false | :unauthorized
+ :private | :guest | false | :job_token | false | :unauthorized
+ :public | :developer | true | :deploy_token | true | :success
+ :public | :developer | true | :deploy_token | false | :unauthorized
+ :private | :developer | true | :deploy_token | true | :success
+ :private | :developer | true | :deploy_token | false | :unauthorized
+ end
# rubocop:enable Metrics/AbcSize
end
@@ -82,18 +124,23 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
project.send("add_#{user_role}", user) if member && user_role != :anonymous
end
- subject(:api_response) do
- request
- response
- end
-
- describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name/authorize' do
+ describe 'PUT /api/v4/projects/:id/packages/ml_models/:model_name/:model_version/:file_name/authorize' do
include_context 'ml model authorize permissions table'
let(:token) { tokens[:personal_access_token] }
let(:user_headers) { { 'HTTP_AUTHORIZATION' => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
let(:request) { authorize_upload_file(headers) }
+ let(:model_name) { 'my_package' }
+ let(:file_name) { 'myfile.tar.gz' }
+
+ subject(:api_response) do
+ url = "/projects/#{project.id}/packages/ml_models/#{model_name}/0.0.1/#{file_name}/authorize"
+
+ put api(url), headers: headers
+
+ response
+ end
describe 'user access' do
where(:visibility, :user_role, :member, :token_type, :valid_token, :expected_status) do
@@ -115,16 +162,14 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
describe 'application security' do
- where(:param_name, :param_value) do
- :package_name | 'my-package/../'
- :package_name | 'my-package%2f%2e%2e%2f'
- :file_name | '../.ssh%2fauthorized_keys'
- :file_name | '%2e%2e%2f.ssh%2fauthorized_keys'
+ where(:model_name, :file_name) do
+ 'my-package/../' | 'myfile.tar.gz'
+ 'my-package%2f%2e%2e%2f' | 'myfile.tar.gz'
+ 'my_package' | '../.ssh%2fauthorized_keys'
+ 'my_package' | '%2e%2e%2f.ssh%2fauthorized_keys'
end
with_them do
- let(:request) { authorize_upload_file(headers, param_name => param_value) }
-
it 'rejects malicious request' do
is_expected.to have_gitlab_http_status(:bad_request)
end
@@ -132,7 +177,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
end
- describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name' do
+ describe 'PUT /api/v4/projects/:id/packages/ml_models/:model_name/:model_version/:file_name' do
include_context 'ml model authorize permissions table'
let_it_be(:file_name) { 'model.md5' }
@@ -143,9 +188,21 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
let(:params) { { file: temp_file(file_name) } }
let(:file_key) { :file }
let(:send_rewritten_field) { true }
+ let(:model_name) { 'my_package' }
+
+ subject(:api_response) do
+ url = "/projects/#{project.id}/packages/ml_models/#{model_name}/0.0.1/#{file_name}"
- let(:request) do
- upload_file(headers)
+ workhorse_finalize(
+ api(url),
+ method: :put,
+ file_key: file_key,
+ params: params,
+ headers: headers,
+ send_rewritten_field: send_rewritten_field
+ )
+
+ response
end
describe 'success' do
@@ -179,22 +236,49 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
end
- def authorize_upload_file(request_headers, package_name: 'mypackage', file_name: 'myfile.tar.gz')
- url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}/authorize"
+ describe 'GET /api/v4/projects/:project_id/packages/ml_models/:model_name/:model_version/:file_name' do
+ include_context 'ml model authorize permissions table'
- put api(url), headers: request_headers
- end
+ let_it_be(:package) { create(:ml_model_package, project: project, name: 'model', version: '0.0.1') }
+ let_it_be(:package_file) { create(:package_file, :generic, package: package, file_name: 'model.md5') }
- def upload_file(request_headers, package_name: 'mypackage')
- url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}"
-
- workhorse_finalize(
- api(url),
- method: :put,
- file_key: file_key,
- params: params,
- headers: request_headers,
- send_rewritten_field: send_rewritten_field
- )
+ let(:model_name) { package.name }
+ let(:model_version) { package.version }
+ let(:file_name) { package_file.file_name }
+
+ let(:token) { tokens[:personal_access_token] }
+ let(:user_headers) { { 'HTTP_AUTHORIZATION' => token } }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+
+ subject(:api_response) do
+ url = "/projects/#{project.id}/packages/ml_models/#{model_name}/#{model_version}/#{file_name}"
+
+ get api(url), headers: headers
+
+ response
+ end
+
+ describe 'user access' do
+ where(:visibility, :user_role, :member, :token_type, :valid_token, :expected_status) do
+ download_permissions_tables
+ end
+
+ with_them do
+ let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
+ let(:user_headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
+
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
+ end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'process ml model package download'
+ else
+ it { is_expected.to have_gitlab_http_status(expected_status) }
+ end
+ end
+
+ it_behaves_like 'Endpoint not found if read_model_registry not available'
+ end
end
end
diff --git a/spec/requests/api/npm_group_packages_spec.rb b/spec/requests/api/npm_group_packages_spec.rb
index d97c7682b4b..431c59cf1b8 100644
--- a/spec/requests/api/npm_group_packages_spec.rb
+++ b/spec/requests/api/npm_group_packages_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe API::NpmGroupPackages, feature_category: :package_registry do
+RSpec.describe API::NpmGroupPackages, feature_category: :package_registry,
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/418757' do
using RSpec::Parameterized::TableSyntax
include_context 'npm api setup'
@@ -152,6 +153,14 @@ RSpec.describe API::NpmGroupPackages, feature_category: :package_registry do
it_behaves_like 'returning response status', params[:expected_status]
end
end
+
+ context 'when metadata cache exists' do
+ let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package.name, project_id: project.id) }
+
+ subject { get(url) }
+
+ it_behaves_like 'generates metadata response "on-the-fly"'
+ end
end
describe 'GET /api/v4/packages/npm/-/package/*package_name/dist-tags' do
@@ -164,12 +173,31 @@ RSpec.describe API::NpmGroupPackages, feature_category: :package_registry do
it_behaves_like 'handling create dist tag requests', scope: :group do
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
+
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let(:tag_name) { 'test' }
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+ let(:env) { { 'api.request.body': package.version } }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { put(url, env: env, headers: headers) }
+ end
end
describe 'DELETE /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
it_behaves_like 'handling delete dist tag requests', scope: :group do
let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
+
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let_it_be(:package_tag) { create(:packages_tag, package: package) }
+
+ let(:tag_name) { package_tag.name }
+ let(:url) { api("/groups/#{group.id}/-/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { delete(url, headers: headers) }
+ end
end
describe 'POST /api/v4/groups/:id/-/packages/npm/-/npm/v1/security/advisories/bulk' do
diff --git a/spec/requests/api/npm_instance_packages_spec.rb b/spec/requests/api/npm_instance_packages_spec.rb
index 97de7fa9e52..4f965d86d66 100644
--- a/spec/requests/api/npm_instance_packages_spec.rb
+++ b/spec/requests/api/npm_instance_packages_spec.rb
@@ -45,6 +45,14 @@ RSpec.describe API::NpmInstancePackages, feature_category: :package_registry do
end
end
end
+
+ context 'when metadata cache exists' do
+ let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package.name, project_id: project.id) }
+
+ subject { get(url) }
+
+ it_behaves_like 'generates metadata response "on-the-fly"'
+ end
end
describe 'GET /api/v4/packages/npm/-/package/*package_name/dist-tags' do
@@ -57,12 +65,31 @@ RSpec.describe API::NpmInstancePackages, feature_category: :package_registry do
it_behaves_like 'handling create dist tag requests', scope: :instance do
let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
+
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let(:tag_name) { 'test' }
+ let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+ let(:env) { { 'api.request.body': package.version } }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { put(url, env: env, headers: headers) }
+ end
end
describe 'DELETE /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
it_behaves_like 'handling delete dist tag requests', scope: :instance do
let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
+
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let_it_be(:package_tag) { create(:packages_tag, package: package) }
+
+ let(:tag_name) { package_tag.name }
+ let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { delete(url, headers: headers) }
+ end
end
describe 'POST /api/v4/packages/npm/-/npm/v1/security/advisories/bulk' do
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 60d4bddc502..8c0b9572af3 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
+RSpec.describe API::NpmProjectPackages, feature_category: :package_registry,
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/418757' do
include ExclusiveLeaseHelpers
include_context 'npm api setup'
@@ -26,6 +27,51 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
it_behaves_like 'rejects invalid package names' do
subject { get(url) }
end
+
+ context 'when metadata cache exists', :aggregate_failures do
+ let!(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package.name, project_id: project.id) }
+ let(:metadata) { Gitlab::Json.parse(npm_metadata_cache.file.read.gsub('dist_tags', 'dist-tags')) }
+
+ subject { get(url) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns response from metadata cache' do
+ expect(Packages::Npm::GenerateMetadataService).not_to receive(:new)
+ expect(Packages::Npm::MetadataCache).to receive(:find_by_package_name_and_project_id)
+ .with(package.name, project.id).and_call_original
+
+ subject
+
+ expect(json_response).to eq(metadata)
+ end
+
+ it 'bumps last_downloaded_at of metadata cache' do
+ expect { subject }
+ .to change { npm_metadata_cache.reload.last_downloaded_at }.from(nil).to(instance_of(ActiveSupport::TimeWithZone))
+ end
+
+ it_behaves_like 'does not enqueue a worker to sync a metadata cache'
+
+ context 'when npm_metadata_cache disabled' do
+ before do
+ stub_feature_flags(npm_metadata_cache: false)
+ end
+
+ it_behaves_like 'generates metadata response "on-the-fly"'
+ end
+
+ context 'when metadata cache file does not exist' do
+ before do
+ FileUtils.rm_rf(npm_metadata_cache.file.path)
+ end
+
+ it_behaves_like 'generates metadata response "on-the-fly"'
+ it_behaves_like 'enqueue a worker to sync a metadata cache'
+ end
+ end
end
describe 'GET /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags' do
@@ -39,12 +85,31 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
it_behaves_like 'handling create dist tag requests', scope: :project do
let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
+
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let(:tag_name) { 'test' }
+ let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+ let(:env) { { 'api.request.body': package.version } }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { put(url, env: env, headers: headers) }
+ end
end
describe 'DELETE /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag' do
it_behaves_like 'handling delete dist tag requests', scope: :project do
let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
end
+
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let_it_be(:package_tag) { create(:packages_tag, package: package) }
+
+ let(:tag_name) { package_tag.name }
+ let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
+
+ subject { delete(url, headers: headers) }
+ end
end
describe 'POST /api/v4/projects/:id/packages/npm/-/npm/v1/security/advisories/bulk' do
@@ -176,12 +241,13 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
subject(:upload_package_with_token) { upload_with_token(package_name, params) }
- shared_examples 'handling invalid record with 400 error' do
+ shared_examples 'handling invalid record with 400 error' do |error_message|
it 'handles an ActiveRecord::RecordInvalid exception with 400 error' do
expect { upload_package_with_token }
.not_to change { project.packages.count }
expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(error_message)
end
end
@@ -191,7 +257,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
let(:package_name) { "@#{group.path}/my_inv@@lid_package_name" }
let(:params) { upload_params(package_name: package_name) }
- it_behaves_like 'handling invalid record with 400 error'
+ it_behaves_like 'handling invalid record with 400 error', "Validation failed: Name is invalid, Name #{Gitlab::Regex.npm_package_name_regex_message}"
it_behaves_like 'not a package tracking event'
end
@@ -213,7 +279,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
with_them do
let(:params) { upload_params(package_name: package_name, package_version: version) }
- it_behaves_like 'handling invalid record with 400 error'
+ it_behaves_like 'handling invalid record with 400 error', "Validation failed: Version #{Gitlab::Regex.semver_regex_message}"
it_behaves_like 'not a package tracking event'
end
end
@@ -222,7 +288,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
let(:package_name) { "@#{group.path}/my_package_name" }
let(:params) { upload_params(package_name: package_name, file: 'npm/payload_with_empty_attachment.json') }
- it_behaves_like 'handling invalid record with 400 error'
+ it_behaves_like 'handling invalid record with 400 error', 'Attachment data is empty.'
it_behaves_like 'not a package tracking event'
end
end
@@ -297,6 +363,10 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
it_behaves_like 'handling upload with different authentications'
end
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let(:package_name) { "@#{group.path}/my_package_name" }
+ end
+
context 'with an existing package' do
let_it_be(:second_project) { create(:project, namespace: namespace) }
@@ -305,7 +375,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
let(:package_name) { "@#{group.path}/test" }
- it_behaves_like 'handling invalid record with 400 error'
+ it_behaves_like 'handling invalid record with 400 error', 'Validation failed: Package already exists'
it_behaves_like 'not a package tracking event'
context 'with a new version' do
@@ -340,6 +410,11 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
.not_to change { project.packages.count }
expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['error']).to eq('Package already exists.')
+ end
+
+ it_behaves_like 'does not enqueue a worker to sync a metadata cache' do
+ subject { upload_package_with_token }
end
end
@@ -389,7 +464,8 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
.not_to change { project.packages.count }
expect(response).to have_gitlab_http_status(:bad_request)
- expect(response.body).to include('Could not obtain package lease.')
+ expect(response.body).to include('Could not obtain package lease. Please try again.')
+ expect(json_response['error']).to eq('Could not obtain package lease. Please try again.')
end
end
@@ -415,15 +491,8 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
end
end
+ it_behaves_like 'handling invalid record with 400 error', 'Validation failed: Package json structure is too large. Maximum size is 20000 characters'
it_behaves_like 'not a package tracking event'
-
- it 'returns an error' do
- expect { upload_package_with_token }
- .not_to change { project.packages.count }
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(response.body).to include('Validation failed: Package json structure is too large')
- end
end
end
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index e0e9c944fe4..86ff739da7e 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -27,6 +27,7 @@ itself: # project
- mirror_overwrites_diverged_branches
- mirror_trigger_builds
- mirror_user_id
+ - mirror_branch_regex
- only_mirror_protected_branches
- pages_https_only
- pending_delete
@@ -42,7 +43,7 @@ itself: # project
- runners_token_encrypted
- storage_version
- topic_list
- - mirror_branch_regex
+ - verification_checksum
remapped_attributes:
avatar: avatar_url
build_allow_git_fetch: build_git_strategy
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index c52948a4cb0..99c190757ca 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -443,7 +443,7 @@ RSpec.describe API::ProjectClusters, feature_category: :deployment_management do
it 'returns validation error' do
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['platform_kubernetes.base'].first)
+ expect(json_response['message']['platform_kubernetes'].first)
.to eq(_('Cannot modify managed Kubernetes cluster'))
end
end
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index 434936c0ee7..3603a71151e 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -567,54 +567,138 @@ RSpec.describe API::ProjectExport, :aggregate_failures, :clean_gitlab_redis_cach
expect(response).to have_gitlab_http_status(:error)
end
end
+
+ context 'when request is to export in batches' do
+ it 'accepts the request' do
+ expect(BulkImports::ExportService)
+ .to receive(:new)
+ .with(portable: project, user: user, batched: true)
+ .and_call_original
+
+ post api(path, user), params: { batched: true }
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+ end
end
describe 'GET /projects/:id/export_relations/download' do
- let_it_be(:export) { create(:bulk_import_export, project: project, relation: 'labels') }
- let_it_be(:upload) { create(:bulk_import_export_upload, export: export) }
+ context 'when export request is not batched' do
+ let_it_be(:export) { create(:bulk_import_export, project: project, relation: 'labels') }
+ let_it_be(:upload) { create(:bulk_import_export_upload, export: export) }
+
+ context 'when export file exists' do
+ it 'downloads exported project relation archive' do
+ upload.update!(export_file: fixture_file_upload('spec/fixtures/bulk_imports/gz/labels.ndjson.gz'))
+
+ get api(download_path, user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.header['Content-Disposition']).to eq("attachment; filename=\"labels.ndjson.gz\"; filename*=UTF-8''labels.ndjson.gz")
+ end
+ end
+
+ context 'when relation is not portable' do
+ let(:relation) { ::BulkImports::FileTransfer::ProjectConfig.new(project).skipped_relations.first }
+
+ it_behaves_like '400 response' do
+ subject(:request) { get api(download_path, user) }
+ end
+ end
+
+ context 'when export file does not exist' do
+ it 'returns 404' do
+ allow(upload).to receive(:export_file).and_return(nil)
+
+ get api(download_path, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when export is batched' do
+ let(:relation) { 'issues' }
+
+ let_it_be(:export) { create(:bulk_import_export, :batched, project: project, relation: 'issues') }
- context 'when export file exists' do
- it 'downloads exported project relation archive' do
+ it 'returns 400' do
+ export.update!(batched: true)
+
+ get api(download_path, user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('Export is batched')
+ end
+ end
+ end
+
+ context 'when export request is batched' do
+ let(:export) { create(:bulk_import_export, :batched, project: project, relation: 'labels') }
+ let(:upload) { create(:bulk_import_export_upload) }
+ let!(:batch) { create(:bulk_import_export_batch, export: export, upload: upload) }
+
+ it 'downloads exported batch' do
upload.update!(export_file: fixture_file_upload('spec/fixtures/bulk_imports/gz/labels.ndjson.gz'))
- get api(download_path, user)
+ get api(download_path, user), params: { batched: true, batch_number: batch.batch_number }
expect(response).to have_gitlab_http_status(:ok)
expect(response.header['Content-Disposition']).to eq("attachment; filename=\"labels.ndjson.gz\"; filename*=UTF-8''labels.ndjson.gz")
end
- end
- context 'when relation is not portable' do
- let(:relation) { ::BulkImports::FileTransfer::ProjectConfig.new(project).skipped_relations.first }
+ context 'when request is to download not batched export' do
+ it 'returns 400' do
+ get api(download_path, user)
- it_behaves_like '400 response' do
- subject(:request) { get api(download_path, user) }
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('Export is batched')
+ end
end
- end
- context 'when export file does not exist' do
- it 'returns 404' do
- allow(upload).to receive(:export_file).and_return(nil)
+ context 'when batch cannot be found' do
+ it 'returns 404' do
+ get api(download_path, user), params: { batched: true, batch_number: 0 }
- get api(download_path, user)
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('Batch not found')
+ end
+ end
+
+ context 'when batch file cannot be found' do
+ it 'returns 404' do
+ batch.upload.destroy!
+
+ get api(download_path, user), params: { batched: true, batch_number: batch.batch_number }
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('Batch file not found')
+ end
end
end
end
describe 'GET /projects/:id/export_relations/status' do
- it 'returns a list of relation export statuses' do
- create(:bulk_import_export, :started, project: project, relation: 'labels')
- create(:bulk_import_export, :finished, project: project, relation: 'milestones')
- create(:bulk_import_export, :failed, project: project, relation: 'project_badges')
+ let_it_be(:started_export) { create(:bulk_import_export, :started, project: project, relation: 'labels') }
+ let_it_be(:finished_export) { create(:bulk_import_export, :finished, project: project, relation: 'milestones') }
+ let_it_be(:failed_export) { create(:bulk_import_export, :failed, project: project, relation: 'project_badges') }
+ it 'returns a list of relation export statuses' do
get api(status_path, user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.pluck('relation')).to contain_exactly('labels', 'milestones', 'project_badges')
expect(json_response.pluck('status')).to contain_exactly(-1, 0, 1)
end
+
+ context 'when relation is specified' do
+ it 'return a single relation export status' do
+ get api(status_path, user), params: { relation: 'labels' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['relation']).to eq('labels')
+ expect(json_response['status']).to eq(0)
+ end
+ end
end
context 'with bulk_import is disabled' do
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index c6bf77e5dcf..9d94b5437b7 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -57,6 +57,7 @@ RSpec.describe API::ProjectHooks, 'ProjectHooks', feature_category: :webhooks do
job_events
deployment_events
releases_events
+ emoji_events
]
end
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index b84b7e9c52d..09991be998a 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -660,6 +660,12 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
expect(response).to have_gitlab_http_status(:no_content)
end
+ it_behaves_like 'enqueue a worker to sync a metadata cache' do
+ let(:package_name) { package1.name }
+
+ subject { delete api(package_url, user) }
+ end
+
context 'with JOB-TOKEN auth' do
let(:job) { create(:ci_build, :running, user: user, project: project) }
@@ -692,6 +698,14 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
delete api(package_url, user)
end
+
+ it_behaves_like 'does not enqueue a worker to sync a metadata cache' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ subject { delete api(package_url, user) }
+ end
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index bb96771b3d5..f5d1bbbc7e8 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -223,14 +223,6 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
include_examples 'includes container_registry_access_level'
- context 'when projects_preloader_fix is disabled' do
- before do
- stub_feature_flags(projects_preloader_fix: false)
- end
-
- include_examples 'includes container_registry_access_level'
- end
-
it 'includes various project feature fields' do
get api(path, user)
project_response = json_response.find { |p| p['id'] == project.id }
@@ -1843,6 +1835,72 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
end
end
+ describe 'GET /users/:user_id/contributed_projects/' do
+ let(:path) { "/users/#{user3.id}/contributed_projects/" }
+
+ let_it_be(:project1) { create(:project, :public, path: 'my-project') }
+ let_it_be(:project2) { create(:project, :public) }
+ let_it_be(:project3) { create(:project, :public) }
+ let_it_be(:private_project) { create(:project, :private) }
+
+ before do
+ private_project.add_maintainer(user3)
+
+ create(:push_event, project: project1, author: user3)
+ create(:push_event, project: project2, author: user3)
+ create(:push_event, project: private_project, author: user3)
+ end
+
+ it 'returns error when user not found' do
+ get api("/users/#{non_existing_record_id}/contributed_projects/", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+
+ context 'with a public profile' do
+ it 'returns projects filtered by user' do
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] })
+ .to contain_exactly(project1.id, project2.id)
+ end
+ end
+
+ context 'with a private profile' do
+ before do
+ user3.update!(private_profile: true)
+ user3.reload
+ end
+
+ context 'user does not have access to view the private profile' do
+ it 'returns no projects' do
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response).to be_empty
+ end
+ end
+
+ context 'user has access to view the private profile as an admin' do
+ it 'returns projects filtered by user' do
+ get api(path, admin, admin_mode: true)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] })
+ .to contain_exactly(project1.id, project2.id, private_project.id)
+ end
+ end
+ end
+ end
+
describe 'POST /projects/user/:id' do
let(:path) { "/projects/user/#{user.id}" }
@@ -2588,12 +2646,12 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
expect(diff).to be_empty, failure_message(diff)
end
- def failure_message(_diff)
+ def failure_message(diff)
<<~MSG
It looks like project's set of exposed attributes is different from the expected set.
The following attributes are missing or newly added:
- {diff.to_a.to_sentence}
+ #{diff.to_a.to_sentence}
Please update #{project_attributes_file} file"
MSG
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 04d5f7ac20a..b79cff5a905 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -121,7 +121,18 @@ RSpec.describe API::ProtectedBranches, feature_category: :source_code_management
get api(route, user)
expect(json_response['push_access_levels']).to include(
- a_hash_including('access_level_description' => 'Deploy key', 'deploy_key_id' => deploy_key.id)
+ a_hash_including('access_level_description' => deploy_key.title, 'deploy_key_id' => deploy_key.id)
+ )
+ end
+ end
+
+ context 'when a deploy key is not present' do
+ it 'returns null deploy key field' do
+ create(:protected_branch_push_access_level, protected_branch: protected_branch)
+ get api(route, user)
+
+ expect(json_response['push_access_levels']).to include(
+ a_hash_including('deploy_key_id' => nil)
)
end
end
diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb
index c6398e624f8..8a98c751877 100644
--- a/spec/requests/api/protected_tags_spec.rb
+++ b/spec/requests/api/protected_tags_spec.rb
@@ -95,7 +95,18 @@ RSpec.describe API::ProtectedTags, feature_category: :source_code_management do
get api(route, user)
expect(json_response['create_access_levels']).to include(
- a_hash_including('access_level_description' => 'Deploy key', 'deploy_key_id' => deploy_key.id)
+ a_hash_including('access_level_description' => deploy_key.title, 'deploy_key_id' => deploy_key.id)
+ )
+ end
+ end
+
+ context 'when a deploy key is not present' do
+ it 'returns null deploy key field' do
+ create(:protected_tag_create_access_level, protected_tag: protected_tag)
+ get api(route, user)
+
+ expect(json_response['create_access_levels']).to include(
+ a_hash_including('deploy_key_id' => nil)
)
end
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 1b331e9c099..0feff90d088 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -688,6 +688,16 @@ RSpec.describe API::Search, :clean_gitlab_redis_rate_limiting, feature_category:
end
end
+ context 'when user does not have permissions for scope' do
+ it 'returns an empty array' do
+ project.project_feature.update!(issues_access_level: Gitlab::VisibilityLevel::PRIVATE)
+
+ get api(endpoint, user), params: { scope: 'issues', search: 'awesome' }
+
+ expect(json_response).to be_empty
+ end
+ end
+
context 'when project does not exist' do
it 'returns 404 error' do
get api('/projects/0/search', user), params: { scope: 'issues', search: 'awesome' }
@@ -861,7 +871,7 @@ RSpec.describe API::Search, :clean_gitlab_redis_rate_limiting, feature_category:
get api(endpoint, user), params: { scope: 'wiki_blobs', search: 'awesome' }
end
- it_behaves_like 'response is correct', schema: 'public_api/v4/blobs'
+ it_behaves_like 'response is correct', schema: 'public_api/v4/wiki_blobs'
it_behaves_like 'ping counters', scope: :wiki_blobs
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 79e96d7ea3e..dfaba969153 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -80,6 +80,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
expect(json_response['valid_runner_registrars']).to match_array(%w(project group))
expect(json_response['ci_max_includes']).to eq(150)
expect(json_response['allow_account_deletion']).to eq(true)
+ expect(json_response['gitlab_shell_operation_limit']).to eq(600)
end
end
@@ -190,13 +191,9 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
default_syntax_highlighting_theme: 2,
projects_api_rate_limit_unauthenticated: 100,
silent_mode_enabled: true,
- slack_app_enabled: true,
- slack_app_id: 'SLACK_APP_ID',
- slack_app_secret: 'SLACK_APP_SECRET',
- slack_app_signing_secret: 'SLACK_APP_SIGNING_SECRET',
- slack_app_verification_token: 'SLACK_APP_VERIFICATION_TOKEN',
valid_runner_registrars: ['group'],
- allow_account_deletion: false
+ allow_account_deletion: false,
+ gitlab_shell_operation_limit: 500
}
expect(response).to have_gitlab_http_status(:ok)
@@ -270,16 +267,23 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
expect(json_response['default_syntax_highlighting_theme']).to eq(2)
expect(json_response['projects_api_rate_limit_unauthenticated']).to be(100)
expect(json_response['silent_mode_enabled']).to be(true)
- expect(json_response['slack_app_enabled']).to be(true)
- expect(json_response['slack_app_id']).to eq('SLACK_APP_ID')
- expect(json_response['slack_app_secret']).to eq('SLACK_APP_SECRET')
- expect(json_response['slack_app_signing_secret']).to eq('SLACK_APP_SIGNING_SECRET')
- expect(json_response['slack_app_verification_token']).to eq('SLACK_APP_VERIFICATION_TOKEN')
expect(json_response['valid_runner_registrars']).to eq(['group'])
expect(json_response['allow_account_deletion']).to be(false)
+ expect(json_response['gitlab_shell_operation_limit']).to be(500)
end
end
+ it "updates default_branch_protection_defaults from the default_branch_protection param" do
+ expected_update = ::Gitlab::Access::BranchProtection.protected_against_developer_pushes.stringify_keys
+
+ put api("/application/settings", admin),
+ params: { default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
+ expect(ApplicationSetting.first.default_branch_protection_defaults).to eq(expected_update)
+ end
+
it "supports legacy performance_bar_allowed_group_id" do
put api("/application/settings", admin),
params: { performance_bar_allowed_group_id: group.full_path }
@@ -550,6 +554,85 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
end
end
+ context 'GitLab for Slack app settings' do
+ let(:settings) do
+ {
+ slack_app_enabled: slack_app_enabled,
+ slack_app_id: slack_app_id,
+ slack_app_secret: slack_app_secret,
+ slack_app_signing_secret: slack_app_signing_secret,
+ slack_app_verification_token: slack_app_verification_token
+ }
+ end
+
+ context 'when GitLab for Slack app is enabled' do
+ let(:slack_app_enabled) { true }
+
+ context 'when other params are blank' do
+ let(:slack_app_id) { nil }
+ let(:slack_app_secret) { nil }
+ let(:slack_app_signing_secret) { nil }
+ let(:slack_app_verification_token) { nil }
+
+ it 'does not update the settings' do
+ put api("/application/settings", admin), params: settings
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+
+ expect(json_response['slack_app_enabled']).to be(nil)
+ expect(json_response['slack_app_id']).to be(nil)
+ expect(json_response['slack_app_secret']).to be(nil)
+ expect(json_response['slack_app_signing_secret']).to be(nil)
+ expect(json_response['slack_app_verification_token']).to be(nil)
+
+ message = json_response['message']
+
+ expect(message['slack_app_id']).to include("can't be blank")
+ expect(message['slack_app_secret']).to include("can't be blank")
+ expect(message['slack_app_signing_secret']).to include("can't be blank")
+ expect(message['slack_app_verification_token']).to include("can't be blank")
+ end
+ end
+
+ context 'when other params are present' do
+ let(:slack_app_id) { 'ID' }
+ let(:slack_app_secret) { 'SECRET' }
+ let(:slack_app_signing_secret) { 'SIGNING_SECRET' }
+ let(:slack_app_verification_token) { 'VERIFICATION_TOKEN' }
+
+ it 'updates the settings' do
+ put api("/application/settings", admin), params: settings
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['slack_app_enabled']).to be(true)
+ expect(json_response['slack_app_id']).to eq('ID')
+ expect(json_response['slack_app_secret']).to eq('SECRET')
+ expect(json_response['slack_app_signing_secret']).to eq('SIGNING_SECRET')
+ expect(json_response['slack_app_verification_token']).to eq('VERIFICATION_TOKEN')
+ end
+ end
+ end
+
+ context 'when GitLab for Slack app is not enabled' do
+ let(:slack_app_enabled) { false }
+ let(:slack_app_id) { nil }
+ let(:slack_app_secret) { nil }
+ let(:slack_app_signing_secret) { nil }
+ let(:slack_app_verification_token) { nil }
+
+ it 'allows blank attributes' do
+ put api("/application/settings", admin), params: settings
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['slack_app_enabled']).to be(false)
+ expect(json_response['slack_app_id']).to be(nil)
+ expect(json_response['slack_app_secret']).to be(nil)
+ expect(json_response['slack_app_signing_secret']).to be(nil)
+ expect(json_response['slack_app_verification_token']).to be(nil)
+ end
+ end
+ end
+
context "missing plantuml_url value when plantuml_enabled is true" do
it "returns a blank parameter error message" do
put api("/application/settings", admin), params: { plantuml_enabled: true }
diff --git a/spec/requests/api/statistics_spec.rb b/spec/requests/api/statistics_spec.rb
index baac39abf2c..76190d4e272 100644
--- a/spec/requests/api/statistics_spec.rb
+++ b/spec/requests/api/statistics_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe API::Statistics, 'Statistics', :aggregate_failures, feature_categ
create_list(:note, 2, author: admin, project: projects.first, noteable: issues.first)
create_list(:milestone, 3, project: projects.first)
create(:key, user: admin)
- create(:merge_request, source_project: projects.first)
+ create(:merge_request, :skip_diff_creation, source_project: projects.first)
fork_project(projects.first, admin)
# Make sure the reltuples have been updated
diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb
index 935ddbf4764..c8f1e8d6973 100644
--- a/spec/requests/api/usage_data_spec.rb
+++ b/spec/requests/api/usage_data_spec.rb
@@ -164,6 +164,61 @@ RSpec.describe API::UsageData, feature_category: :service_ping do
end
end
+ describe 'POST /usage_data/track_event' do
+ let(:endpoint) { '/usage_data/track_event' }
+ let(:known_event) { 'i_compliance_dashboard' }
+ let(:unknown_event) { 'unknown' }
+ let(:namespace_id) { 123 }
+ let(:project_id) { 123 }
+
+ context 'without CSRF token' do
+ it 'returns forbidden' do
+ allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(false)
+
+ post api(endpoint, user), params: { event: known_event, namespace_id: namespace_id, project_id: project_id }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'usage_data_api feature not enabled' do
+ it 'returns not_found' do
+ stub_feature_flags(usage_data_api: false)
+
+ post api(endpoint, user), params: { event: known_event, namespace_id: namespace_id, project_id: project_id }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'without authentication' do
+ it 'returns 401 response' do
+ post api(endpoint), params: { event: known_event, namespace_id: namespace_id, project_id: project_id }
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'with authentication' do
+ before do
+ stub_application_setting(usage_ping_enabled: true)
+ allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(true)
+ end
+
+ context 'with correct params' do
+ it 'returns status ok' do
+ expect(Gitlab::InternalEvents).to receive(:track_event).with(known_event, anything)
+ # allow other events to also get triggered
+ allow(Gitlab::InternalEvents).to receive(:track_event)
+
+ post api(endpoint, user), params: { event: known_event, namespace_id: namespace_id, project_id: project_id }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+ end
+
describe 'GET /usage_data/metric_definitions' do
let(:endpoint) { '/usage_data/metric_definitions' }
let(:metric_yaml) do
diff --git a/spec/requests/api/user_runners_spec.rb b/spec/requests/api/user_runners_spec.rb
new file mode 100644
index 00000000000..0e40dcade19
--- /dev/null
+++ b/spec/requests/api/user_runners_spec.rb
@@ -0,0 +1,243 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::UserRunners, :aggregate_failures, feature_category: :runner_fleet do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:user, reload: true) { create(:user, username: 'user.withdot') }
+
+ describe 'POST /user/runners' do
+ subject(:request) { post api(path, current_user, **post_args), params: runner_attrs }
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, namespace: group) }
+ let_it_be(:group_owner) { create(:user).tap { |user| group.add_owner(user) } }
+ let_it_be(:group_maintainer) { create(:user).tap { |user| group.add_maintainer(user) } }
+ let_it_be(:project_developer) { create(:user).tap { |user| project.add_developer(user) } }
+
+ let(:post_args) { { admin_mode: true } }
+ let(:runner_attrs) { { runner_type: 'instance_type' } }
+ let(:path) { '/user/runners' }
+
+ shared_examples 'when runner creation fails due to authorization' do
+ it 'does not create a runner' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end.not_to change { Ci::Runner.count }
+ end
+ end
+
+ shared_context 'when user does not have sufficient permissions returns forbidden' do
+ context 'when user is admin and admin mode is disabled' do
+ let(:current_user) { admin }
+ let(:post_args) { { admin_mode: false } }
+
+ it_behaves_like 'when runner creation fails due to authorization'
+ end
+
+ context 'when user is not an admin or a member of the namespace' do
+ let(:current_user) { user }
+
+ it_behaves_like 'when runner creation fails due to authorization'
+ end
+ end
+
+ shared_examples 'creates a runner' do
+ it 'creates a runner' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(:created)
+ end.to change { Ci::Runner.count }.by(1)
+ end
+ end
+
+ shared_examples 'fails to create runner with expected_status_code' do
+ let(:expected_message) { nil }
+ let(:expected_error) { nil }
+
+ it 'does not create runner' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(expected_status_code)
+ expect(json_response['message']).to include(expected_message) if expected_message
+ expect(json_response['error']).to include(expected_error) if expected_error
+ end.not_to change { Ci::Runner.count }
+ end
+ end
+
+ shared_context 'with request authorized with access token' do
+ let(:current_user) { nil }
+ let(:pat) { create(:personal_access_token, user: token_user, scopes: [scope]) }
+ let(:path) { "/user/runners?private_token=#{pat.token}" }
+
+ %i[create_runner api].each do |scope|
+ context "with #{scope} scope" do
+ let(:scope) { scope }
+
+ it_behaves_like 'creates a runner'
+ end
+ end
+
+ context 'with read_api scope' do
+ let(:scope) { :read_api }
+
+ it_behaves_like 'fails to create runner with expected_status_code' do
+ let(:expected_status_code) { :forbidden }
+ let(:expected_error) { 'insufficient_scope' }
+ end
+ end
+ end
+
+ context 'when runner_type is :instance_type' do
+ let(:runner_attrs) { { runner_type: 'instance_type' } }
+
+ context 'when user has sufficient permissions' do
+ let(:current_user) { admin }
+
+ it_behaves_like 'creates a runner'
+ end
+
+ context 'with admin mode enabled', :enable_admin_mode do
+ let(:token_user) { admin }
+
+ it_behaves_like 'with request authorized with access token'
+ end
+
+ it_behaves_like 'when user does not have sufficient permissions returns forbidden'
+
+ context 'when user is not an admin' do
+ let(:current_user) { user }
+
+ it_behaves_like 'when runner creation fails due to authorization'
+ end
+
+ context 'when model validation fails' do
+ let(:runner_attrs) { { runner_type: 'instance_type', run_untagged: false, tag_list: [] } }
+ let(:current_user) { admin }
+
+ it_behaves_like 'fails to create runner with expected_status_code' do
+ let(:expected_status_code) { :bad_request }
+ let(:expected_message) { 'Tags list can not be empty' }
+ end
+ end
+ end
+
+ context 'when runner_type is :group_type' do
+ let(:post_args) { {} }
+
+ context 'when group_id is specified' do
+ let(:runner_attrs) { { runner_type: 'group_type', group_id: group.id } }
+
+ context 'when user has sufficient permissions' do
+ let(:current_user) { group_owner }
+
+ it_behaves_like 'creates a runner'
+ end
+
+ it_behaves_like 'with request authorized with access token' do
+ let(:token_user) { group_owner }
+ end
+
+ it_behaves_like 'when user does not have sufficient permissions returns forbidden'
+
+ context 'when user is a maintainer' do
+ let(:current_user) { group_maintainer }
+
+ it_behaves_like 'when runner creation fails due to authorization'
+ end
+ end
+
+ context 'when group_id is not specified' do
+ let(:runner_attrs) { { runner_type: 'group_type' } }
+ let(:current_user) { group_owner }
+
+ it 'fails to create runner with :bad_request' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to include('group_id is missing')
+ end.not_to change { Ci::Runner.count }
+ end
+ end
+ end
+
+ context 'when runner_type is :project_type' do
+ let(:post_args) { {} }
+
+ context 'when project_id is specified' do
+ let(:runner_attrs) { { runner_type: 'project_type', project_id: project.id } }
+
+ context 'when user has sufficient permissions' do
+ let(:current_user) { group_owner }
+
+ it_behaves_like 'creates a runner'
+ end
+
+ it_behaves_like 'with request authorized with access token' do
+ let(:token_user) { group_owner }
+ end
+
+ it_behaves_like 'when user does not have sufficient permissions returns forbidden'
+
+ context 'when user is a developer' do
+ let(:current_user) { project_developer }
+
+ it_behaves_like 'when runner creation fails due to authorization'
+ end
+ end
+
+ context 'when project_id is not specified' do
+ let(:runner_attrs) { { runner_type: 'project_type' } }
+ let(:current_user) { group_owner }
+
+ it 'fails to create runner with :bad_request' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to include('project_id is missing')
+ end.not_to change { Ci::Runner.count }
+ end
+ end
+ end
+
+ context 'with missing runner_type' do
+ let(:runner_attrs) { {} }
+ let(:current_user) { admin }
+
+ it 'fails to create runner with :bad_request' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('runner_type is missing, runner_type does not have a valid value')
+ end.not_to change { Ci::Runner.count }
+ end
+ end
+
+ context 'with unknown runner_type' do
+ let(:runner_attrs) { { runner_type: 'unknown' } }
+ let(:current_user) { admin }
+
+ it 'fails to create runner with :bad_request' do
+ expect do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('runner_type does not have a valid value')
+ end.not_to change { Ci::Runner.count }
+ end
+ end
+
+ it 'returns a 401 error if unauthorized' do
+ post api(path), params: runner_attrs
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 3737c91adbc..2bbcf6b3f38 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -4851,169 +4851,4 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
let(:attributable) { user }
let(:other_attributable) { admin }
end
-
- describe 'POST /user/runners', feature_category: :runner_fleet do
- subject(:request) { post api(path, current_user, **post_args), params: runner_attrs }
-
- let_it_be(:group_owner) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, namespace: group) }
-
- let(:post_args) { { admin_mode: true } }
- let(:runner_attrs) { { runner_type: 'instance_type' } }
- let(:path) { '/user/runners' }
-
- before do
- group.add_owner(group_owner)
- end
-
- shared_context 'returns forbidden when user does not have sufficient permissions' do
- let(:current_user) { admin }
- let(:post_args) { { admin_mode: false } }
-
- it 'does not create a runner' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end.not_to change { Ci::Runner.count }
- end
- end
-
- shared_examples 'creates a runner' do
- it 'creates a runner' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:created)
- end.to change { Ci::Runner.count }.by(1)
- end
- end
-
- shared_examples 'fails to create runner with :bad_request' do
- it 'does not create runner' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to include(expected_error)
- end.not_to change { Ci::Runner.count }
- end
- end
-
- context 'when runner_type is :instance_type' do
- let(:runner_attrs) { { runner_type: 'instance_type' } }
-
- context 'when user has sufficient permissions' do
- let(:current_user) { admin }
-
- it_behaves_like 'creates a runner'
- end
-
- it_behaves_like 'returns forbidden when user does not have sufficient permissions'
-
- context 'when model validation fails' do
- let(:runner_attrs) { { runner_type: 'instance_type', run_untagged: false, tag_list: [] } }
- let(:current_user) { admin }
-
- it_behaves_like 'fails to create runner with :bad_request' do
- let(:expected_error) { 'Tags list can not be empty' }
- end
- end
- end
-
- context 'when runner_type is :group_type' do
- let(:post_args) { {} }
-
- context 'when group_id is specified' do
- let(:runner_attrs) { { runner_type: 'group_type', group_id: group.id } }
-
- context 'when user has sufficient permissions' do
- let(:current_user) { group_owner }
-
- it_behaves_like 'creates a runner'
- end
-
- it_behaves_like 'returns forbidden when user does not have sufficient permissions'
- end
-
- context 'when group_id is not specified' do
- let(:runner_attrs) { { runner_type: 'group_type' } }
- let(:current_user) { group_owner }
-
- it 'fails to create runner with :bad_request' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to include('group_id is missing')
- end.not_to change { Ci::Runner.count }
- end
- end
- end
-
- context 'when runner_type is :project_type' do
- let(:post_args) { {} }
-
- context 'when project_id is specified' do
- let(:runner_attrs) { { runner_type: 'project_type', project_id: project.id } }
-
- context 'when user has sufficient permissions' do
- let(:current_user) { group_owner }
-
- it_behaves_like 'creates a runner'
- end
-
- it_behaves_like 'returns forbidden when user does not have sufficient permissions'
- end
-
- context 'when project_id is not specified' do
- let(:runner_attrs) { { runner_type: 'project_type' } }
- let(:current_user) { group_owner }
-
- it 'fails to create runner with :bad_request' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to include('project_id is missing')
- end.not_to change { Ci::Runner.count }
- end
- end
- end
-
- context 'with missing runner_type' do
- let(:runner_attrs) { {} }
- let(:current_user) { admin }
-
- it 'fails to create runner with :bad_request' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq('runner_type is missing, runner_type does not have a valid value')
- end.not_to change { Ci::Runner.count }
- end
- end
-
- context 'with unknown runner_type' do
- let(:runner_attrs) { { runner_type: 'unknown' } }
- let(:current_user) { admin }
-
- it 'fails to create runner with :bad_request' do
- expect do
- request
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq('runner_type does not have a valid value')
- end.not_to change { Ci::Runner.count }
- end
- end
-
- it 'returns a 401 error if unauthorized' do
- post api(path), params: runner_attrs
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 5b50e8a1021..d3d1a2a6cd0 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -236,11 +236,6 @@ RSpec.describe 'Git HTTP requests', feature_category: :source_code_management do
allow(::Users::ActivityService).to receive(:new).and_return(activity_service)
allow(activity_service).to receive(:execute)
- # During project creation, we need to track the project wiki
- # repository. So it is over the query limit threshold, and we
- # have to adjust it.
- allow(Gitlab::QueryLimiting::Transaction).to receive(:threshold).and_return(101)
-
expect do
upload(path, user: user.username, password: user.password) do |response|
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/requests/groups/observability_controller_spec.rb b/spec/requests/groups/observability_controller_spec.rb
index b82cf2b0bad..247535bc990 100644
--- a/spec/requests/groups/observability_controller_spec.rb
+++ b/spec/requests/groups/observability_controller_spec.rb
@@ -17,6 +17,10 @@ RSpec.describe Groups::ObservabilityController, feature_category: :tracing do
end
it_behaves_like 'observability csp policy' do
+ before_all do
+ group.add_developer(user)
+ end
+
let(:tested_path) { path }
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index b07296a0df2..199138eb3a9 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -807,7 +807,7 @@ RSpec.describe 'Git LFS API and storage', feature_category: :source_code_managem
end
end
- describe 'to one project' do
+ describe 'to one project', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/418757' do
describe 'when user is authenticated' do
describe 'when user has push access to the project' do
before do
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 82f972e7f94..217241200ff 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -270,13 +270,20 @@ RSpec.describe 'OpenID Connect requests', feature_category: :system_access do
end
context 'OpenID configuration information' do
+ let(:expected_scopes) do
+ %w[
+ admin_mode api read_user read_api read_repository write_repository sudo openid profile email
+ read_observability write_observability create_runner
+ ]
+ end
+
it 'correctly returns the configuration' do
get '/.well-known/openid-configuration'
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['issuer']).to eq('http://localhost')
expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys')
- expect(json_response['scopes_supported']).to match_array %w[admin_mode api read_user read_api read_repository write_repository sudo openid profile email read_observability write_observability]
+ expect(json_response['scopes_supported']).to match_array expected_scopes
end
context 'with a cross-origin request' do
@@ -286,7 +293,7 @@ RSpec.describe 'OpenID Connect requests', feature_category: :system_access do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['issuer']).to eq('http://localhost')
expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys')
- expect(json_response['scopes_supported']).to match_array %w[admin_mode api read_user read_api read_repository write_repository sudo openid profile email read_observability write_observability]
+ expect(json_response['scopes_supported']).to match_array expected_scopes
end
it_behaves_like 'cross-origin GET request'
diff --git a/spec/requests/organizations/organizations_controller_spec.rb b/spec/requests/organizations/organizations_controller_spec.rb
index a51a5751831..bd54b50de99 100644
--- a/spec/requests/organizations/organizations_controller_spec.rb
+++ b/spec/requests/organizations/organizations_controller_spec.rb
@@ -5,9 +5,7 @@ require 'spec_helper'
RSpec.describe Organizations::OrganizationsController, feature_category: :cell do
let_it_be(:organization) { create(:organization) }
- describe 'GET #directory' do
- subject(:gitlab_request) { get directory_organization_path(organization) }
-
+ RSpec.shared_examples 'basic organization controller action' do
before do
sign_in(user)
end
@@ -42,4 +40,16 @@ RSpec.describe Organizations::OrganizationsController, feature_category: :cell d
end
end
end
+
+ describe 'GET #show' do
+ subject(:gitlab_request) { get organization_path(organization) }
+
+ it_behaves_like 'basic organization controller action'
+ end
+
+ describe 'GET #groups_and_projects' do
+ subject(:gitlab_request) { get groups_and_projects_organization_path(organization) }
+
+ it_behaves_like 'basic organization controller action'
+ end
end
diff --git a/spec/requests/projects/alert_management_controller_spec.rb b/spec/requests/projects/alert_management_controller_spec.rb
new file mode 100644
index 00000000000..698087bf761
--- /dev/null
+++ b/spec/requests/projects/alert_management_controller_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::AlertManagementController, feature_category: :incident_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:id) { 1 }
+
+ before_all do
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ end
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET #index' do
+ context 'when user is authorized' do
+ let(:user) { developer }
+
+ it 'shows the page' do
+ get project_alert_management_index_path(project)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'when user is unauthorized' do
+ let(:user) { reporter }
+
+ it 'shows 404' do
+ get project_alert_management_index_path(project)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'GET #details' do
+ context 'when user is authorized' do
+ let(:user) { developer }
+
+ it 'shows the page' do
+ get project_alert_management_alert_path(project, id)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'sets alert id from the route' do
+ get project_alert_management_alert_path(project, id)
+
+ expect(assigns(:alert_id)).to eq(id.to_s)
+ end
+ end
+
+ context 'when user is unauthorized' do
+ let(:user) { reporter }
+
+ it 'shows 404' do
+ get project_alert_management_alert_path(project, id)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/requests/projects/incidents_controller_spec.rb b/spec/requests/projects/incidents_controller_spec.rb
new file mode 100644
index 00000000000..9a0d6cdf8ce
--- /dev/null
+++ b/spec/requests/projects/incidents_controller_spec.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::IncidentsController, feature_category: :incident_management do
+ let_it_be_with_refind(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:anonymous) { nil }
+
+ before_all do
+ project.add_guest(guest)
+ project.add_developer(developer)
+ end
+
+ before do
+ sign_in(user) if user
+ end
+
+ subject { make_request }
+
+ shared_examples 'not found' do
+ include_examples 'returning response status', :not_found
+ end
+
+ shared_examples 'login required' do
+ it 'redirects to the login page' do
+ subject
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ describe 'GET #index' do
+ def make_request
+ get project_incidents_path(project)
+ end
+
+ let(:user) { developer }
+
+ it 'shows the page' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:index)
+ end
+
+ context 'when user is unauthorized' do
+ let(:user) { anonymous }
+
+ it_behaves_like 'login required'
+ end
+
+ context 'when user is a guest' do
+ let(:user) { guest }
+
+ it 'shows the page' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:index)
+ end
+ end
+ end
+
+ describe 'GET #show' do
+ def make_request
+ get incident_project_issues_path(project, resource)
+ end
+
+ let_it_be(:resource) { create(:incident, project: project) }
+
+ let(:user) { developer }
+
+ it 'renders incident page' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:show)
+
+ expect(assigns(:incident)).to be_present
+ expect(assigns(:incident).author.association(:status)).to be_loaded
+ expect(assigns(:issue)).to be_present
+ expect(assigns(:noteable)).to eq(assigns(:incident))
+ end
+
+ context 'with non existing id' do
+ let(:resource) { non_existing_record_id }
+
+ it_behaves_like 'not found'
+ end
+
+ context 'for issue' do
+ let_it_be(:resource) { create(:issue, project: project) }
+
+ it_behaves_like 'not found'
+ end
+
+ context 'when user is a guest' do
+ let(:user) { guest }
+
+ it 'shows the page' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:show)
+ end
+ end
+
+ context 'when unauthorized' do
+ let(:user) { anonymous }
+
+ it_behaves_like 'login required'
+ end
+ end
+end
diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb
index 583fd5f586e..1ae65939c86 100644
--- a/spec/requests/projects/issues_controller_spec.rb
+++ b/spec/requests/projects/issues_controller_spec.rb
@@ -17,6 +17,11 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
describe 'GET #new' do
include_context 'group project issue'
+ before do
+ group.add_developer(user)
+ login_as(user)
+ end
+
it_behaves_like "observability csp policy", described_class do
let(:tested_path) do
new_project_issue_path(project)
@@ -26,11 +31,13 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
describe 'GET #show' do
before do
+ group.add_developer(user)
login_as(user)
end
it_behaves_like "observability csp policy", described_class do
include_context 'group project issue'
+
let(:tested_path) do
project_issue_path(project, issue)
end
diff --git a/spec/requests/projects/merge_requests/creations_spec.rb b/spec/requests/projects/merge_requests/creations_spec.rb
index ace6ef0f7b8..e8a073fef5f 100644
--- a/spec/requests/projects/merge_requests/creations_spec.rb
+++ b/spec/requests/projects/merge_requests/creations_spec.rb
@@ -6,8 +6,13 @@ RSpec.describe 'merge requests creations', feature_category: :code_review_workfl
describe 'GET /:namespace/:project/merge_requests/new' do
include ProjectForksHelper
- let(:project) { create(:project, :repository) }
- let(:user) { project.first_owner }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ group.add_developer(user)
+ end
before do
login_as(user)
@@ -26,16 +31,13 @@ RSpec.describe 'merge requests creations', feature_category: :code_review_workfl
end
it_behaves_like "observability csp policy", Projects::MergeRequests::CreationsController do
- let_it_be(:group) { create(:group) }
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, group: group) }
let(:tested_path) do
project_new_merge_request_path(project, merge_request: {
title: 'Some feature',
- source_branch: 'fix',
- target_branch: 'feature',
- target_project: project,
- source_project: project
+ source_branch: 'fix',
+ target_branch: 'feature',
+ target_project: project,
+ source_project: project
})
end
end
diff --git a/spec/requests/projects/merge_requests_controller_spec.rb b/spec/requests/projects/merge_requests_controller_spec.rb
index 955e6822211..955b6e53686 100644
--- a/spec/requests/projects/merge_requests_controller_spec.rb
+++ b/spec/requests/projects/merge_requests_controller_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :source_code
context 'when logged in' do
before do
+ group.add_developer(user)
login_as(user)
end
diff --git a/spec/requests/projects/ml/candidates_controller_spec.rb b/spec/requests/projects/ml/candidates_controller_spec.rb
index eec7af99063..4c7491970e1 100644
--- a/spec/requests/projects/ml/candidates_controller_spec.rb
+++ b/spec/requests/projects/ml/candidates_controller_spec.rb
@@ -10,13 +10,17 @@ RSpec.describe Projects::Ml::CandidatesController, feature_category: :mlops do
let(:ff_value) { true }
let(:candidate_iid) { candidate.iid }
- let(:model_experiments_enabled) { true }
+ let(:read_model_experiments) { true }
+ let(:write_model_experiments) { true }
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?)
.with(user, :read_model_experiments, project)
- .and_return(model_experiments_enabled)
+ .and_return(read_model_experiments)
+ allow(Ability).to receive(:allowed?)
+ .with(user, :write_model_experiments, project)
+ .and_return(write_model_experiments)
sign_in(user)
end
@@ -34,9 +38,9 @@ RSpec.describe Projects::Ml::CandidatesController, feature_category: :mlops do
end
end
- shared_examples '404 when model experiments is unavailable' do
+ shared_examples 'requires read_model_experiments' do
context 'when user does not have access' do
- let(:model_experiments_enabled) { false }
+ let(:read_model_experiments) { false }
it_behaves_like 'renders 404'
end
@@ -61,7 +65,7 @@ RSpec.describe Projects::Ml::CandidatesController, feature_category: :mlops do
end
it_behaves_like '404 if candidate does not exist'
- it_behaves_like '404 when model experiments is unavailable'
+ it_behaves_like 'requires read_model_experiments'
end
describe 'DELETE #destroy' do
@@ -83,7 +87,14 @@ RSpec.describe Projects::Ml::CandidatesController, feature_category: :mlops do
end
it_behaves_like '404 if candidate does not exist'
- it_behaves_like '404 when model experiments is unavailable'
+
+ describe 'requires write_model_experiments' do
+ let(:write_model_experiments) { false }
+
+ it 'is 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
private
diff --git a/spec/requests/projects/ml/experiments_controller_spec.rb b/spec/requests/projects/ml/experiments_controller_spec.rb
index e2d26e84f75..9440c716640 100644
--- a/spec/requests/projects/ml/experiments_controller_spec.rb
+++ b/spec/requests/projects/ml/experiments_controller_spec.rb
@@ -15,13 +15,17 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
let(:ff_value) { true }
let(:basic_params) { { namespace_id: project.namespace.to_param, project_id: project } }
let(:experiment_iid) { experiment.iid }
- let(:model_experiments_enabled) { true }
+ let(:read_model_experiments) { true }
+ let(:write_model_experiments) { true }
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?)
.with(user, :read_model_experiments, project)
- .and_return(model_experiments_enabled)
+ .and_return(read_model_experiments)
+ allow(Ability).to receive(:allowed?)
+ .with(user, :write_model_experiments, project)
+ .and_return(write_model_experiments)
sign_in(user)
end
@@ -40,9 +44,9 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
end
end
- shared_examples '404 when model experiments is unavailable' do
+ shared_examples 'requires read_model_experiments' do
context 'when user does not have access' do
- let(:model_experiments_enabled) { false }
+ let(:read_model_experiments) { false }
it_behaves_like 'renders 404'
end
@@ -100,7 +104,7 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
end
end
- it_behaves_like '404 when model experiments is unavailable' do
+ it_behaves_like 'requires read_model_experiments' do
before do
list_experiments
end
@@ -211,7 +215,7 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
end
it_behaves_like '404 if experiment does not exist'
- it_behaves_like '404 when model experiments is unavailable'
+ it_behaves_like 'requires read_model_experiments'
end
end
@@ -243,7 +247,7 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
end
it_behaves_like '404 if experiment does not exist'
- it_behaves_like '404 when model experiments is unavailable'
+ it_behaves_like 'requires read_model_experiments'
end
end
end
@@ -268,7 +272,14 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
end
it_behaves_like '404 if experiment does not exist'
- it_behaves_like '404 when model experiments is unavailable'
+
+ describe 'requires write_model_experiments' do
+ let(:write_model_experiments) { false }
+
+ it 'is 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
private
diff --git a/spec/requests/projects/ml/models_controller_spec.rb b/spec/requests/projects/ml/models_controller_spec.rb
new file mode 100644
index 00000000000..d03748c8dff
--- /dev/null
+++ b/spec/requests/projects/ml/models_controller_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Ml::ModelsController, feature_category: :mlops do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
+ let_it_be(:model1_a) { create(:ml_model_package, project: project) }
+ let_it_be(:model1_b) { create(:ml_model_package, project: project, name: model1_a.name) }
+ let_it_be(:model2) { create(:ml_model_package, project: project) }
+
+ let(:model_registry_enabled) { true }
+
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_model_registry, project)
+ .and_return(model_registry_enabled)
+
+ sign_in(user)
+ end
+
+ describe 'GET index' do
+ subject(:index_request) do
+ list_models
+ response
+ end
+
+ it 'renders the template' do
+ expect(index_request).to render_template('projects/ml/models/index')
+ end
+
+ it 'fetches the models using the finder' do
+ expect(::Projects::Ml::ModelFinder).to receive(:new).with(project).and_call_original
+
+ index_request
+ end
+
+ it 'prepares model view using the presenter' do
+ expect(::Ml::ModelsIndexPresenter).to receive(:new).and_call_original
+
+ index_request
+ end
+
+ it 'does not perform N+1 sql queries' do
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { list_models }
+
+ create_list(:ml_model_package, 4, project: project)
+
+ expect { list_models }.not_to exceed_all_query_limit(control_count)
+ end
+
+ context 'when user does not have access' do
+ let(:model_registry_enabled) { false }
+
+ it 'renders 404' do
+ is_expected.to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ private
+
+ def list_models
+ get project_ml_models_path(project)
+ end
+end
diff --git a/spec/requests/projects/packages/package_files_controller_spec.rb b/spec/requests/projects/packages/package_files_controller_spec.rb
index e5849be9f13..4f1793b831d 100644
--- a/spec/requests/projects/packages/package_files_controller_spec.rb
+++ b/spec/requests/projects/packages/package_files_controller_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Projects::Packages::PackageFilesController, feature_category: :pa
subject
expect(response.headers['Content-Disposition'])
- .to eq(%Q(attachment; filename="#{filename}"; filename*=UTF-8''#{filename}))
+ .to eq(%(attachment; filename="#{filename}"; filename*=UTF-8''#{filename}))
end
it_behaves_like 'bumping the package last downloaded at field'
diff --git a/spec/requests/projects/service_desk/custom_email_controller_spec.rb b/spec/requests/projects/service_desk/custom_email_controller_spec.rb
new file mode 100644
index 00000000000..8ce238ab99c
--- /dev/null
+++ b/spec/requests/projects/service_desk/custom_email_controller_spec.rb
@@ -0,0 +1,380 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ServiceDesk::CustomEmailController, feature_category: :service_desk do
+ let_it_be_with_reload(:project) do
+ create(:project, :private, service_desk_enabled: true)
+ end
+
+ let_it_be(:custom_email_path) { project_service_desk_custom_email_path(project, format: :json) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:illegitimite_user) { create(:user) }
+
+ let(:message) { instance_double(Mail::Message) }
+ let(:error_cannot_create_custom_email) { s_("ServiceDesk|Cannot create custom email") }
+ let(:error_cannot_update_custom_email) { s_("ServiceDesk|Cannot update custom email") }
+ let(:error_does_not_exist) { s_('ServiceDesk|Custom email does not exist') }
+ let(:error_custom_email_exists) { s_('ServiceDesk|Custom email already exists') }
+
+ let(:custom_email_params) do
+ {
+ custom_email: 'user@example.com',
+ smtp_address: 'smtp.example.com',
+ smtp_port: '587',
+ smtp_username: 'user@example.com',
+ smtp_password: 'supersecret'
+ }
+ end
+
+ let(:empty_json_response) do
+ {
+ "custom_email" => nil,
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => nil,
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => nil,
+ "error_message" => nil
+ }
+ end
+
+ before_all do
+ project.add_developer(illegitimite_user)
+ project.add_maintainer(user)
+ end
+
+ shared_examples 'a json response with empty values' do
+ it 'returns json response with empty values' do
+ perform_request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(empty_json_response)
+ end
+ end
+
+ shared_examples 'a controller that responds with status' do |status|
+ it "responds with #{status} for GET custom email" do
+ get custom_email_path
+ expect(response).to have_gitlab_http_status(status)
+ end
+
+ it "responds with #{status} for POST custom email" do
+ post custom_email_path
+ expect(response).to have_gitlab_http_status(status)
+ end
+
+ it "responds with #{status} for PUT custom email" do
+ put custom_email_path
+ expect(response).to have_gitlab_http_status(status)
+ end
+
+ it "responds with #{status} for DELETE custom email" do
+ delete custom_email_path
+ expect(response).to have_gitlab_http_status(status)
+ end
+ end
+
+ shared_examples 'a controller with disabled feature flag with status' do |status|
+ context 'when feature flag service_desk_custom_email is disabled' do
+ before do
+ stub_feature_flags(service_desk_custom_email: false)
+ end
+
+ it_behaves_like 'a controller that responds with status', status
+ end
+ end
+
+ shared_examples 'a deletable resource' do
+ describe 'DELETE custom email' do
+ let(:perform_request) { delete custom_email_path }
+
+ it_behaves_like 'a json response with empty values'
+ end
+ end
+
+ context 'with legitimate user signed in' do
+ before do
+ sign_out(illegitimite_user)
+ sign_in(user)
+ end
+
+ # because CustomEmailController check_feature_flag_enabled responds
+ it_behaves_like 'a controller with disabled feature flag with status', :not_found
+
+ describe 'GET custom email' do
+ let(:perform_request) { get custom_email_path }
+
+ it_behaves_like 'a json response with empty values'
+ end
+
+ describe 'POST custom email' do
+ before do
+ # We send verification email directly
+ allow(message).to receive(:deliver)
+ allow(Notify).to receive(:service_desk_custom_email_verification_email).and_return(message)
+ end
+
+ it 'adds custom email and kicks of verification' do
+ post custom_email_path, params: custom_email_params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ "custom_email" => custom_email_params[:custom_email],
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "started",
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => custom_email_params[:smtp_address],
+ "error_message" => nil
+ )
+ end
+
+ context 'when custom_email param is not valid' do
+ it 'does not add custom email' do
+ post custom_email_path, params: custom_email_params.merge(custom_email: 'useratexample.com')
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ empty_json_response.merge("error_message" => error_cannot_create_custom_email)
+ )
+ end
+ end
+
+ context 'when smtp_password param is not valid' do
+ it 'does not add custom email' do
+ post custom_email_path, params: custom_email_params.merge(smtp_password: '2short')
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ empty_json_response.merge("error_message" => error_cannot_create_custom_email)
+ )
+ end
+ end
+
+ context 'when the verification process fails fast' do
+ before do
+ # Could not establish connection, invalid host etc.
+ allow(message).to receive(:deliver).and_raise(SocketError)
+ end
+
+ it 'adds custom email and kicks of verification and returns verification error state' do
+ post custom_email_path, params: custom_email_params
+
+ # In terms of "custom email object creation", failing fast on the
+ # verification is a legit state that we don't treat as an error.
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ "custom_email" => custom_email_params[:custom_email],
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "failed",
+ "custom_email_verification_error" => "smtp_host_issue",
+ "custom_email_smtp_address" => custom_email_params[:smtp_address],
+ "error_message" => nil
+ )
+ end
+ end
+ end
+
+ describe 'PUT custom email' do
+ let(:custom_email_params) { { custom_email_enabled: true } }
+
+ it 'does not update records' do
+ put custom_email_path, params: custom_email_params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ empty_json_response.merge("error_message" => error_cannot_update_custom_email)
+ )
+ end
+ end
+
+ describe 'DELETE custom email' do
+ it 'does not touch any records' do
+ delete custom_email_path
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ empty_json_response.merge("error_message" => error_does_not_exist)
+ )
+ end
+ end
+
+ context 'when custom email is set up' do
+ let!(:settings) { create(:service_desk_setting, project: project, custom_email: 'user@example.com') }
+ let!(:credential) { create(:service_desk_custom_email_credential, project: project) }
+
+ before do
+ project.reset
+ end
+
+ context 'and verification started' do
+ let!(:verification) do
+ create(:service_desk_custom_email_verification, project: project)
+ end
+
+ it_behaves_like 'a deletable resource'
+
+ describe 'GET custom email' do
+ it 'returns custom email in its current state' do
+ get custom_email_path
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ "custom_email" => "user@example.com",
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "started",
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => "smtp.example.com",
+ "error_message" => nil
+ )
+ end
+ end
+
+ describe 'POST custom email' do
+ it 'returns custom email in its current state' do
+ post custom_email_path, params: custom_email_params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ "custom_email" => custom_email_params[:custom_email],
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "started",
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => custom_email_params[:smtp_address],
+ "error_message" => error_custom_email_exists
+ )
+ end
+ end
+
+ describe 'PUT custom email' do
+ let(:custom_email_params) { { custom_email_enabled: true } }
+
+ it 'marks custom email as enabled' do
+ put custom_email_path, params: custom_email_params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ "custom_email" => "user@example.com",
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "started",
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => "smtp.example.com",
+ "error_message" => error_cannot_update_custom_email
+ )
+ end
+ end
+ end
+
+ context 'and verification finished' do
+ let!(:verification) do
+ create(:service_desk_custom_email_verification, project: project, state: :finished, token: nil)
+ end
+
+ it_behaves_like 'a deletable resource'
+
+ describe 'GET custom email' do
+ it 'returns custom email in its current state' do
+ get custom_email_path
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ "custom_email" => "user@example.com",
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "finished",
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => "smtp.example.com",
+ "error_message" => nil
+ )
+ end
+ end
+
+ describe 'PUT custom email' do
+ let(:custom_email_params) { { custom_email_enabled: true } }
+
+ it 'marks custom email as enabled' do
+ put custom_email_path, params: custom_email_params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ "custom_email" => "user@example.com",
+ "custom_email_enabled" => true,
+ "custom_email_verification_state" => "finished",
+ "custom_email_verification_error" => nil,
+ "custom_email_smtp_address" => "smtp.example.com",
+ "error_message" => nil
+ )
+ end
+ end
+ end
+
+ context 'and verification failed' do
+ let!(:verification) do
+ create(:service_desk_custom_email_verification,
+ project: project,
+ state: :failed,
+ token: nil,
+ error: :smtp_host_issue
+ )
+ end
+
+ it_behaves_like 'a deletable resource'
+
+ describe 'GET custom email' do
+ it 'returns custom email in its current state' do
+ get custom_email_path
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to include(
+ "custom_email" => "user@example.com",
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "failed",
+ "custom_email_verification_error" => "smtp_host_issue",
+ "custom_email_smtp_address" => "smtp.example.com",
+ "error_message" => nil
+ )
+ end
+ end
+
+ describe 'PUT custom email' do
+ let(:custom_email_params) { { custom_email_enabled: true } }
+
+ it 'does not mark custom email as enabled' do
+ put custom_email_path, params: custom_email_params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response).to include(
+ "custom_email" => "user@example.com",
+ "custom_email_enabled" => false,
+ "custom_email_verification_state" => "failed",
+ "custom_email_verification_error" => "smtp_host_issue",
+ "custom_email_smtp_address" => "smtp.example.com",
+ "error_message" => error_cannot_update_custom_email
+ )
+ end
+ end
+ end
+ end
+ end
+
+ context 'when user is anonymous' do
+ before do
+ sign_out(user)
+ sign_out(illegitimite_user)
+ end
+
+ # because Projects::ApplicationController :authenticate_user! responds
+ # with redirect to login page
+ it_behaves_like 'a controller that responds with status', :found
+ it_behaves_like 'a controller with disabled feature flag with status', :found
+ end
+
+ context 'with illegitimate user signed in' do
+ before do
+ sign_out(user)
+ sign_in(illegitimite_user)
+ end
+
+ it_behaves_like 'a controller that responds with status', :not_found
+ # because CustomEmailController check_feature_flag_enabled responds
+ it_behaves_like 'a controller with disabled feature flag with status', :not_found
+ end
+end
diff --git a/spec/requests/projects/service_desk_controller_spec.rb b/spec/requests/projects/service_desk_controller_spec.rb
new file mode 100644
index 00000000000..54fe176e244
--- /dev/null
+++ b/spec/requests/projects/service_desk_controller_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ServiceDeskController, feature_category: :service_desk do
+ let_it_be(:project) do
+ create(:project, :private, :custom_repo,
+ service_desk_enabled: true,
+ files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
+ end
+
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ project.add_maintainer(user)
+ end
+
+ before do
+ allow(Gitlab::Email::IncomingEmail).to receive(:enabled?).and_return(true)
+ allow(Gitlab::Email::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
+
+ sign_in(user)
+ end
+
+ describe 'GET #show' do
+ it 'returns service_desk JSON data' do
+ get project_service_desk_path(project, format: :json)
+
+ expect(json_response["service_desk_address"]).to match(/\A[^@]+@[^@]+\z/)
+ expect(json_response["service_desk_enabled"]).to be_truthy
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ context 'when user is not project maintainer' do
+ let(:guest) { create(:user) }
+
+ it 'renders 404' do
+ project.add_guest(guest)
+ sign_in(guest)
+
+ get project_service_desk_path(project, format: :json)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when issue template is present' do
+ it 'returns template_file_missing as false' do
+ create(:service_desk_setting, project: project, issue_template_key: 'service_desk')
+
+ get project_service_desk_path(project, format: :json)
+
+ response_hash = Gitlab::Json.parse(response.body)
+ expect(response_hash['template_file_missing']).to eq(false)
+ end
+ end
+
+ context 'when issue template file becomes outdated' do
+ it 'returns template_file_missing as true' do
+ service = ServiceDeskSetting.new(project_id: project.id, issue_template_key: 'deleted')
+ service.save!(validate: false)
+
+ get project_service_desk_path(project, format: :json)
+
+ expect(json_response['template_file_missing']).to eq(true)
+ end
+ end
+ end
+
+ describe 'PUT #update' do
+ it 'toggles services desk incoming email' do
+ project.update!(service_desk_enabled: false)
+
+ put project_service_desk_refresh_path(project, format: :json), params: { service_desk_enabled: true }
+
+ expect(json_response["service_desk_address"]).to be_present
+ expect(json_response["service_desk_enabled"]).to be_truthy
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'sets issue_template_key' do
+ put project_service_desk_refresh_path(project, format: :json), params: { issue_template_key: 'service_desk' }
+
+ settings = project.service_desk_setting
+ expect(settings).to be_present
+ expect(settings.issue_template_key).to eq('service_desk')
+ expect(json_response['template_file_missing']).to eq(false)
+ expect(json_response['issue_template_key']).to eq('service_desk')
+ end
+
+ it 'returns an error when update of service desk settings fails' do
+ put project_service_desk_refresh_path(project, format: :json), params: { issue_template_key: 'invalid key' }
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response['message']).to eq('Issue template key is empty or does not exist')
+ end
+
+ context 'when user cannot admin the project' do
+ let(:other_user) { create(:user) }
+
+ it 'renders 404' do
+ sign_in(other_user)
+ put project_service_desk_refresh_path(project, format: :json), params: { service_desk_enabled: true }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/requests/projects/tracing_controller_spec.rb b/spec/requests/projects/tracing_controller_spec.rb
new file mode 100644
index 00000000000..eecaa0d962a
--- /dev/null
+++ b/spec/requests/projects/tracing_controller_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::TracingController, feature_category: :tracing do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:user) { create(:user) }
+ let(:path) { nil }
+ let(:observability_tracing_ff) { true }
+
+ subject do
+ get path
+ response
+ end
+
+ describe 'GET #index' do
+ before do
+ stub_feature_flags(observability_tracing: observability_tracing_ff)
+ sign_in(user)
+ end
+
+ let(:path) { project_tracing_index_path(project) }
+
+ it_behaves_like 'observability csp policy' do
+ before_all do
+ project.add_developer(user)
+ end
+
+ let(:tested_path) { path }
+ end
+
+ context 'when user does not have permissions' do
+ it 'returns 404' do
+ expect(subject).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user has permissions' do
+ before_all do
+ project.add_developer(user)
+ end
+
+ it 'returns 200' do
+ expect(subject).to have_gitlab_http_status(:ok)
+ end
+
+ it 'renders the js-tracing element correctly' do
+ element = Nokogiri::HTML.parse(subject.body).at_css('#js-tracing')
+
+ expected_view_model = {
+ tracingUrl: Gitlab::Observability.tracing_url(project),
+ provisioningUrl: Gitlab::Observability.provisioning_url(project),
+ oauthUrl: Gitlab::Observability.oauth_url
+ }.to_json
+ expect(element.attributes['data-view-model'].value).to eq(expected_view_model)
+ end
+
+ context 'when feature is disabled' do
+ let(:observability_tracing_ff) { false }
+
+ it 'returns 404' do
+ expect(subject).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb
index f2d4e288ddc..365b20ad4aa 100644
--- a/spec/requests/search_controller_spec.rb
+++ b/spec/requests/search_controller_spec.rb
@@ -39,7 +39,8 @@ RSpec.describe SearchController, type: :request, feature_category: :global_searc
context 'for issues scope' do
let(:object) { :issue }
- let(:creation_args) { { project: project, title: 'foo' } }
+ let(:labels) { create_list(:label, 3, project: project) }
+ let(:creation_args) { { project: project, title: 'foo', labels: labels } }
let(:params) { { search: 'foo', scope: 'issues' } }
# some N+1 queries still exist
# each issue runs an extra query for group namespaces
@@ -50,8 +51,9 @@ RSpec.describe SearchController, type: :request, feature_category: :global_searc
context 'for merge_requests scope' do
let(:creation_traits) { [:unique_branches] }
+ let(:labels) { create_list(:label, 3, project: project) }
let(:object) { :merge_request }
- let(:creation_args) { { source_project: project, title: 'bar' } }
+ let(:creation_args) { { source_project: project, title: 'bar', labels: labels } }
let(:params) { { search: 'bar', scope: 'merge_requests' } }
# some N+1 queries still exist
# each merge request runs an extra query for project routes
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index c49dbb6a269..f96d7864782 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -598,15 +598,10 @@ RSpec.describe UsersController, feature_category: :user_management do
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).not_to be_empty
end
-
- it 'does not list projects aimed for deletion' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(assigns(:contributed_projects)).to eq([project])
- end
end
%i(html json).each do |format|
- context "format: #{format}" do
+ context "with format: #{format}" do
let(:format) { format }
context 'with public profile' do
@@ -626,6 +621,13 @@ RSpec.describe UsersController, feature_category: :user_management do
let(:user) { create(:admin) }
it_behaves_like 'renders contributed projects'
+
+ if format == :json
+ it 'does not list projects aimed for deletion' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).not_to include aimed_for_deletion_project.name
+ end
+ end
end
end
end
@@ -652,15 +654,10 @@ RSpec.describe UsersController, feature_category: :user_management do
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).not_to be_empty
end
-
- it 'does not list projects aimed for deletion' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(assigns(:starred_projects)).to eq([project])
- end
end
%i(html json).each do |format|
- context "format: #{format}" do
+ context "with format: #{format}" do
let(:format) { format }
context 'with public profile' do
@@ -680,6 +677,13 @@ RSpec.describe UsersController, feature_category: :user_management do
let(:user) { create(:admin) }
it_behaves_like 'renders starred projects'
+
+ if format == :json
+ it 'does not list projects aimed for deletion' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).not_to include aimed_for_deletion_project.name
+ end
+ end
end
end
end
diff --git a/spec/requests/verifies_with_email_spec.rb b/spec/requests/verifies_with_email_spec.rb
index 6325ecc1184..f3f8e4a1a83 100644
--- a/spec/requests/verifies_with_email_spec.rb
+++ b/spec/requests/verifies_with_email_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_redis_rate_limiting,
-feature_category: :user_management do
+ feature_category: :instance_resiliency do
include SessionHelpers
include EmailHelpers