diff --git a/app/assets/stylesheets/page_bundles/jira_connect.scss b/app/assets/stylesheets/page_bundles/jira_connect.scss index 6972e98b0bf..371cc5ee69a 100644 --- a/app/assets/stylesheets/page_bundles/jira_connect.scss +++ b/app/assets/stylesheets/page_bundles/jira_connect.scss @@ -1,9 +1,7 @@ @import 'mixins_and_variables_and_functions'; @import '@gitlab/ui/src/scss/bootstrap'; -@import 'bootstrap-vue/src/index'; - -@import '@gitlab/ui/src/scss/utilities'; +@import '@gitlab/ui/src/scss/bootstrap_vue'; // We should only import styles that we actually use. @import '@gitlab/ui/src/components/base/alert/alert'; diff --git a/app/services/issuable/destroy_service.rb b/app/services/issuable/destroy_service.rb index eec24bf9d58..38515149d07 100644 --- a/app/services/issuable/destroy_service.rb +++ b/app/services/issuable/destroy_service.rb @@ -8,6 +8,10 @@ module Issuable end def execute(issuable) + # load sync object before destroy otherwise we cannot access it for + # deletion of label links in delete_label_links + @synced_object_to_delete = issuable.try(:sync_object) + before_destroy(issuable) after_destroy(issuable) if issuable.destroy end @@ -29,14 +33,32 @@ module Issuable end def delete_todos(issuable) + synced_object_to_delete = @synced_object_to_delete + issuable.run_after_commit_or_now do - TodosDestroyer::DestroyedIssuableWorker.perform_async(issuable.id, issuable.class.name) + TodosDestroyer::DestroyedIssuableWorker.perform_async(issuable.id, issuable.class.base_class.name) + + # if there is a sync object, we need to cleanup its todos as well + next unless synced_object_to_delete + + TodosDestroyer::DestroyedIssuableWorker.perform_async( + synced_object_to_delete.id, synced_object_to_delete.class.base_class.name + ) end end def delete_label_links(issuable) + synced_object_to_delete = @synced_object_to_delete + issuable.run_after_commit_or_now do - Issuable::LabelLinksDestroyWorker.perform_async(issuable.id, issuable.class.name) + Issuable::LabelLinksDestroyWorker.perform_async(issuable.id, issuable.class.base_class.name) + + # if there is a sync object, we need to cleanup its label links as well + next unless synced_object_to_delete + + Issuable::LabelLinksDestroyWorker.perform_async( + synced_object_to_delete.id, synced_object_to_delete.class.base_class.name + ) end end end diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml index 13b9ea9742f..8ab22072add 100644 --- a/app/views/jira_connect/subscriptions/index.html.haml +++ b/app/views/jira_connect/subscriptions/index.html.haml @@ -3,3 +3,4 @@ = webpack_bundle_tag 'jira_connect_app' - add_page_specific_style 'page_bundles/jira_connect', defer: false +- add_page_specific_style 'tailwind' diff --git a/db/post_migrate/20240611114131_remove_project_statistics_repository_size_and_project_id_index.rb b/db/post_migrate/20240611114131_remove_project_statistics_repository_size_and_project_id_index.rb new file mode 100644 index 00000000000..4825be661ac --- /dev/null +++ b/db/post_migrate/20240611114131_remove_project_statistics_repository_size_and_project_id_index.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class RemoveProjectStatisticsRepositorySizeAndProjectIdIndex < Gitlab::Database::Migration[2.2] + milestone '17.1' + + INDEX_NAME = 'index_project_statistics_on_repository_size_and_project_id' + COLUMNS = %i[repository_size project_id] + + # TODO: Index to be destroyed synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/466691 + def up + return unless should_run? + + prepare_async_index_removal :project_statistics, COLUMNS, name: INDEX_NAME + end + + def down + return unless should_run? + + unprepare_async_index :project_statistics, COLUMNS, name: INDEX_NAME + end + + def should_run? + Gitlab.com_except_jh? + end +end diff --git a/db/schema_migrations/20240611114131 b/db/schema_migrations/20240611114131 new file mode 100644 index 00000000000..55377895096 --- /dev/null +++ b/db/schema_migrations/20240611114131 @@ -0,0 +1 @@ +572b8d57ea60ff7cf45598081d29d82a7134221d7e27a1be1c562fc7f081536e \ No newline at end of file diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index 77d6811a55b..9e3286e194d 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -114,6 +114,47 @@ curl --request POST \ }' ``` +### Update a package protection rule + +Update a package protection rule for a project. + +```plaintext +PATCH /api/v4/projects/:id/packages/protection/rules/:package_protection_rule_id +``` + +Supported attributes: + +| Attribute | Type | Required | Description | +|---------------------------------------|-----------------|----------|--------------------------------| +| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. | +| `package_protection_rule_id` | integer | Yes | ID of the package protection rule to be updated. | +| `package_name_pattern` | string | No | Package name protected by the protection rule. For example `@my-scope/my-package-*`. Wildcard character `*` allowed. | +| `package_type` | string | No | Package type protected by the protection rule. For example `npm`. | +| `minimum_access_level_for_push` | string | No | Minimum GitLab access level able to push a package. For example `developer`, `maintainer`, `owner`. | + +If successful, returns [`200`](rest/index.md#status-codes) and the updated package protection rule. + +Can return the following status codes: + +- `200 OK`: The package protection rule was patched successfully. +- `400 Bad Request`: The patch is invalid. +- `401 Unauthorized`: The access token is invalid. +- `403 Forbidden`: The user does not have permission to patch a package protection rule. +- `404 Not Found`: The project was not found. +- `422 Unprocessable Entity`: The package protection rule could not be patched, for example, because the `package_name_pattern` is already taken. + +Example request: + +```shell +curl --request PATCH \ + --header "PRIVATE-TOKEN: " \ + --header "Content-Type: application/json" \ + --url "https://gitlab.example.com/api/v4/projects/7/packages/protection/rules/32" \ + --data '{ + "package_name_pattern": "new-package-name-pattern-*" + }' +``` + ## Delete a package protection rule Deletes a package protection rule from a project. diff --git a/lib/api/project_packages_protection_rules.rb b/lib/api/project_packages_protection_rules.rb index a86ac8fb076..1dd0dbaa641 100644 --- a/lib/api/project_packages_protection_rules.rb +++ b/lib/api/project_packages_protection_rules.rb @@ -18,70 +18,111 @@ module API requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - desc 'Get list of package protection rules for a project' do - success Entities::Projects::Packages::Protection::Rule - failure [ - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not Found' } - ] - tags %w[projects] - is_array true - end - get ':id/packages/protection/rules' do - present user_project.package_protection_rules, with: Entities::Projects::Packages::Protection::Rule - end + resource ':id/packages/protection/rules' do + desc 'Get list of package protection rules for a project' do + success Entities::Projects::Packages::Protection::Rule + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' } + ] + tags %w[projects] + is_array true + hidden true + end + get do + present user_project.package_protection_rules, with: Entities::Projects::Packages::Protection::Rule + end - desc 'Create a package protection rule for a project' do - success Entities::Projects::Packages::Protection::Rule - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not Found' }, - { code: 422, message: 'Unprocessable Entity' } - ] - tags %w[projects] - end - params do - requires :package_name_pattern, type: String, - desc: 'Package name protected by the rule. For example @my-scope/my-package-*. Wildcard character * allowed.' - requires :package_type, type: String, values: Packages::Protection::Rule.package_types.keys, - desc: 'Package type protected by the rule. For example npm.' - requires :minimum_access_level_for_push, type: String, - values: Packages::Protection::Rule.minimum_access_level_for_pushes.keys, - desc: 'Minimum GitLab access level able to push a package. For example developer, maintainer, owner.' - end - post ':id/packages/protection/rules' do - response = ::Packages::Protection::CreateRuleService.new(project: user_project, current_user: current_user, - params: declared_params(params)).execute + desc 'Create a package protection rule for a project' do + success Entities::Projects::Packages::Protection::Rule + failure [ + { code: 400, message: 'Bad Request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' }, + { code: 422, message: 'Unprocessable Entity' } + ] + tags %w[projects] + hidden true + end + params do + requires :package_name_pattern, type: String, + desc: 'Package name protected by the rule. For example @my-scope/my-package-*. + Wildcard character * allowed.' + requires :package_type, type: String, values: Packages::Protection::Rule.package_types.keys, + desc: 'Package type protected by the rule. For example npm.' + requires :minimum_access_level_for_push, type: String, + values: Packages::Protection::Rule.minimum_access_level_for_pushes.keys, + desc: 'Minimum GitLab access level able to push a package. For example developer, maintainer, owner.' + end + post do + response = ::Packages::Protection::CreateRuleService.new(project: user_project, current_user: current_user, + params: declared_params).execute - render_api_error!({ error: response.message }, :unprocessable_entity) if response.error? + render_api_error!({ error: response.message }, :unprocessable_entity) if response.error? - present response[:package_protection_rule], with: Entities::Projects::Packages::Protection::Rule - end + present response[:package_protection_rule], with: Entities::Projects::Packages::Protection::Rule + end - desc 'Delete package protection rule' do - success code: 204, message: '204 No Content' - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not Found' } - ] - tags %w[projects] - end - params do - requires :package_protection_rule_id, type: Integer, desc: 'The ID of the package protection rule' - end - delete ':id/packages/protection/rules/:package_protection_rule_id' do - package_protection_rule = user_project.package_protection_rules.find(params[:package_protection_rule_id]) + params do + requires :package_protection_rule_id, type: Integer, desc: 'The ID of the package protection rule' + end + resource ':package_protection_rule_id' do + desc 'Update a package protection rule for a project' do + success Entities::Projects::Packages::Protection::Rule + failure [ + { code: 400, message: 'Bad Request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' }, + { code: 422, message: 'Unprocessable Entity' } + ] + tags %w[projects] + hidden true + end + params do + optional :package_name_pattern, type: String, + desc: 'Package name protected by the rule. For example @my-scope/my-package-*. + Wildcard character * allowed.' + optional :package_type, type: String, values: Packages::Protection::Rule.package_types.keys, + desc: 'Package type protected by the rule. For example npm.' + optional :minimum_access_level_for_push, type: String, + values: Packages::Protection::Rule.minimum_access_level_for_pushes.keys, + desc: 'Minimum GitLab access level able to push a package. For example developer, maintainer, owner.' + end + patch do + package_protection_rule = user_project.package_protection_rules.find(params[:package_protection_rule_id]) - destroy_conditionally!(package_protection_rule) do |package_protection_rule| - response = ::Packages::Protection::DeleteRuleService.new(package_protection_rule, - current_user: current_user).execute + response = ::Packages::Protection::UpdateRuleService.new(package_protection_rule, + current_user: current_user, params: declared_params(include_missing: false)).execute - render_api_error!({ error: response.message }, :bad_request) if response.error? + render_api_error!({ error: response.message }, :unprocessable_entity) if response.error? + + present response[:package_protection_rule], with: Entities::Projects::Packages::Protection::Rule + end + + desc 'Delete package protection rule' do + success code: 204, message: '204 No Content' + failure [ + { code: 400, message: 'Bad Request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' } + ] + tags %w[projects] + hidden true + end + delete do + package_protection_rule = user_project.package_protection_rules.find(params[:package_protection_rule_id]) + + destroy_conditionally!(package_protection_rule) do |package_protection_rule| + response = ::Packages::Protection::DeleteRuleService.new(package_protection_rule, + current_user: current_user).execute + + render_api_error!({ error: response.message }, :bad_request) if response.error? + end + end end end end diff --git a/qa/Gemfile b/qa/Gemfile index 8bc70d275ac..1648078bab2 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -11,7 +11,7 @@ gem 'capybara', '~> 3.40.0' gem 'capybara-screenshot', '~> 1.0.26' gem 'rake', '~> 13', '>= 13.2.1' gem 'rspec', '~> 3.13' -gem 'selenium-webdriver', '= 4.21.1' +gem 'selenium-webdriver', '= 4.22.0' gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default gem 'rest-client', '~> 2.1.0' gem 'rspec_junit_formatter', '~> 0.6.0' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index f15142103ec..936d7f09fe8 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -210,6 +210,7 @@ GEM llhttp-ffi (0.4.0) ffi-compiler (~> 1.0) rake (~> 13.0) + logger (1.6.0) loofah (2.21.3) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -318,8 +319,9 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - selenium-webdriver (4.21.1) + selenium-webdriver (4.22.0) base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -409,11 +411,11 @@ DEPENDENCIES rspec-parameterized (~> 1.0.2) rspec_junit_formatter (~> 0.6.0) ruby-debug-ide (~> 0.7.3) - selenium-webdriver (= 4.21.1) + selenium-webdriver (= 4.22.0) slack-notifier (~> 2.4) terminal-table (~> 3.0.2) warning (~> 1.4) zeitwerk (~> 2.6, >= 2.6.15) BUNDLED WITH - 2.5.11 + 2.5.13 diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb index e7ba8f34f05..c9af36e04b8 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb @@ -11,7 +11,7 @@ module QA end it( - 'user registers a new group runner', + 'user registers a new group runner', :blocking, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/388740' ) do Flow::Login.sign_in diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb index 35d4ddac118..43bcc6e053e 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb @@ -10,7 +10,7 @@ module QA runner.remove_via_api! end - it 'user registers a new project runner', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348025' do + it 'user registers a new project runner', :blocking, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348025' do Flow::Login.sign_in runner.project.visit! diff --git a/spec/migrations/20240611114131_remove_project_statistics_repository_size_and_project_id_index_spec.rb b/spec/migrations/20240611114131_remove_project_statistics_repository_size_and_project_id_index_spec.rb new file mode 100644 index 00000000000..6502074aa1d --- /dev/null +++ b/spec/migrations/20240611114131_remove_project_statistics_repository_size_and_project_id_index_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe RemoveProjectStatisticsRepositorySizeAndProjectIdIndex, feature_category: :consumables_cost_management do + let(:migration) { described_class.new } + let(:postgres_async_indexes) { table(:postgres_async_indexes) } + + describe '#up' do + subject(:up) { migration.up } + + it 'does nothing when not on gitlab.com' do + expect { up }.not_to change { postgres_async_indexes.count } + end + + it 'prepares async index removal when on gitlab.com', :saas do + expect { up }.to change { postgres_async_indexes.count }.from(0).to(1) + end + end + + describe '#down' do + subject(:down) { migration.down } + + before do + postgres_async_indexes.create!( + name: 'index_project_statistics_on_repository_size_and_project_id', + table_name: 'project_statistics', + definition: 'test index' + ) + end + + it 'does nothing when not on gitlab.com' do + expect { down }.not_to change { postgres_async_indexes.count } + end + + it 'unprepares async index removal when on gitlab.com', :saas do + expect { down }.to change { postgres_async_indexes.count }.from(1).to(0) + end + end +end diff --git a/spec/requests/api/project_packages_protection_rules_spec.rb b/spec/requests/api/project_packages_protection_rules_spec.rb index 95e4216f86d..6cdd71bb682 100644 --- a/spec/requests/api/project_packages_protection_rules_spec.rb +++ b/spec/requests/api/project_packages_protection_rules_spec.rb @@ -15,6 +15,12 @@ RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature let_it_be(:invalid_token) { 'invalid-token123' } let_it_be(:headers_with_invalid_token) { { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => invalid_token } } + let(:params) do + { package_name_pattern: '@my-new-scope/my-package-*', + package_type: package_protection_rule.package_type, + minimum_access_level_for_push: package_protection_rule.minimum_access_level_for_push } + end + shared_examples 'rejecting project packages protection rules request when not enough permissions' do using RSpec::Parameterized::TableSyntax @@ -34,6 +40,48 @@ RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature end end + shared_examples 'rejecting project packages protection rules request when enough permissions' do + context 'when feature flag is disabled' do + before do + stub_feature_flags(packages_protected_packages: false) + end + + it_behaves_like 'returning response status', :not_found + end + + context 'when the project id is invalid' do + let(:url) { "/projects/invalid/packages/protection/rules" } + + it_behaves_like 'returning response status', :not_found + end + + context 'when the project id does not exist' do + let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules" } + + it_behaves_like 'returning response status', :not_found + end + end + + shared_examples 'rejecting project packages protection rules request when handling rule ids' do + context 'when the rule id is invalid' do + let(:url) { "/projects/#{project.id}/packages/protection/rules/invalid" } + + it_behaves_like 'returning response status', :bad_request + end + + context 'when the rule id does not exist' do + let(:url) { "/projects/#{project.id}/packages/protection/rules/#{non_existing_record_id}" } + + it_behaves_like 'returning response status', :not_found + end + + context 'when the package protection rule does belong to another project' do + let(:url) { "/projects/#{other_project.id}/packages/protection/rules/#{package_protection_rule.id}" } + + it_behaves_like 'returning response status', :not_found + end + end + describe 'GET /projects/:id/packages/protection/rules' do let(:url) { "/projects/#{project.id}/packages/protection/rules" } @@ -55,25 +103,7 @@ RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature expect(json_response.count).to eq(2) end - context 'when the project id is invalid' do - let(:url) { "/projects/invalid/packages/protection/rules" } - - it_behaves_like 'returning response status', :not_found - end - - context 'when the project id does not exist' do - let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules" } - - it_behaves_like 'returning response status', :not_found - end - - context 'when packages_protected_packages is disabled' do - before do - stub_feature_flags(packages_protected_packages: false) - end - - it_behaves_like 'returning response status', :not_found - end + it_behaves_like 'rejecting project packages protection rules request when enough permissions' end context 'with invalid token' do @@ -85,11 +115,6 @@ RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature describe 'POST /projects/:id/packages/protection/rules' do let(:url) { "/projects/#{project.id}/packages/protection/rules" } - let(:params) do - { package_name_pattern: '@my-new-scope/my-package-*', - package_type: package_protection_rule.package_type, - minimum_access_level_for_push: package_protection_rule.minimum_access_level_for_push } - end subject(:post_package_rule) { post(api(url, api_user), params: params) } @@ -136,25 +161,86 @@ RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature end end - context 'when the project id is invalid' do - let(:url) { "/projects/invalid/packages/protection/rules" } + it_behaves_like 'rejecting project packages protection rules request when enough permissions' + end - it_behaves_like 'returning response status', :not_found - end + context 'with invalid token' do + subject(:post_package_rules) { post(api(url), headers: headers_with_invalid_token, params: params) } - context 'when the project id does not exist' do - let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules" } + it_behaves_like 'returning response status', :unauthorized + end + end - it_behaves_like 'returning response status', :not_found - end + describe 'PATCH /projects/:id/packages/protection/rules/:package_protection_rule_id' do + let(:url) { "/projects/#{project.id}/packages/protection/rules/#{package_protection_rule.id}" } - context 'when packages_protected_packages is disabled' do + subject(:patch_package_rule) { patch(api(url, api_user), params: params) } + + it_behaves_like 'rejecting project packages protection rules request when not enough permissions' + + context 'for maintainer' do + let(:api_user) { maintainer } + let_it_be(:changed_scope) { '@my-changed-scope/my-package-*' } + + context 'with full changeset' do before do - stub_feature_flags(packages_protected_packages: false) + params[:package_name_pattern] = changed_scope end - it_behaves_like 'returning response status', :not_found + it 'updates a package protection rule' do + patch_package_rule + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response["package_name_pattern"]).to eq(changed_scope) + expect(json_response["package_type"]).to eq(package_protection_rule.package_type) + end end + + context 'with a single change' do + let(:params) { { package_name_pattern: changed_scope } } + + it 'updates a package protection rule' do + patch_package_rule + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response["package_name_pattern"]).to eq(changed_scope) + end + end + + context 'with invalid package_type' do + before do + params[:package_type] = "not in enum" + end + + it_behaves_like 'returning response status', :bad_request + end + + context 'with invalid minimum_access_level_for_push' do + before do + params[:minimum_access_level_for_push] = "not in enum" + end + + it_behaves_like 'returning response status', :bad_request + end + + context 'with already existing package_name_pattern' do + before do + other_package_protection_rule = create(:package_protection_rule, project: project, + package_name_pattern: "@my-scope/my-package-*") + params[:package_name_pattern] = other_package_protection_rule.package_name_pattern + end + + it_behaves_like 'returning response status', :unprocessable_entity + end + + it_behaves_like 'rejecting project packages protection rules request when handling rule ids' + it_behaves_like 'rejecting project packages protection rules request when enough permissions' + end + + context 'with invalid token' do + subject(:patch_package_rules) { patch(api(url), headers: headers_with_invalid_token, params: params) } + + it_behaves_like 'returning response status', :unauthorized end end @@ -175,44 +261,9 @@ RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature end.to raise_error(ActiveRecord::RecordNotFound) expect(response).to have_gitlab_http_status(:no_content) end - end - context 'when the package protection rule does belong to another project' do - let(:url) { "/projects/#{other_project.id}/packages/protection/rules/#{package_protection_rule.id}" } - - it_behaves_like 'returning response status', :not_found - end - - context 'when the project id is invalid' do - let(:url) { "/projects/invalid/packages/protection/rules/#{package_protection_rule.id}" } - - it_behaves_like 'returning response status', :not_found - end - - context 'when the project id does not exist' do - let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules/#{package_protection_rule.id}" } - - it_behaves_like 'returning response status', :not_found - end - - context 'when the rule id is invalid' do - let(:url) { "/projects/#{project.id}/packages/protection/rules/invalid" } - - it_behaves_like 'returning response status', :bad_request - end - - context 'when the rule id does not exist' do - let(:url) { "/projects/#{project.id}/packages/protection/rules/#{non_existing_record_id}" } - - it_behaves_like 'returning response status', :not_found - end - - context 'when packages_protected_packages is disabled' do - before do - stub_feature_flags(packages_protected_packages: false) - end - - it_behaves_like 'returning response status', :not_found + it_behaves_like 'rejecting project packages protection rules request when handling rule ids' + it_behaves_like 'rejecting project packages protection rules request when enough permissions' end context 'with invalid token' do diff --git a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb index 92681b8ba79..30c7471f979 100644 --- a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb +++ b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb @@ -1,18 +1,22 @@ # frozen_string_literal: true RSpec.shared_examples_for 'service scheduling async deletes' do - it 'destroys associated todos asynchronously' do - expect(worker_class) - .to receive(:perform_async) - .with(issuable.id, issuable.class.name) + it 'destroys associated todos asynchronously', :sidekiq_inline do + expect(worker_class).to receive(:perform_async).with(issuable.id, issuable.class.base_class.name) + + if try(:sync_object).present? + expect(worker_class).to receive(:perform_async).with(sync_object.id, sync_object.class.base_class.name) + end subject.execute(issuable) end - it 'works inside a transaction' do - expect(worker_class) - .to receive(:perform_async) - .with(issuable.id, issuable.class.name) + it 'works inside a transaction', :sidekiq_inline do + expect(worker_class).to receive(:perform_async).with(issuable.id, issuable.class.base_class.name) + + if try(:sync_object).present? + expect(worker_class).to receive(:perform_async).with(sync_object.id, sync_object.class.base_class.name) + end ApplicationRecord.transaction do subject.execute(issuable)