diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-28 18:09:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-28 18:09:19 +0300 |
commit | 24623a974348595d33cc9be6881b285a026ff13b (patch) | |
tree | 20a28aa9b423a65c52ea5ca54c6fcb91dd9ddfd6 /spec | |
parent | 72ba138510cad0a8c4819ef0512153dd4dbced7f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
47 files changed, 1118 insertions, 637 deletions
diff --git a/spec/features/projects/activity/user_sees_activity_spec.rb b/spec/features/projects/activity/user_sees_activity_spec.rb index 5335b9d0e95..7940ded41a9 100644 --- a/spec/features/projects/activity/user_sees_activity_spec.rb +++ b/spec/features/projects/activity/user_sees_activity_spec.rb @@ -10,12 +10,14 @@ RSpec.describe 'Projects > Activity > User sees activity', feature_category: :gr before do create(:event, :created, project: project, target: issue, author: user) event = create(:push_event, project: project, author: user) - create(:push_event_payload, - event: event, - action: :created, - commit_to: '6d394385cf567f80a8fd85055db1ab4c5295806f', - ref: 'fix', - commit_count: 1) + create( + :push_event_payload, + event: event, + action: :created, + commit_to: '6d394385cf567f80a8fd85055db1ab4c5295806f', + ref: 'fix', + commit_count: 1 + ) end it 'shows the last push in the activity page', :js do diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb index 38a7bdf65c3..c4f20ff74b2 100644 --- a/spec/features/projects/badges/coverage_spec.rb +++ b/spec/features/projects/badges/coverage_spec.rb @@ -190,8 +190,15 @@ RSpec.describe 'test coverage badge', feature_category: :code_testing do end def show_test_coverage_badge(job: nil, min_good: nil, min_acceptable: nil, min_medium: nil) - visit coverage_project_badges_path(project, ref: :master, job: job, min_good: min_good, - min_acceptable: min_acceptable, min_medium: min_medium, format: :svg) + visit coverage_project_badges_path( + project, + ref: :master, + job: job, + min_good: min_good, + min_acceptable: min_acceptable, + min_medium: min_medium, + format: :svg + ) end def expect_coverage_badge(coverage) diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb index 2092af537e8..a888b5b977d 100644 --- a/spec/features/projects/branches/download_buttons_spec.rb +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -9,18 +9,24 @@ RSpec.describe 'Download buttons in branches page', feature_category: :groups_an let(:project) { create(:project, :repository) } let(:pipeline) do - create(:ci_pipeline, - project: project, - sha: project.commit('binary-encoding').sha, - ref: 'binary-encoding', # make sure the branch is in the 1st page! - status: status) + create( + :ci_pipeline, + project: project, + sha: project.commit('binary-encoding').sha, + ref: 'binary-encoding', # make sure the branch is in the 1st page! + status: status + ) end let!(:build) do - create(:ci_build, :success, :artifacts, - pipeline: pipeline, - status: pipeline.status, - name: 'build') + create( + :ci_build, + :success, + :artifacts, + pipeline: pipeline, + status: pipeline.status, + name: 'build' + ) end before do diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index 841a9cf78c5..50df7bb7ca5 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -95,15 +95,22 @@ RSpec.describe 'Branches', feature_category: :groups_and_projects do it 'shows only default_per_page active branches sorted by last updated' do visit project_branches_filtered_path(project, state: 'active') - expect(page).to have_content(sorted_branches(repository, count: Kaminari.config.default_per_page, - sort_by: :updated_desc, state: 'active')) + expect(page).to have_content(sorted_branches( + repository, + count: Kaminari.config.default_per_page, + sort_by: :updated_desc, + state: 'active' + )) end it 'shows only default_per_page branches sorted by last updated on All branches' do visit project_branches_filtered_path(project, state: 'all') - expect(page).to have_content(sorted_branches(repository, count: Kaminari.config.default_per_page, - sort_by: :updated_desc)) + expect(page).to have_content(sorted_branches( + repository, + count: Kaminari.config.default_per_page, + sort_by: :updated_desc + )) end end end diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb index dfd58a99953..54a189692ce 100644 --- a/spec/features/projects/commit/builds_spec.rb +++ b/spec/features/projects/commit/builds_spec.rb @@ -6,9 +6,7 @@ RSpec.describe 'project commit pipelines', :js, feature_category: :continuous_in let(:project) { create(:project, :repository) } before do - create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: 'master') + create(:ci_pipeline, project: project, sha: project.commit.sha, ref: 'master') user = create(:user) project.add_maintainer(user) diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index 3611efd1477..d2104799e79 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -7,11 +7,13 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js, feature_category: :sou context 'when commit has pipelines' do let(:pipeline) do - create(:ci_pipeline, - status: :running, - project: project, - ref: project.default_branch, - sha: project.commit.sha) + create( + :ci_pipeline, + status: :running, + project: project, + ref: project.default_branch, + sha: project.commit.sha + ) end let(:build) { create(:ci_build, pipeline: pipeline, status: :running) } diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 791f626b8d9..3513e249b63 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -175,9 +175,13 @@ RSpec.describe 'User browses commits', feature_category: :source_code_management let(:confidential_issue) { create(:issue, confidential: true, title: 'Secret issue!', project: project) } before do - project.repository.create_file(user, 'dummy-file', 'dummy content', - branch_name: 'feature', - message: "Linking #{confidential_issue.to_reference}") + project.repository.create_file( + user, + 'dummy-file', + 'dummy content', + branch_name: 'feature', + message: "Linking #{confidential_issue.to_reference}" + ) end context 'when the user cannot see confidential issues but was cached with a link', :use_clean_rails_memory_store_fragment_caching do diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index 0f903901984..11ea72b87a2 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -300,14 +300,12 @@ RSpec.describe 'Environment', feature_category: :groups_and_projects do context 'with manual action' do let(:action) do - create(:ci_build, :manual, pipeline: pipeline, - name: 'deploy to production', environment: environment.name) + create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production', environment: environment.name) end context 'when user has ability to trigger deployment' do let(:permissions) do - create(:protected_branch, :developers_can_merge, - name: action.ref, project: project) + create(:protected_branch, :developers_can_merge, name: action.ref, project: project) end it 'does show a play button' do @@ -331,8 +329,7 @@ RSpec.describe 'Environment', feature_category: :groups_and_projects do context 'when user has no ability to trigger a deployment' do let(:permissions) do - create(:protected_branch, :no_one_can_merge, - name: action.ref, project: project) + create(:protected_branch, :no_one_can_merge, name: action.ref, project: project) end it 'does not show a play button' do @@ -391,10 +388,7 @@ RSpec.describe 'Environment', feature_category: :groups_and_projects do end let(:deployment) do - create(:deployment, :success, - environment: environment, - deployable: build, - on_stop: 'close_app') + create(:deployment, :success, environment: environment, deployable: build, on_stop: 'close_app') end context 'when user has ability to stop environment' do @@ -411,8 +405,7 @@ RSpec.describe 'Environment', feature_category: :groups_and_projects do context 'when user has no ability to stop environment' do let(:permissions) do - create(:protected_branch, :no_one_can_merge, - name: action.ref, project: project) + create(:protected_branch, :no_one_can_merge, name: action.ref, project: project) end it 'does not allow to stop environment', :js do @@ -445,9 +438,7 @@ RSpec.describe 'Environment', feature_category: :groups_and_projects do describe 'environment folders', :js do context 'when folder name contains special charaters' do before do - create(:environment, project: project, - name: 'staging-1.0/review', - state: :available) + create(:environment, project: project, name: 'staging-1.0/review', state: :available) end it 'renders a correct environment folder' do @@ -465,8 +456,7 @@ RSpec.describe 'Environment', feature_category: :groups_and_projects do let(:project) { create(:project, :repository) } let!(:environment) do - create(:environment, :with_review_app, project: project, - ref: 'feature') + create(:environment, :with_review_app, project: project, ref: 'feature') end it 'user visits environment page', :js do diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 9f1f7d04339..3a2c7f0ac7b 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -159,9 +159,7 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery let(:project) { create(:project, :repository) } let!(:deployment) do - create(:deployment, :success, - environment: environment, - sha: project.commit.id) + create(:deployment, :success, environment: environment, sha: project.commit.id) end it 'shows deployment SHA and internal ID' do @@ -182,10 +180,7 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery end let!(:deployment) do - create(:deployment, :success, - environment: environment, - deployable: build, - sha: project.commit.id) + create(:deployment, :success, environment: environment, deployable: build, sha: project.commit.id) end before do @@ -241,10 +236,7 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery end let(:deployment) do - create(:deployment, :success, - environment: environment, - deployable: build, - on_stop: 'close_app') + create(:deployment, :success, environment: environment, deployable: build, on_stop: 'close_app') end it 'shows a stop button and dialog' do @@ -296,18 +288,11 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery let!(:build) { create(:ci_build, pipeline: pipeline) } let!(:delayed_job) do - create(:ci_build, :scheduled, - pipeline: pipeline, - name: 'delayed job', - stage: 'test') + create(:ci_build, :scheduled, pipeline: pipeline, name: 'delayed job', stage: 'test') end let!(:deployment) do - create(:deployment, - :success, - environment: environment, - deployable: build, - sha: project.commit.id) + create(:deployment, :success, environment: environment, deployable: build, sha: project.commit.id) end before do @@ -327,10 +312,7 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery context 'when delayed job is expired already' do let!(:delayed_job) do - create(:ci_build, :expired_scheduled, - pipeline: pipeline, - name: 'delayed job', - stage: 'test') + create(:ci_build, :expired_scheduled, pipeline: pipeline, name: 'delayed job', stage: 'test') end it "shows 00:00:00 as the remaining time" do @@ -365,9 +347,7 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery let(:project) { create(:project, :repository) } let!(:deployment) do - create(:deployment, :failed, - environment: environment, - sha: project.commit.id) + create(:deployment, :failed, environment: environment, sha: project.commit.id) end it 'does not show deployments', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/409990' do @@ -382,9 +362,7 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery let_it_be(:project) { create(:project, :repository) } let!(:deployment) do - create(:deployment, :running, - environment: environment, - sha: project.commit.id) + create(:deployment, :running, environment: environment, sha: project.commit.id) end it "renders the upcoming deployment", :aggregate_failures do @@ -443,14 +421,8 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery describe 'environments folders' do describe 'available environments' do before do - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-1', - state: :available) - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-2', - state: :available) + create(:environment, :will_auto_stop, project: project, name: 'staging/review-1', state: :available) + create(:environment, :will_auto_stop, project: project, name: 'staging/review-2', state: :available) end it 'users unfurls an environment folder' do @@ -470,14 +442,8 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery describe 'stopped environments' do before do - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-1', - state: :stopped) - create(:environment, :will_auto_stop, - project: project, - name: 'staging/review-2', - state: :stopped) + create(:environment, :will_auto_stop, project: project, name: 'staging/review-1', state: :stopped) + create(:environment, :will_auto_stop, project: project, name: 'staging/review-2', state: :stopped) end it 'users unfurls an environment folder' do @@ -497,12 +463,8 @@ RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery describe 'environments folders view' do before do - create(:environment, project: project, - name: 'staging.review/review-1', - state: :available) - create(:environment, project: project, - name: 'staging.review/review-2', - state: :available) + create(:environment, project: project, name: 'staging.review/review-1', state: :available) + create(:environment, project: project, name: 'staging.review/review-2', state: :available) end it 'user opens folder view' do diff --git a/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb index 852d7bca96a..2c8d7275fbf 100644 --- a/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb +++ b/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb @@ -9,8 +9,7 @@ RSpec.describe 'User deletes feature flag', :js, feature_category: :feature_flag let(:project) { create(:project, namespace: user.namespace) } let!(:feature_flag) do - create_flag(project, 'ci_live_trace', false, - description: 'For live trace feature') + create_flag(project, 'ci_live_trace', false, description: 'For live trace feature') end before do diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb index 9b3d19cfea3..81bd0523c70 100644 --- a/spec/features/projects/files/download_buttons_spec.rb +++ b/spec/features/projects/files/download_buttons_spec.rb @@ -7,18 +7,11 @@ RSpec.describe 'Projects > Files > Download buttons in files tree', feature_cate let(:user) { project.creator } let(:pipeline) do - create(:ci_pipeline, - project: project, - sha: project.commit.sha, - ref: project.default_branch, - status: 'success') + create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch, status: 'success') end let!(:build) do - create(:ci_build, :success, :artifacts, - pipeline: pipeline, - status: pipeline.status, - name: 'build') + create(:ci_build, :success, :artifacts, pipeline: pipeline, status: pipeline.status, name: 'build') end before do diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb index b4edd5c2729..6efe1eb1ad1 100644 --- a/spec/features/projects/files/editing_a_file_spec.rb +++ b/spec/features/projects/files/editing_a_file_spec.rb @@ -13,16 +13,14 @@ RSpec.describe 'Projects > Files > User wants to edit a file', feature_category: commit_message: "Committing First Update", file_path: ".gitignore", file_content: "First Update", - last_commit_sha: Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, - ".gitignore").sha + last_commit_sha: Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, ".gitignore").sha } end context 'when the user has write access' do before do sign_in user - visit project_edit_blob_path(project, - File.join(project.default_branch, '.gitignore')) + visit project_edit_blob_path(project, File.join(project.default_branch, '.gitignore')) end it 'file has been updated since the user opened the edit page' do @@ -43,8 +41,7 @@ RSpec.describe 'Projects > Files > User wants to edit a file', feature_category: before do forked_project sign_in user - visit project_edit_blob_path(project, - File.join(project.default_branch, '.gitignore')) + visit project_edit_blob_path(project, File.join(project.default_branch, '.gitignore')) end context 'and the forked project is ahead of the upstream project' do diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb index bfe1fd073c5..c8543764d15 100644 --- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js, -feature_category: :groups_and_projects do + feature_category: :groups_and_projects do include Features::WebIdeSpecHelpers let(:project) { create(:project_empty_repo) } diff --git a/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb b/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb index 645bfeb14e3..37b718061c6 100644 --- a/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb +++ b/spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' # This is a regression test for https://gitlab.com/gitlab-org/gitlab-foss/issues/37569 RSpec.describe 'Projects > Files > User browses a tree with a folder containing only a folder', :js, -feature_category: :groups_and_projects do + feature_category: :groups_and_projects do let(:project) { create(:project, :empty_repo) } let(:user) { project.first_owner } diff --git a/spec/features/projects/issues/viewing_issues_with_external_authorization_enabled_spec.rb b/spec/features/projects/issues/viewing_issues_with_external_authorization_enabled_spec.rb index 3d40bae8544..bf1b2f7e5cd 100644 --- a/spec/features/projects/issues/viewing_issues_with_external_authorization_enabled_spec.rb +++ b/spec/features/projects/issues/viewing_issues_with_external_authorization_enabled_spec.rb @@ -8,26 +8,19 @@ RSpec.describe 'viewing an issue with cross project references' do let(:user) { create(:user) } let(:other_project) do - create(:project, :public, - external_authorization_classification_label: 'other_label') + create(:project, :public, external_authorization_classification_label: 'other_label') end let(:other_issue) do - create(:issue, :closed, - title: 'I am in another project', - project: other_project) + create(:issue, :closed, title: 'I am in another project', project: other_project) end let(:other_confidential_issue) do - create(:issue, :confidential, :closed, - title: 'I am in another project and confidential', - project: other_project) + create(:issue, :confidential, :closed, title: 'I am in another project and confidential', project: other_project) end let(:other_merge_request) do - create(:merge_request, :closed, - title: 'I am a merge request in another project', - source_project: other_project) + create(:merge_request, :closed, title: 'I am a merge request in another project', source_project: other_project) end let(:description_referencing_other_issue) do @@ -39,15 +32,11 @@ RSpec.describe 'viewing an issue with cross project references' do let(:project) { create(:project) } let(:issue) do - create(:issue, - project: project, - description: description_referencing_other_issue) + create(:issue, project: project, description: description_referencing_other_issue) end let(:confidential_issue) do - create(:issue, :confidential, :closed, - title: "I am in the same project and confidential", - project: project) + create(:issue, :confidential, :closed, title: "I am in the same project and confidential", project: project) end before do diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index aeba53c22b6..77f95827d88 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -79,8 +79,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do context 'when a job can be retried' do let!(:job) do - create(:ci_build, pipeline: pipeline, - stage: 'test') + create(:ci_build, pipeline: pipeline, stage: 'test') end before do @@ -148,10 +147,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do context 'with downloadable artifacts' do let!(:with_artifacts) do - build = create(:ci_build, :success, - pipeline: pipeline, - name: 'rspec tests', - stage: 'test') + build = create(:ci_build, :success, pipeline: pipeline, name: 'rspec tests', stage: 'test') create(:ci_job_artifact, :archive, job: build) end @@ -167,10 +163,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do context 'with artifacts expired' do let!(:with_artifacts_expired) do - create(:ci_build, :expired, :success, - pipeline: pipeline, - name: 'rspec', - stage: 'test') + create(:ci_build, :expired, :success, pipeline: pipeline, name: 'rspec', stage: 'test') end before do @@ -188,8 +181,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do context 'column links' do let!(:job) do - create(:ci_build, pipeline: pipeline, - stage: 'test') + create(:ci_build, pipeline: pipeline, stage: 'test') end before do diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb index 6656ca3ef18..2780326cd35 100644 --- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb +++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Members > Group member cannot request access to their group project', -feature_category: :groups_and_projects do + feature_category: :groups_and_projects do let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:project, namespace: group) } diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb index 9db34cee5d6..47cd0d612b5 100644 --- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb +++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Projects > Members > Group requester cannot request access to project', :js, -feature_category: :groups_and_projects do + feature_category: :groups_and_projects do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public) } diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index 6f76424e377..9af36b4b2a9 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -38,9 +38,11 @@ RSpec.describe 'Projects > Members > User requests access', :js, feature_categor context 'code access is restricted' do it 'user can request access' do - project.project_feature.update!(repository_access_level: ProjectFeature::PRIVATE, - builds_access_level: ProjectFeature::PRIVATE, - merge_requests_access_level: ProjectFeature::PRIVATE) + project.project_feature.update!( + repository_access_level: ProjectFeature::PRIVATE, + builds_access_level: ProjectFeature::PRIVATE, + merge_requests_access_level: ProjectFeature::PRIVATE + ) visit project_path(project) expect(page).to have_content 'Request Access' diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 1f60996fac8..aeb01603872 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -21,42 +21,39 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do let!(:external_stage) { create(:ci_stage, name: 'external', pipeline: pipeline) } let!(:build_passed) do - create(:ci_build, :success, - pipeline: pipeline, stage: 'build', stage_idx: 0, name: 'build') + create(:ci_build, :success, pipeline: pipeline, stage: 'build', stage_idx: 0, name: 'build') end let!(:build_failed) do - create(:ci_build, :failed, - pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'test') + create(:ci_build, :failed, pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'test') end let!(:build_preparing) do - create(:ci_build, :preparing, - pipeline: pipeline, stage: 'deploy', stage_idx: 2, name: 'prepare') + create(:ci_build, :preparing, pipeline: pipeline, stage: 'deploy', stage_idx: 2, name: 'prepare') end let!(:build_running) do - create(:ci_build, :running, - pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'deploy') + create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'deploy') end let!(:build_manual) do - create(:ci_build, :manual, - pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'manual-build') + create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'manual-build') end let!(:build_scheduled) do - create(:ci_build, :scheduled, - pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'delayed-job') + create(:ci_build, :scheduled, pipeline: pipeline, stage: 'deploy', stage_idx: 3, name: 'delayed-job') end let!(:build_external) do - create(:generic_commit_status, status: 'success', - pipeline: pipeline, - name: 'jenkins', - ci_stage: external_stage, - ref: 'master', - target_url: 'http://gitlab.com/status') + create( + :generic_commit_status, + status: 'success', + pipeline: pipeline, + name: 'jenkins', + ci_stage: external_stage, + ref: 'master', + target_url: 'http://gitlab.com/status' + ) end end @@ -127,13 +124,16 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do describe 'pipeline stats text' do let(:finished_pipeline) do - create(:ci_pipeline, :success, project: project, - ref: 'master', sha: project.commit.id, user: user) + create(:ci_pipeline, :success, project: project, ref: 'master', sha: project.commit.id, user: user) end before do - finished_pipeline.update!(started_at: "2023-01-01 01:01:05", created_at: "2023-01-01 01:01:01", - finished_at: "2023-01-01 01:01:10", duration: 9) + finished_pipeline.update!( + started_at: "2023-01-01 01:01:05", + created_at: "2023-01-01 01:01:01", + finished_at: "2023-01-01 01:01:10", + duration: 9 + ) end context 'pipeline has finished' do @@ -332,13 +332,15 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do context 'when pipeline has a downstream pipeline' do let(:downstream_project) { create(:project, :repository, group: group) } let(:downstream_pipeline) do - create(:ci_pipeline, - status, - user: user, - project: downstream_project, - ref: 'master', - sha: downstream_project.commit.id, - child_of: pipeline) + create( + :ci_pipeline, + status, + user: user, + project: downstream_project, + ref: 'master', + sha: downstream_project.commit.id, + child_of: pipeline + ) end let!(:build) { create(:ci_build, status, pipeline: downstream_pipeline, user: user) } @@ -585,10 +587,13 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do context 'when pipeline ref does not exist in repository anymore' do let(:pipeline) do - create(:ci_empty_pipeline, project: project, - ref: 'non-existent', - sha: project.commit.id, - user: user) + create( + :ci_empty_pipeline, + project: project, + ref: 'non-existent', + sha: project.commit.id, + user: user + ) end before do @@ -612,10 +617,12 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do let(:target_project) { project } let(:merge_request) do - create(:merge_request, + create( + :merge_request, :with_detached_merge_request_pipeline, source_project: source_project, - target_project: target_project) + target_project: target_project + ) end let(:pipeline) do @@ -788,17 +795,23 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do let(:downstream) { create(:project, :repository) } let(:pipeline) do - create(:ci_pipeline, project: project, - ref: 'master', - sha: project.commit.id, - user: user) + create( + :ci_pipeline, + project: project, + ref: 'master', + sha: project.commit.id, + user: user + ) end let!(:bridge) do - create(:ci_bridge, pipeline: pipeline, - name: 'cross-build', - user: user, - downstream: downstream) + create( + :ci_bridge, + pipeline: pipeline, + name: 'cross-build', + user: user, + downstream: downstream + ) end describe 'GET /:project/-/pipelines/:id' do @@ -866,13 +879,20 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do let(:resource_group) { create(:ci_resource_group, project: project) } let!(:test_job) do - create(:ci_build, :pending, stage: 'test', name: 'test', - stage_idx: 1, pipeline: pipeline, project: project) + create(:ci_build, :pending, stage: 'test', name: 'test', stage_idx: 1, pipeline: pipeline, project: project) end let!(:deploy_job) do - create(:ci_build, :created, stage: 'deploy', name: 'deploy', - stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group) + create( + :ci_build, + :created, + stage: 'deploy', + name: 'deploy', + stage_idx: 2, + pipeline: pipeline, + project: project, + resource_group: resource_group + ) end describe 'GET /:project/-/pipelines/:id' do @@ -1131,8 +1151,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do context 'when user does have permission to retry build' do before do - create(:protected_branch, :developers_can_merge, - name: pipeline.ref, project: project) + create(:protected_branch, :developers_can_merge, name: pipeline.ref, project: project) end it 'shows retry button for failed build' do @@ -1239,11 +1258,13 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do include_context 'pipeline builds' let(:pipeline) do - create(:ci_pipeline, - project: project, - ref: 'master', - sha: project.commit.id, - user: user) + create( + :ci_pipeline, + project: project, + ref: 'master', + sha: project.commit.id, + user: user + ) end before do @@ -1259,12 +1280,14 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do context 'when pipeline has configuration errors' do let(:pipeline) do - create(:ci_pipeline, - :invalid, - project: project, - ref: 'master', - sha: project.commit.id, - user: user) + create( + :ci_pipeline, + :invalid, + project: project, + ref: 'master', + sha: project.commit.id, + user: user + ) end before do @@ -1302,10 +1325,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do context 'when pipeline is stuck' do let(:pipeline) do - create(:ci_pipeline, - project: project, - status: :created, - user: user) + create(:ci_pipeline, project: project, status: :created, user: user) end before do @@ -1325,12 +1345,14 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do let(:project) { create(:project, :repository, auto_devops_attributes: { enabled: true }) } let(:pipeline) do - create(:ci_pipeline, - :auto_devops_source, - project: project, - ref: 'master', - sha: project.commit.id, - user: user) + create( + :ci_pipeline, + :auto_devops_source, + project: project, + ref: 'master', + sha: project.commit.id, + user: user + ) end before do @@ -1348,21 +1370,25 @@ RSpec.describe 'Pipeline', :js, feature_category: :groups_and_projects do include_context 'pipeline builds' let(:pipeline) do - create(:ci_pipeline, - source: :merge_request_event, - project: merge_request.source_project, - ref: 'feature', - sha: merge_request.diff_head_sha, - user: user, - merge_request: merge_request) + create( + :ci_pipeline, + source: :merge_request_event, + project: merge_request.source_project, + ref: 'feature', + sha: merge_request.diff_head_sha, + user: user, + merge_request: merge_request + ) end let(:merge_request) do - create(:merge_request, - source_project: project, - source_branch: 'feature', - target_project: project, - target_branch: 'master') + create( + :merge_request, + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master' + ) end before do diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index c47c9c75f91..25eddf64f99 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -105,8 +105,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do context 'when pipeline is cancelable' do let!(:build) do - create(:ci_build, pipeline: pipeline, - stage: 'test') + create(:ci_build, pipeline: pipeline, stage: 'test') end before do @@ -135,8 +134,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do context 'when pipeline is retryable', :sidekiq_might_not_need_inline do let!(:build) do - create(:ci_build, pipeline: pipeline, - stage: 'test') + create(:ci_build, pipeline: pipeline, stage: 'test') end before do @@ -164,10 +162,12 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do context 'when pipeline is detached merge request pipeline' do let(:merge_request) do - create(:merge_request, - :with_detached_merge_request_pipeline, - source_project: source_project, - target_project: target_project) + create( + :merge_request, + :with_detached_merge_request_pipeline, + source_project: source_project, + target_project: target_project + ) end let!(:pipeline) { merge_request.all_pipelines.first } @@ -183,8 +183,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do within '.pipeline-tags' do expect(page).to have_content(expected_detached_mr_tag) - expect(page).to have_link(merge_request.iid, - href: project_merge_request_path(project, merge_request)) + expect(page).to have_link(merge_request.iid, href: project_merge_request_path(project, merge_request)) expect(page).not_to have_link(pipeline.ref) end @@ -202,11 +201,13 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do context 'when pipeline is merge request pipeline' do let(:merge_request) do - create(:merge_request, - :with_merge_request_pipeline, - source_project: source_project, - target_project: target_project, - merge_sha: target_project.commit.sha) + create( + :merge_request, + :with_merge_request_pipeline, + source_project: source_project, + target_project: target_project, + merge_sha: target_project.commit.sha + ) end let!(:pipeline) { merge_request.all_pipelines.first } @@ -222,8 +223,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do within '.pipeline-tags' do expect(page).not_to have_content(expected_detached_mr_tag) - expect(page).to have_link(merge_request.iid, - href: project_merge_request_path(project, merge_request)) + expect(page).to have_link(merge_request.iid, href: project_merge_request_path(project, merge_request)) expect(page).not_to have_link(pipeline.ref) end @@ -520,9 +520,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do context 'mini pipeline graph' do let!(:build) do - create(:ci_build, :pending, pipeline: pipeline, - stage: 'build', - name: 'build') + create(:ci_build, :pending, pipeline: pipeline, stage: 'build', name: 'build') end dropdown_selector = '[data-testid="mini-pipeline-graph-dropdown"]' @@ -554,9 +552,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do context 'for a failed pipeline' do let!(:build) do - create(:ci_build, :failed, pipeline: pipeline, - stage: 'build', - name: 'build') + create(:ci_build, :failed, pipeline: pipeline, stage: 'build', name: 'build') end it 'displays the failure reason' do @@ -624,10 +620,12 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do let(:project) { create(:project, :repository) } let(:pipeline) do - create(:ci_empty_pipeline, - project: project, - sha: project.commit.id, - user: user) + create( + :ci_empty_pipeline, + project: project, + sha: project.commit.id, + user: user + ) end let(:external_stage) { create(:ci_stage, name: 'external', pipeline: pipeline) } diff --git a/spec/features/projects/releases/user_views_release_spec.rb b/spec/features/projects/releases/user_views_release_spec.rb index efa0ebd761d..282b8958814 100644 --- a/spec/features/projects/releases/user_views_release_spec.rb +++ b/spec/features/projects/releases/user_views_release_spec.rb @@ -7,10 +7,12 @@ RSpec.describe 'User views Release', :js, feature_category: :continuous_delivery let(:user) { create(:user) } let(:release) do - create(:release, - project: project, - name: 'The first release', - description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') + create( + :release, + project: project, + name: 'The first release', + description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)' + ) end before do diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb index 98842f54015..66c4041241c 100644 --- a/spec/features/snippets/search_snippets_spec.rb +++ b/spec/features/snippets/search_snippets_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Search Snippets', :js, feature_category: :source_code_management visit dashboard_snippets_path submit_search('Middle') - select_search_scope('Titles and Descriptions') + select_search_scope(_("Snippets")) expect(page).to have_link(public_snippet.title) expect(page).to have_link(private_snippet.title) diff --git a/spec/frontend/fixtures/groups.rb b/spec/frontend/fixtures/groups.rb index 9c22ff176ff..e69287c879b 100644 --- a/spec/frontend/fixtures/groups.rb +++ b/spec/frontend/fixtures/groups.rb @@ -2,24 +2,39 @@ require 'spec_helper' -RSpec.describe 'Groups (JavaScript fixtures)', type: :controller do +RSpec.describe 'Groups (JavaScript fixtures)', feature_category: :groups_and_projects do + include ApiHelpers include JavaScriptFixturesHelpers - let(:user) { create(:user) } - let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre') } + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre') } + let_it_be(:projects) { create_list(:project, 2, namespace: group) } - before do - group.add_owner(user) - sign_in(user) - end + describe GroupsController, '(JavaScript fixtures)', type: :controller do + render_views - render_views + before do + group.add_owner(user) + sign_in(user) + end - describe GroupsController, '(JavaScript fixtures)', type: :controller do it 'groups/edit.html' do get :edit, params: { id: group } expect(response).to be_successful end end + + describe API::Groups, '(JavaScript fixtures)', type: :request do + before do + group.add_owner(user) + sign_in(user) + end + + it 'api/groups/projects/get.json' do + get api("/groups/#{group.id}/projects", user) + + expect(response).to be_successful + end + end end diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js index b474745790e..e32c50db8bf 100644 --- a/spec/frontend/groups/components/app_spec.js +++ b/spec/frontend/groups/components/app_spec.js @@ -93,10 +93,9 @@ describe('AppComponent', () => { page: 2, filterGroupsBy: 'git', sortBy: 'created_desc', - archived: true, }) .then(() => { - expect(getGroupsSpy).toHaveBeenCalledWith(1, 2, 'git', 'created_desc', true); + expect(getGroupsSpy).toHaveBeenCalledWith(1, 2, 'git', 'created_desc'); }); }); @@ -154,7 +153,6 @@ describe('AppComponent', () => { filterGroupsBy: 'foobar', sortBy: null, updatePagination: true, - archived: null, }); return fetchPromise.then(() => { expect(vm.updateGroups).toHaveBeenCalledWith(mockSearchedGroups, true); @@ -177,7 +175,6 @@ describe('AppComponent', () => { page: 2, filterGroupsBy: null, sortBy: null, - archived: true, }); expect(vm.isLoading).toBe(true); @@ -186,7 +183,6 @@ describe('AppComponent', () => { filterGroupsBy: null, sortBy: null, updatePagination: true, - archived: true, }); return fetchPagePromise.then(() => { @@ -471,7 +467,7 @@ describe('AppComponent', () => { it('calls API with expected params', () => { emitFetchFilteredAndSortedGroups(); - expect(getGroupsSpy).toHaveBeenCalledWith(undefined, undefined, search, sort, undefined); + expect(getGroupsSpy).toHaveBeenCalledWith(undefined, undefined, search, sort); }); it('updates pagination', () => { diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js index ca852f398d0..8db69295ac4 100644 --- a/spec/frontend/groups/components/overview_tabs_spec.js +++ b/spec/frontend/groups/components/overview_tabs_spec.js @@ -10,26 +10,29 @@ import SharedProjectsEmptyState from '~/groups/components/empty_states/shared_pr import ArchivedProjectsEmptyState from '~/groups/components/empty_states/archived_projects_empty_state.vue'; import GroupsStore from '~/groups/store/groups_store'; import GroupsService from '~/groups/service/groups_service'; +import ArchivedProjectsService from '~/groups/service/archived_projects_service'; import { createRouter } from '~/groups/init_overview_tabs'; import eventHub from '~/groups/event_hub'; import { ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED, - OVERVIEW_TABS_SORTING_ITEMS, + SORTING_ITEM_NAME, + SORTING_ITEM_UPDATED, + SORTING_ITEM_STARS, } from '~/groups/constants'; import axios from '~/lib/utils/axios_utils'; import waitForPromises from 'helpers/wait_for_promises'; Vue.component('GroupFolder', GroupFolderComponent); const router = createRouter(); -const [SORTING_ITEM_NAME, , SORTING_ITEM_UPDATED] = OVERVIEW_TABS_SORTING_ITEMS; describe('OverviewTabs', () => { let wrapper; let axiosMock; const defaultProvide = { + groupId: '1', endpoints: { subgroups_and_projects: '/groups/foobar/-/children.json', shared: '/groups/foobar/-/shared_projects.json', @@ -92,7 +95,10 @@ describe('OverviewTabs', () => { expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({ action: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, store: new GroupsStore({ showSchemaMarkup: true }), - service: new GroupsService(defaultProvide.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]), + service: new GroupsService( + defaultProvide.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS], + defaultProvide.initialSort, + ), }); await waitForPromises(); @@ -115,7 +121,10 @@ describe('OverviewTabs', () => { expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({ action: ACTIVE_TAB_SHARED, store: new GroupsStore(), - service: new GroupsService(defaultProvide.endpoints[ACTIVE_TAB_SHARED]), + service: new GroupsService( + defaultProvide.endpoints[ACTIVE_TAB_SHARED], + defaultProvide.initialSort, + ), }); expect(tabPanel.vm.$attrs.lazy).toBe(false); @@ -140,7 +149,7 @@ describe('OverviewTabs', () => { expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({ action: ACTIVE_TAB_ARCHIVED, store: new GroupsStore(), - service: new GroupsService(defaultProvide.endpoints[ACTIVE_TAB_ARCHIVED]), + service: new ArchivedProjectsService(defaultProvide.groupId, defaultProvide.initialSort), }); expect(tabPanel.vm.$attrs.lazy).toBe(false); @@ -219,7 +228,7 @@ describe('OverviewTabs', () => { it(`pushes expected route when ${tabToClick} tab is clicked`, async () => { await findTab(tabToClick).trigger('click'); - expect(routerMock.push).toHaveBeenCalledWith(expectedRoute); + expect(routerMock.push).toHaveBeenCalledWith(expect.objectContaining(expectedRoute)); }); }); @@ -304,6 +313,52 @@ describe('OverviewTabs', () => { sharedAssertions({ search: '', sort: SORTING_ITEM_UPDATED.asc }); }); + describe('when tab is changed', () => { + describe('when selected sort is supported', () => { + beforeEach(async () => { + await createComponent({ + route: { + name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, + params: { group: 'foo/bar/baz' }, + query: { sort: SORTING_ITEM_NAME.asc }, + }, + }); + }); + + it('adds sort query string', async () => { + await findTab(OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED]).trigger('click'); + + expect(routerMock.push).toHaveBeenCalledWith( + expect.objectContaining({ + query: { sort: SORTING_ITEM_NAME.asc }, + }), + ); + }); + }); + + describe('when selected sort is not supported', () => { + beforeEach(async () => { + await createComponent({ + route: { + name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, + params: { group: 'foo/bar/baz' }, + query: { sort: SORTING_ITEM_STARS.asc }, + }, + }); + }); + + it('defaults to sorting by name', async () => { + await findTab(OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED]).trigger('click'); + + expect(routerMock.push).toHaveBeenCalledWith( + expect.objectContaining({ + query: { sort: SORTING_ITEM_NAME.asc }, + }), + ); + }); + }); + }); + describe('when sort direction is changed', () => { beforeEach(async () => { await setup(); diff --git a/spec/frontend/groups/service/archived_projects_service_spec.js b/spec/frontend/groups/service/archived_projects_service_spec.js new file mode 100644 index 00000000000..3aec9d57ee1 --- /dev/null +++ b/spec/frontend/groups/service/archived_projects_service_spec.js @@ -0,0 +1,90 @@ +import projects from 'test_fixtures/api/groups/projects/get.json'; +import ArchivedProjectsService from '~/groups/service/archived_projects_service'; +import Api from '~/api'; + +jest.mock('~/api'); + +describe('ArchivedProjectsService', () => { + const groupId = 1; + let service; + + beforeEach(() => { + service = new ArchivedProjectsService(groupId, 'name_asc'); + }); + + describe('getGroups', () => { + const headers = { 'x-next-page': '2', 'x-page': '1', 'x-per-page': '20' }; + const page = 2; + const query = 'git'; + const sort = 'created_asc'; + + beforeEach(() => { + Api.groupProjects.mockResolvedValueOnce({ data: projects, headers }); + }); + + it('returns promise the resolves with formatted project', async () => { + await expect(service.getGroups(undefined, page, query, sort)).resolves.toEqual({ + data: projects.map((project) => { + return { + id: project.id, + name: project.name, + full_name: project.name_with_namespace, + markdown_description: project.description_html, + visibility: project.visibility, + avatar_url: project.avatar_url, + relative_path: `/${project.path_with_namespace}`, + edit_path: null, + leave_path: null, + can_edit: false, + can_leave: false, + can_remove: false, + type: 'project', + permission: null, + children: [], + parent_id: project.namespace.id, + project_count: 0, + subgroup_count: 0, + number_users_with_delimiter: 0, + star_count: project.star_count, + updated_at: project.updated_at, + marked_for_deletion: project.marked_for_deletion_at !== null, + last_activity_at: project.last_activity_at, + }; + }), + headers, + }); + + expect(Api.groupProjects).toHaveBeenCalledWith(groupId, query, { + archived: true, + page, + order_by: 'created_at', + sort: 'asc', + }); + }); + + describe.each` + sortArgument | expectedOrderByParameter | expectedSortParameter + ${'name_asc'} | ${'name'} | ${'asc'} + ${'name_desc'} | ${'name'} | ${'desc'} + ${'created_asc'} | ${'created_at'} | ${'asc'} + ${'created_desc'} | ${'created_at'} | ${'desc'} + ${'latest_activity_asc'} | ${'last_activity_at'} | ${'asc'} + ${'latest_activity_desc'} | ${'last_activity_at'} | ${'desc'} + ${undefined} | ${'name'} | ${'asc'} + `( + 'when the sort argument is $sortArgument', + ({ sortArgument, expectedSortParameter, expectedOrderByParameter }) => { + it(`calls the API with sort parameter set to ${expectedSortParameter} and order_by parameter set to ${expectedOrderByParameter}`, () => { + service.getGroups(undefined, page, query, sortArgument); + + expect(Api.groupProjects).toHaveBeenCalledWith(groupId, query, { + archived: true, + page, + order_by: expectedOrderByParameter, + sort: expectedSortParameter, + }); + }); + }, + ); + }); +}); diff --git a/spec/frontend/groups/service/groups_service_spec.js b/spec/frontend/groups/service/groups_service_spec.js index e037a6df1e2..ef0a7fde70a 100644 --- a/spec/frontend/groups/service/groups_service_spec.js +++ b/spec/frontend/groups/service/groups_service_spec.js @@ -7,7 +7,7 @@ describe('GroupsService', () => { let service; beforeEach(() => { - service = new GroupsService(mockEndpoint); + service = new GroupsService(mockEndpoint, 'created_asc'); }); describe('getGroups', () => { @@ -17,17 +17,28 @@ describe('GroupsService', () => { page: 2, filter: 'git', sort: 'created_asc', - archived: true, }; - service.getGroups(55, 2, 'git', 'created_asc', true); + service.getGroups(55, 2, 'git', 'created_asc'); expect(axios.get).toHaveBeenCalledWith(mockEndpoint, { params: { parent_id: 55 } }); - service.getGroups(null, 2, 'git', 'created_asc', true); + service.getGroups(null, 2, 'git', 'created_asc'); expect(axios.get).toHaveBeenCalledWith(mockEndpoint, { params }); }); + + describe('when sort argument is undefined', () => { + it('calls API with `initialSort` argument', () => { + jest.spyOn(axios, 'get').mockResolvedValue(); + + service.getGroups(undefined, 2, 'git', undefined); + + expect(axios.get).toHaveBeenCalledWith(mockEndpoint, { + params: { sort: 'created_asc', filter: 'git', page: 2 }, + }); + }); + }); }); describe('leaveGroup', () => { diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js index 7cf8633d749..eb57deb46b1 100644 --- a/spec/frontend/search/mock_data.js +++ b/spec/frontend/search/mock_data.js @@ -132,6 +132,13 @@ export const MOCK_NAVIGATION = { active: true, count: '2,430', }, + epics: { + label: 'Epics', + scope: 'epics', + link: '/search?scope=epics&search=et', + active: true, + count: '0', + }, merge_requests: { label: 'Merge requests', scope: 'merge_requests', @@ -496,6 +503,14 @@ export const MOCK_NAVIGATION_ITEMS = [ items: [], }, { + title: 'Epics', + icon: 'epic', + link: '/search?scope=epics&search=et', + is_active: true, + pill_count: '0', + items: [], + }, + { title: 'Merge requests', icon: 'merge-request', link: '/search?scope=merge_requests&search=et', @@ -505,7 +520,7 @@ export const MOCK_NAVIGATION_ITEMS = [ }, { title: 'Wiki', - icon: 'overview', + icon: 'book', link: '/search?scope=wiki_blobs&search=et', is_active: false, pill_count: '0', @@ -529,7 +544,7 @@ export const MOCK_NAVIGATION_ITEMS = [ }, { title: 'Milestones', - icon: 'tag', + icon: 'clock', link: '/search?scope=milestones&search=et', is_active: false, pill_count: '0', diff --git a/spec/frontend/search/sidebar/components/scope_legacy_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_legacy_navigation_spec.js index 6a94da31a1b..786ad806ea6 100644 --- a/spec/frontend/search/sidebar/components/scope_legacy_navigation_spec.js +++ b/spec/frontend/search/sidebar/components/scope_legacy_navigation_spec.js @@ -7,6 +7,8 @@ import ScopeLegacyNavigation from '~/search/sidebar/components/scope_legacy_navi Vue.use(Vuex); +const MOCK_NAVIGATION_ENTRIES = Object.entries(MOCK_NAVIGATION); + describe('ScopeLegacyNavigation', () => { let wrapper; @@ -55,12 +57,12 @@ describe('ScopeLegacyNavigation', () => { }); it('renders all nav item components', () => { - expect(findGlNavItems()).toHaveLength(9); + expect(findGlNavItems()).toHaveLength(MOCK_NAVIGATION_ENTRIES.length); }); it('has all proper links', () => { const linkAtPosition = 3; - const { link } = MOCK_NAVIGATION[Object.keys(MOCK_NAVIGATION)[linkAtPosition]]; + const { link } = MOCK_NAVIGATION_ENTRIES[linkAtPosition][1]; expect(findGlNavItems().at(linkAtPosition).attributes('href')).toBe(link); }); diff --git a/spec/frontend/search/sidebar/components/scope_sidebar_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_sidebar_navigation_spec.js index 4b71ff0bedc..86939bdc5d6 100644 --- a/spec/frontend/search/sidebar/components/scope_sidebar_navigation_spec.js +++ b/spec/frontend/search/sidebar/components/scope_sidebar_navigation_spec.js @@ -7,6 +7,8 @@ import { MOCK_QUERY, MOCK_NAVIGATION, MOCK_NAVIGATION_ITEMS } from '../../mock_d Vue.use(Vuex); +const MOCK_NAVIGATION_ENTRIES = Object.entries(MOCK_NAVIGATION); + describe('ScopeSidebarNavigation', () => { let wrapper; @@ -59,7 +61,7 @@ describe('ScopeSidebarNavigation', () => { }); it('renders all nav item components', () => { - expect(findNavItems()).toHaveLength(9); + expect(findNavItems()).toHaveLength(MOCK_NAVIGATION_ENTRIES.length); }); it('has all proper links', () => { diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 9df044c8a66..1b5f23a5e8e 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -503,6 +503,7 @@ RSpec.describe GroupsHelper do it 'returns expected hash' do expect(helper.group_overview_tabs_app_data(group)).to match( { + group_id: group.id, subgroups_and_projects_endpoint: including("/groups/#{group.path}/-/children.json"), shared_projects_endpoint: including("/groups/#{group.path}/-/shared_projects.json"), archived_projects_endpoint: including("/groups/#{group.path}/-/children.json?archived=only"), diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 5c55c1d604a..6634e7dbf27 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -1396,9 +1396,9 @@ RSpec.describe SearchHelper, feature_category: :global_search do context 'data' do where(:scope, :label, :data, :search, :active_scope) do "projects" | "Projects" | { qa_selector: 'projects_tab' } | nil | "projects" - "snippet_titles" | "Titles and Descriptions" | nil | { snippets: "test" } | "code" + "snippet_titles" | "Snippets" | nil | { snippets: "test" } | "code" "projects" | "Projects" | { qa_selector: 'projects_tab' } | nil | "issue" - "snippet_titles" | "Titles and Descriptions" | nil | { snippets: "test" } | "snippet_titles" + "snippet_titles" | "Snippets" | nil | { snippets: "test" } | "snippet_titles" end with_them do diff --git a/spec/lib/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb index 0c5d587d8e8..ad73665326a 100644 --- a/spec/lib/expand_variables_spec.rb +++ b/spec/lib/expand_variables_spec.rb @@ -3,7 +3,7 @@ require 'fast_spec_helper' require 'rspec-parameterized' -RSpec.describe ExpandVariables do +RSpec.describe ExpandVariables, feature_category: :secrets_management do shared_examples 'common variable expansion' do |expander| using RSpec::Parameterized::TableSyntax @@ -35,7 +35,14 @@ RSpec.describe ExpandVariables do { key: 'variable', value: 'value' } ] }, - "simple expansions": { + "expansion using %": { + value: 'key%variable%', + result: 'keyvalue', + variables: [ + { key: 'variable', value: 'value' } + ] + }, + "multiple simple expansions": { value: 'key$variable$variable2', result: 'keyvalueresult', variables: [ @@ -43,7 +50,7 @@ RSpec.describe ExpandVariables do { key: 'variable2', value: 'result' } ] }, - "complex expansions": { + "multiple complex expansions": { value: 'key${variable}${variable2}', result: 'keyvalueresult', variables: [ @@ -51,6 +58,15 @@ RSpec.describe ExpandVariables do { key: 'variable2', value: 'result' } ] }, + "nested expansion is not expanded": { + value: 'key$variable$variable2', + result: 'keyvalue$variable3', + variables: [ + { key: 'variable', value: 'value' }, + { key: 'variable2', value: '$variable3' }, + { key: 'variable3', value: 'result' } + ] + }, "out-of-order expansion": { value: 'key$variable2$variable', result: 'keyresultvalue', @@ -99,10 +115,86 @@ RSpec.describe ExpandVariables do end end + shared_examples 'file variable expansion with expand_file_refs true' do |expander| + using RSpec::Parameterized::TableSyntax + + where do + { + "simple with a file variable": { + value: 'key$variable', + result: 'keyvalue', + variables: [ + { key: 'variable', value: 'value', file: true } + ] + }, + "complex expansion with a file variable": { + value: 'key${variable}', + result: 'keyvalue', + variables: [ + { key: 'variable', value: 'value', file: true } + ] + }, + "expansion using % with a file variable": { + value: 'key%variable%', + result: 'keyvalue', + variables: [ + { key: 'variable', value: 'value', file: true } + ] + } + } + end + + with_them do + subject { expander.call(value, variables, expand_file_refs: true) } + + it { is_expected.to eq(result) } + end + end + + shared_examples 'file variable expansion with expand_file_refs false' do |expander| + using RSpec::Parameterized::TableSyntax + + where do + { + "simple with a file variable": { + value: 'key$variable', + result: 'key$variable', + variables: [ + { key: 'variable', value: 'value', file: true } + ] + }, + "complex expansion with a file variable": { + value: 'key${variable}', + result: 'key${variable}', + variables: [ + { key: 'variable', value: 'value', file: true } + ] + }, + "expansion using % with a file variable": { + value: 'key%variable%', + result: 'key%variable%', + variables: [ + { key: 'variable', value: 'value', file: true } + ] + } + } + end + + with_them do + subject { expander.call(value, variables, expand_file_refs: false) } + + it { is_expected.to eq(result) } + end + end + describe '#expand' do context 'table tests' do it_behaves_like 'common variable expansion', described_class.method(:expand) + it_behaves_like 'file variable expansion with expand_file_refs true', described_class.method(:expand) + + it_behaves_like 'file variable expansion with expand_file_refs false', described_class.method(:expand) + context 'with missing variables' do using RSpec::Parameterized::TableSyntax @@ -169,6 +261,10 @@ RSpec.describe ExpandVariables do context 'table tests' do it_behaves_like 'common variable expansion', described_class.method(:expand_existing) + it_behaves_like 'file variable expansion with expand_file_refs true', described_class.method(:expand_existing) + + it_behaves_like 'file variable expansion with expand_file_refs false', described_class.method(:expand_existing) + context 'with missing variables' do using RSpec::Parameterized::TableSyntax diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index b864dba58de..603609e5e62 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate describe 'constants' do it 'API_SCOPES contains all scopes for API access' do - expect(subject::API_SCOPES).to match_array %i[api read_user read_api] + expect(subject::API_SCOPES).to match_array %i[api read_user read_api create_runner] end it 'ADMIN_SCOPES contains all scopes for ADMIN access' do @@ -40,29 +40,29 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate end it 'contains all non-default scopes' do - expect(subject.all_available_scopes).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode read_observability write_observability] + expect(subject.all_available_scopes).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode read_observability write_observability create_runner] end it 'contains for non-admin user all non-default scopes without ADMIN access and without observability scopes' do user = build_stubbed(:user, admin: false) - expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry] + expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry create_runner] end it 'contains for admin user all non-default scopes with ADMIN access and without observability scopes' do user = build_stubbed(:user, admin: true) - expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode] + expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode create_runner] end it 'contains for project all resource bot scopes without observability scopes' do - expect(subject.available_scopes_for(project)).to match_array %i[api read_api read_repository write_repository read_registry write_registry] + expect(subject.available_scopes_for(project)).to match_array %i[api read_api read_repository write_repository read_registry write_registry create_runner] end it 'contains for group all resource bot scopes' do group = build_stubbed(:group) - expect(subject.available_scopes_for(group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry read_observability write_observability] + expect(subject.available_scopes_for(group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry read_observability write_observability create_runner] end it 'contains for unsupported type no scopes' do @@ -70,7 +70,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate end it 'optional_scopes contains all non-default scopes' do - expect(subject.optional_scopes).to match_array %i[read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode openid profile email read_observability write_observability] + expect(subject.optional_scopes).to match_array %i[read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode openid profile email read_observability write_observability create_runner] end context 'with observability_group_tab feature flag' do @@ -82,7 +82,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate it 'contains for group all resource bot scopes without observability scopes' do group = build_stubbed(:group) - expect(subject.available_scopes_for(group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry] + expect(subject.available_scopes_for(group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry create_runner] end end @@ -94,23 +94,23 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate end it 'contains for other group all resource bot scopes including observability scopes' do - expect(subject.available_scopes_for(group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry read_observability write_observability] + expect(subject.available_scopes_for(group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry read_observability write_observability create_runner] end it 'contains for admin user all non-default scopes with ADMIN access and without observability scopes' do user = build_stubbed(:user, admin: true) - expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode] + expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo admin_mode create_runner] end it 'contains for project all resource bot scopes without observability scopes' do - expect(subject.available_scopes_for(project)).to match_array %i[api read_api read_repository write_repository read_registry write_registry] + expect(subject.available_scopes_for(project)).to match_array %i[api read_api read_repository write_repository read_registry write_registry create_runner] end it 'contains for other group all resource bot scopes without observability scopes' do other_group = build_stubbed(:group) - expect(subject.available_scopes_for(other_group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry] + expect(subject.available_scopes_for(other_group)).to match_array %i[api read_api read_repository write_repository read_registry write_registry create_runner] end end end @@ -351,6 +351,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate 'read_api' | described_class.read_only_authentication_abilities 'read_repository' | [:download_code] 'write_repository' | [:download_code, :push_code] + 'create_runner' | [:create_instance_runner, :create_runner] 'read_user' | [] 'sudo' | [] 'openid' | [] @@ -412,6 +413,12 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate expect_results_with_abilities(personal_access_token, [:download_code, :push_code]) end + it 'succeeds for personal access tokens with the `create_runner` scope' do + personal_access_token = create(:personal_access_token, scopes: ['create_runner']) + + expect_results_with_abilities(personal_access_token, [:create_instance_runner, :create_runner]) + end + context 'when registry is enabled' do before do stub_container_registry_config(enabled: true) diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb index 181e37de9b9..006f7c2ebe6 100644 --- a/spec/lib/gitlab/ci/variables/collection_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -3,6 +3,62 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Variables::Collection, feature_category: :secrets_management do + describe '.fabricate' do + using RSpec::Parameterized::TableSyntax + + where do + { + "given an array of variables": { + input: [ + { key: 'VAR1', value: 'value1' }, + { key: 'VAR2', value: 'value2' } + ] + }, + "given a hash of variables": { + input: { 'VAR1' => 'value1', 'VAR2' => 'value2' } + }, + "given a proc that evaluates to an array": { + input: -> do + [ + { key: 'VAR1', value: 'value1' }, + { key: 'VAR2', value: 'value2' } + ] + end + }, + "given a proc that evaluates to a hash": { + input: -> do + { 'VAR1' => 'value1', 'VAR2' => 'value2' } + end + }, + "given a collection": { + input: Gitlab::Ci::Variables::Collection.new( + [ + { key: 'VAR1', value: 'value1' }, + { key: 'VAR2', value: 'value2' } + ] + ) + } + } + end + + with_them do + subject(:collection) { Gitlab::Ci::Variables::Collection.fabricate(input) } + + it 'returns a collection' do + expect(collection).to be_a(Gitlab::Ci::Variables::Collection) + expect(collection.size).to eq(2) + expect(collection.map(&:key)).to contain_exactly('VAR1', 'VAR2') + expect(collection.map(&:value)).to contain_exactly('value1', 'value2') + end + end + + context 'when given an unrecognized type' do + it 'raises error' do + expect { Gitlab::Ci::Variables::Collection.fabricate(1) }.to raise_error(ArgumentError) + end + end + end + describe '.new' do it 'can be initialized with an array' do variable = { key: 'VAR', value: 'value', public: true, masked: false } diff --git a/spec/lib/gitlab/legacy_github_import/client_spec.rb b/spec/lib/gitlab/legacy_github_import/client_spec.rb index 757bdd8dd6c..4aea7308e37 100644 --- a/spec/lib/gitlab/legacy_github_import/client_spec.rb +++ b/spec/lib/gitlab/legacy_github_import/client_spec.rb @@ -4,7 +4,8 @@ require 'spec_helper' RSpec.describe Gitlab::LegacyGithubImport::Client, feature_category: :importers do let(:token) { '123456' } - let(:github_provider) { GitlabSettings::Options.build('app_id' => 'asd123', 'app_secret' => 'asd123', 'name' => 'github', 'args' => { 'client_options' => {} }) } + let(:github_provider) { GitlabSettings::Options.build('app_id' => 'asd123', 'app_secret' => 'asd123', 'name' => 'github', 'args' => { 'client_options' => client_options }) } + let(:client_options) { {} } let(:wait_for_rate_limit_reset) { true } subject(:client) { described_class.new(token, wait_for_rate_limit_reset: wait_for_rate_limit_reset) } @@ -13,8 +14,19 @@ RSpec.describe Gitlab::LegacyGithubImport::Client, feature_category: :importers allow(Gitlab.config.omniauth).to receive(:providers).and_return([github_provider]) end - it 'convert OAuth2 client options to symbols' do - expect(client.client.options.keys).to all(be_kind_of(Symbol)) + context 'with client options' do + let(:client_options) do + { + 'authorize_url' => 'https://github.com/login/oauth/authorize', + 'token_url' => 'https://github.com/login/oauth/access_token' + } + end + + it 'convert OAuth2 client options to symbols' do + expect(client.client.options.keys).to all(be_kind_of(Symbol)) + expect(client.client.options[:authorize_url]).to eq(client_options['authorize_url']) + expect(client.client.options[:token_url]).to eq(client_options['token_url']) + end end it 'does not crash (e.g. GitlabSettings::MissingSetting) when verify_ssl config is not present' 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/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/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb index c43f1e5264e..15f2cc0990c 100644 --- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb +++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb @@ -1094,34 +1094,6 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category process_pipeline end end - - context 'when FF `ci_reset_skipped_jobs_in_atomic_processing` is disabled' do - before do - stub_feature_flags(ci_reset_skipped_jobs_in_atomic_processing: false) - - process_pipeline # First pipeline processing - - # Change the manual jobs from stopped to alive status - manual1.enqueue! - manual2.enqueue! - - mock_play_jobs_during_processing([manual1, manual2]) - end - - it 'does not run ResetSkippedJobsService' do - expect(Ci::ResetSkippedJobsService).not_to receive(:new) - - process_pipeline - - expect(all_builds_names_and_statuses).to eq(statuses_2) - end - - it 'does not log event' do - expect(Gitlab::AppJsonLogger).not_to receive(:info) - - process_pipeline - end - end end context 'when a bridge job has parallel:matrix config', :sidekiq_inline do diff --git a/spec/services/packages/nuget/extract_metadata_content_service_spec.rb b/spec/services/packages/nuget/extract_metadata_content_service_spec.rb new file mode 100644 index 00000000000..ff1b26e8b28 --- /dev/null +++ b/spec/services/packages/nuget/extract_metadata_content_service_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Nuget::ExtractMetadataContentService, feature_category: :package_registry do + let(:nuspec_file_content) { fixture_file(nuspec_filepath) } + + let(:service) { described_class.new(nuspec_file_content) } + + describe '#execute' do + subject { service.execute.payload } + + context 'with nuspec file content' do + context 'with dependencies' do + let(:nuspec_filepath) { 'packages/nuget/with_dependencies.nuspec' } + + it { is_expected.to have_key(:package_dependencies) } + + it 'extracts dependencies' do + dependencies = subject[:package_dependencies] + + expect(dependencies).to include(name: 'Moqi', version: '2.5.6') + expect(dependencies).to include(name: 'Castle.Core') + expect(dependencies).to include(name: 'Test.Dependency', version: '2.3.7', + target_framework: '.NETStandard2.0') + expect(dependencies).to include(name: 'Newtonsoft.Json', version: '12.0.3', + target_framework: '.NETStandard2.0') + end + end + + context 'with package types' do + let(:nuspec_filepath) { 'packages/nuget/with_package_types.nuspec' } + + it { is_expected.to have_key(:package_types) } + + it 'extracts package types' do + expect(subject[:package_types]).to include('SymbolsPackage') + end + end + + context 'with a nuspec file with metadata' do + let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' } + + it { expect(subject[:package_tags].sort).to eq(%w[foo bar test tag1 tag2 tag3 tag4 tag5].sort) } + end + end + + context 'with a nuspec file content with metadata' do + let_it_be(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' } + + it 'returns the correct metadata' do + expected_metadata = { + authors: 'Author Test', + description: 'Description Test', + license_url: 'https://opensource.org/licenses/MIT', + project_url: 'https://gitlab.com/gitlab-org/gitlab', + icon_url: 'https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png' + } + + expect(subject.slice(*expected_metadata.keys)).to eq(expected_metadata) + end + end + end +end diff --git a/spec/services/packages/nuget/extract_metadata_file_service_spec.rb b/spec/services/packages/nuget/extract_metadata_file_service_spec.rb new file mode 100644 index 00000000000..412c22fe8de --- /dev/null +++ b/spec/services/packages/nuget/extract_metadata_file_service_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Nuget::ExtractMetadataFileService, feature_category: :package_registry do + let_it_be(:package_file) { create(:nuget_package).package_files.first } + + let(:service) { described_class.new(package_file.id) } + + describe '#execute' do + subject { service.execute } + + shared_examples 'raises an error' do |error_message| + it { expect { subject }.to raise_error(described_class::ExtractionError, error_message) } + end + + context 'with valid package file id' do + expected_metadata = <<~XML.squish + <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> + <metadata> + <id>DummyProject.DummyPackage</id> + <version>1.0.0</version> + <title>Dummy package</title> + <authors>Test</authors> + <owners>Test</owners> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <description>This is a dummy project</description> + <dependencies> + <group targetFramework=".NETCoreApp3.0"> + <dependency id="Newtonsoft.Json" version="12.0.3" exclude="Build,Analyzers" /> + </group> + </dependencies> + </metadata> + </package> + XML + + it 'returns the nuspec file content' do + expect(subject.payload.squish).to include(expected_metadata) + end + end + + context 'with invalid package file id' do + let(:package_file) { instance_double('Packages::PackageFile', id: 555) } + + it_behaves_like 'raises an error', 'invalid package file' + end + + context 'when linked to a non nuget package' do + before do + package_file.package.maven! + end + + it_behaves_like 'raises an error', 'invalid package file' + end + + context 'with a 0 byte package file id' do + before do + allow_next_instance_of(Packages::PackageFileUploader) do |instance| + allow(instance).to receive(:size).and_return(0) + end + end + + it_behaves_like 'raises an error', 'invalid package file' + end + + context 'without the nuspec file' do + before do + allow_next_instance_of(Zip::File) do |instance| + allow(instance).to receive(:glob).and_return([]) + end + end + + it_behaves_like 'raises an error', 'nuspec file not found' + end + + context 'with a too big nuspec file' do + before do + allow_next_instance_of(Zip::File) do |instance| + allow(instance).to receive(:glob).and_return([instance_double('File', size: 6.megabytes)]) + end + end + + it_behaves_like 'raises an error', 'nuspec file too big' + end + + context 'with a corrupted nupkg file with a wrong entry size' do + let(:nupkg_fixture_path) { expand_fixture_path('packages/nuget/corrupted_package.nupkg') } + + before do + allow(Zip::File).to receive(:new).and_return(Zip::File.new(nupkg_fixture_path, false, false)) + end + + it_behaves_like 'raises an error', + <<~ERROR.squish + nuspec file has the wrong entry size: entry 'DummyProject.DummyPackage.nuspec' should be 255B, + but is larger when inflated. + ERROR + end + end +end diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb index 8954b89971e..c8c06414830 100644 --- a/spec/services/packages/nuget/metadata_extraction_service_spec.rb +++ b/spec/services/packages/nuget/metadata_extraction_service_spec.rb @@ -5,17 +5,33 @@ require 'spec_helper' RSpec.describe Packages::Nuget::MetadataExtractionService, feature_category: :package_registry do let_it_be(:package_file) { create(:nuget_package).package_files.first } - let(:service) { described_class.new(package_file.id) } + subject { described_class.new(package_file.id) } describe '#execute' do - subject { service.execute } - - shared_examples 'raises an error' do |error_message| - it { expect { subject }.to raise_error(described_class::ExtractionError, error_message) } + let(:nuspec_file_content) do + <<~XML.squish + <?xml version="1.0" encoding="utf-8"?> + <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> + <metadata> + <id>DummyProject.DummyPackage</id> + <version>1.0.0</version> + <title>Dummy package</title> + <authors>Test</authors> + <owners>Test</owners> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <description>This is a dummy project</description> + <dependencies> + <group targetFramework=".NETCoreApp3.0"> + <dependency id="Newtonsoft.Json" version="12.0.3" exclude="Build,Analyzers" /> + </group> + </dependencies> + </metadata> + </package> + XML end - context 'with valid package file id' do - expected_metadata = { + let(:expected_metadata) do + { package_name: 'DummyProject.DummyPackage', package_version: '1.0.0', authors: 'Test', @@ -30,113 +46,21 @@ RSpec.describe Packages::Nuget::MetadataExtractionService, feature_category: :pa package_tags: [], package_types: [] } - - it { is_expected.to eq(expected_metadata) } end - context 'with nuspec file' do - before do - allow(service).to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath)) - end - - context 'with dependencies' do - let(:nuspec_filepath) { 'packages/nuget/with_dependencies.nuspec' } - - it { is_expected.to have_key(:package_dependencies) } - - it 'extracts dependencies' do - dependencies = subject[:package_dependencies] - - expect(dependencies).to include(name: 'Moqi', version: '2.5.6') - expect(dependencies).to include(name: 'Castle.Core') - expect(dependencies).to include(name: 'Test.Dependency', version: '2.3.7', target_framework: '.NETStandard2.0') - expect(dependencies).to include(name: 'Newtonsoft.Json', version: '12.0.3', target_framework: '.NETStandard2.0') - end - end - - context 'with package types' do - let(:nuspec_filepath) { 'packages/nuget/with_package_types.nuspec' } - - it { is_expected.to have_key(:package_types) } - - it 'extracts package types' do - expect(subject[:package_types]).to include('SymbolsPackage') + it 'calls the necessary services and executes the metadata extraction' do + expect(::Packages::Nuget::ExtractMetadataFileService).to receive(:new).with(package_file.id) do + double.tap do |service| + expect(service).to receive(:execute).and_return(double(payload: nuspec_file_content)) end end - context 'with a nuspec file with metadata' do - let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' } - - it { expect(subject[:package_tags].sort).to eq(%w(foo bar test tag1 tag2 tag3 tag4 tag5).sort) } - end - end - - context 'with a nuspec file with metadata' do - let_it_be(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' } - - before do - allow(service).to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath)) - end - - it 'returns the correct metadata' do - expected_metadata = { - authors: 'Author Test', - description: 'Description Test', - license_url: 'https://opensource.org/licenses/MIT', - project_url: 'https://gitlab.com/gitlab-org/gitlab', - icon_url: 'https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png' - } - - expect(subject.slice(*expected_metadata.keys)).to eq(expected_metadata) - end - end + expect(::Packages::Nuget::ExtractMetadataContentService).to receive_message_chain(:new, :execute) + .with(nuspec_file_content).with(no_args).and_return(double(payload: expected_metadata)) - context 'with invalid package file id' do - let(:package_file) { double('file', id: 555) } - - it_behaves_like 'raises an error', 'invalid package file' - end - - context 'linked to a non nuget package' do - before do - package_file.package.maven! - end - - it_behaves_like 'raises an error', 'invalid package file' - end - - context 'with a 0 byte package file id' do - before do - allow_any_instance_of(Packages::PackageFileUploader).to receive(:size).and_return(0) - end - - it_behaves_like 'raises an error', 'invalid package file' - end - - context 'without the nuspec file' do - before do - allow_any_instance_of(Zip::File).to receive(:glob).and_return([]) - end - - it_behaves_like 'raises an error', 'nuspec file not found' - end - - context 'with a too big nuspec file' do - before do - allow_any_instance_of(Zip::File).to receive(:glob).and_return([double('file', size: 6.megabytes)]) - end - - it_behaves_like 'raises an error', 'nuspec file too big' - end - - context 'with a corrupted nupkg file with a wrong entry size' do - let(:nupkg_fixture_path) { expand_fixture_path('packages/nuget/corrupted_package.nupkg') } - - before do - allow(Zip::File).to receive(:new).and_return(Zip::File.new(nupkg_fixture_path, false, false)) - end + metadata = subject.execute.payload - it_behaves_like 'raises an error', "nuspec file has the wrong entry size: entry 'DummyProject.DummyPackage.nuspec' should be 255B, but is larger when inflated." + expect(metadata).to eq(expected_metadata) end end end diff --git a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb index fa7d994c13c..caa4e42d002 100644 --- a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb +++ b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb @@ -228,7 +228,7 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_ end end - it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError, with_message: 'nuspec file not found' + it_behaves_like 'raising an', ::Packages::Nuget::ExtractMetadataFileService::ExtractionError, with_message: 'nuspec file not found' end context 'with a symbol package' do diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index ffe1034bcad..2298f2e086f 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -122,11 +122,9 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'AdminEmailsWorker' => 3, 'Analytics::CodeReviewMetricsWorker' => 3, 'Analytics::DevopsAdoption::CreateSnapshotWorker' => 3, - 'Analytics::InstanceStatistics::CounterJobWorker' => 3, 'Analytics::UsageTrends::CounterJobWorker' => 3, 'ApprovalRules::ExternalApprovalRulePayloadWorker' => 3, 'ApproveBlockedPendingApprovalUsersWorker' => 3, - 'ArchiveTraceWorker' => 3, 'AuthorizedKeysWorker' => 3, 'AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker' => 3, 'AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker' => 3, @@ -136,7 +134,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'AutoMergeProcessWorker' => 3, 'BackgroundMigrationWorker' => 3, 'BackgroundMigration::CiDatabaseWorker' => 3, - 'BuildFinishedWorker' => 3, 'BuildHooksWorker' => 3, 'BuildQueueWorker' => 3, 'BuildSuccessWorker' => 3, @@ -187,7 +184,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'Clusters::Applications::DeactivateIntegrationWorker' => 3, 'Clusters::Applications::UninstallWorker' => 3, 'Clusters::Applications::WaitForUninstallAppWorker' => 3, - 'Clusters::Cleanup::AppWorker' => 3, 'Clusters::Cleanup::ProjectNamespaceWorker' => 3, 'Clusters::Cleanup::ServiceAccountWorker' => 3, 'ContainerExpirationPolicies::CleanupContainerRepositoryWorker' => 0, @@ -207,10 +203,7 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'DependencyProxy::CleanupBlobWorker' => 3, 'DependencyProxy::CleanupManifestWorker' => 3, 'Deployments::AutoRollbackWorker' => 3, - 'Deployments::FinishedWorker' => 3, - 'Deployments::ForwardDeploymentWorker' => 3, 'Deployments::LinkMergeRequestWorker' => 3, - 'Deployments::SuccessWorker' => 3, 'Deployments::UpdateEnvironmentWorker' => 3, 'Deployments::ApprovalWorker' => 3, 'DesignManagement::CopyDesignCollectionWorker' => 3, @@ -260,7 +253,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'Geo::VerificationTimeoutWorker' => false, 'Geo::VerificationWorker' => 3, 'GeoRepositoryDestroyWorker' => 3, - 'GitGarbageCollectWorker' => false, 'Gitlab::BitbucketServerImport::AdvanceStageWorker' => 3, 'Gitlab::BitbucketServerImport::Stage::FinishImportWorker' => 3, 'Gitlab::BitbucketServerImport::Stage::ImportLfsObjectsWorker' => 3, @@ -356,7 +348,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'MergeRequestCleanupRefsWorker' => 3, 'MergeRequestMergeabilityCheckWorker' => 3, 'MergeRequestResetApprovalsWorker' => 3, - 'MergeRequests::AssigneesChangeWorker' => 3, 'MergeRequests::CaptureSuggestedReviewersAcceptedWorker' => 3, 'MergeRequests::CreatePipelineWorker' => 3, 'MergeRequests::DeleteSourceBranchWorker' => 3, @@ -373,7 +364,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'Onboarding::ProgressWorker' => 3, 'Onboarding::UserAddedWorker' => 3, 'Namespaces::FreeUserCap::OverLimitNotificationWorker' => false, - 'Namespaces::RefreshRootStatisticsWorker' => 3, 'Namespaces::RootStatisticsWorker' => 3, 'Namespaces::ScheduleAggregationWorker' => 3, 'Namespaces::FreeUserCap::NotificationClearingWorker' => false, @@ -411,9 +401,7 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'ProjectDestroyWorker' => 3, 'ProjectExportWorker' => false, 'ProjectImportScheduleWorker' => 1, - 'ProjectScheduleBulkRepositoryShardMovesWorker' => 3, 'ProjectTemplateExportWorker' => false, - 'ProjectUpdateRepositoryStorageWorker' => 3, 'Projects::DeregisterSuggestedReviewersProjectWorker' => 3, 'Projects::DisableLegacyOpenSourceLicenseForInactiveProjectsWorker' => 3, 'Projects::GitGarbageCollectWorker' => false, @@ -451,8 +439,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'Security::TrackSecureScansWorker' => 1, 'ServiceDeskEmailReceiverWorker' => 3, 'SetUserStatusBasedOnUserCapSettingWorker' => 3, - 'SnippetScheduleBulkRepositoryShardMovesWorker' => 3, - 'SnippetUpdateRepositoryStorageWorker' => 3, 'Snippets::ScheduleBulkRepositoryShardMovesWorker' => 3, 'Snippets::UpdateRepositoryStorageWorker' => 3, 'StageUpdateWorker' => 3, @@ -480,7 +466,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'VulnerabilityExports::ExportWorker' => 3, 'WaitForClusterCreationWorker' => 3, 'WebHookWorker' => 4, - 'WebHooks::DestroyWorker' => 3, 'WebHooks::LogExecutionWorker' => 3, 'Wikis::GitGarbageCollectWorker' => false, 'WorkItems::ImportWorkItemsCsvWorker' => 3, @@ -490,6 +475,23 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do }.merge(extra_retry_exceptions) end + it 'defines `retry_exceptions` only for existing workers', if: Gitlab.ee? do + removed_workers = retry_exceptions.keys - retry_exception_workers.map { |worker| worker.klass.to_s } + message = -> do + list = removed_workers.map { |name| "- #{name}" } + + <<~MESSAGE + The following workers no longer exist but are defined in `retry_exceptions`: + + #{list.join("\n")} + + Make sure to remove them from `retry_exceptions` because their definition is unnecessary. + MESSAGE + end + + expect(removed_workers).to be_empty, message + end + it 'uses the default number of retries for new jobs' do expect(workers_without_defaults - cronjobs - retry_exception_workers).to all(have_attributes(retries: true)) end diff --git a/spec/workers/packages/nuget/extraction_worker_spec.rb b/spec/workers/packages/nuget/extraction_worker_spec.rb index c1d42d556c2..11eaa1b5dde 100644 --- a/spec/workers/packages/nuget/extraction_worker_spec.rb +++ b/spec/workers/packages/nuget/extraction_worker_spec.rb @@ -61,7 +61,7 @@ RSpec.describe Packages::Nuget::ExtractionWorker, type: :worker, feature_categor allow_any_instance_of(Zip::File).to receive(:glob).and_return([]) end - it_behaves_like 'handling the metadata error', exception_class: ::Packages::Nuget::MetadataExtractionService::ExtractionError + it_behaves_like 'handling the metadata error', exception_class: ::Packages::Nuget::ExtractMetadataFileService::ExtractionError end context 'with package with an invalid package name' do |