summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-25 00:08:34 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-25 00:08:34 +0000
commit1e6730a4e32f6cbf4b84aa9fc13204778783f33c (patch)
tree6dd6c9ed98ec836432cf431397a4ef45dd78deb8 /spec
parent95a48f11db963bc55ab918e3eb24f8576dca4a81 (diff)
downloadgitlab-ce-1e6730a4e32f6cbf4b84aa9fc13204778783f33c.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/batch_comments/components/draft_note_spec.js12
-rw-r--r--spec/frontend/frequent_items/utils_spec.js47
-rw-r--r--spec/frontend/issuable_create/components/issuable_form_spec.js3
-rw-r--r--spec/frontend/monitoring/components/charts/time_series_spec.js4
-rw-r--r--spec/frontend/monitoring/components/dashboard_actions_menu_spec.js7
-rw-r--r--spec/frontend/nav/components/responsive_app_spec.js2
-rw-r--r--spec/frontend/nav/components/top_nav_container_view_spec.js5
-rw-r--r--spec/frontend/notes/components/discussion_notes_spec.js14
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_form_spec.js2
-rw-r--r--spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap6
-rw-r--r--spec/frontend/terraform/components/terraform_list_spec.js3
-rw-r--r--spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js2
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js3
-rw-r--r--spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/paginated_list_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap30
-rw-r--r--spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js10
-rw-r--r--spec/models/service_desk_setting_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/packages/destroy_package_spec.rb93
-rw-r--r--spec/services/packages/destroy_package_service_spec.rb61
20 files changed, 256 insertions, 62 deletions
diff --git a/spec/frontend/batch_comments/components/draft_note_spec.js b/spec/frontend/batch_comments/components/draft_note_spec.js
index c2d488a465e..5d22823e974 100644
--- a/spec/frontend/batch_comments/components/draft_note_spec.js
+++ b/spec/frontend/batch_comments/components/draft_note_spec.js
@@ -1,5 +1,6 @@
import { getByRole } from '@testing-library/dom';
import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import DraftNote from '~/batch_comments/components/draft_note.vue';
import { createStore } from '~/batch_comments/stores';
import NoteableNote from '~/notes/components/noteable_note.vue';
@@ -8,6 +9,14 @@ import { createDraft } from '../mock_data';
const localVue = createLocalVue();
+const NoteableNoteStub = stubComponent(NoteableNote, {
+ template: `
+ <div>
+ <slot name="note-header-info">Test</slot>
+ </div>
+ `,
+});
+
describe('Batch comments draft note component', () => {
let store;
let wrapper;
@@ -26,6 +35,9 @@ describe('Batch comments draft note component', () => {
store,
propsData,
localVue,
+ stubs: {
+ NoteableNote: NoteableNoteStub,
+ },
});
jest.spyOn(wrapper.vm.$store, 'dispatch').mockImplementation();
diff --git a/spec/frontend/frequent_items/utils_spec.js b/spec/frontend/frequent_items/utils_spec.js
index a7ab18b0d10..8c3841558f4 100644
--- a/spec/frontend/frequent_items/utils_spec.js
+++ b/spec/frontend/frequent_items/utils_spec.js
@@ -66,35 +66,36 @@ describe('Frequent Items utils spec', () => {
});
describe('updateExistingFrequentItem', () => {
- let mockedProject;
-
- beforeEach(() => {
- mockedProject = {
- ...mockProject,
- frequency: 1,
- lastAccessedOn: 1497979281815,
- };
+ const LAST_ACCESSED = 1497979281815;
+ const WITHIN_AN_HOUR = LAST_ACCESSED + HOUR_IN_MS;
+ const OVER_AN_HOUR = WITHIN_AN_HOUR + 1;
+ const EXISTING_ITEM = Object.freeze({
+ ...mockProject,
+ frequency: 1,
+ lastAccessedOn: 1497979281815,
});
- it('updates item if accessed over an hour ago', () => {
- const newTimestamp = Date.now() + HOUR_IN_MS + 1;
+ it.each`
+ desc | existingProps | newProps | expected
+ ${'updates item if accessed over an hour ago'} | ${{}} | ${{ lastAccessedOn: OVER_AN_HOUR }} | ${{ lastAccessedOn: Date.now(), frequency: 2 }}
+ ${'does not update is accessed with an hour'} | ${{}} | ${{ lastAccessedOn: WITHIN_AN_HOUR }} | ${{ lastAccessedOn: EXISTING_ITEM.lastAccessedOn, frequency: 1 }}
+ ${'updates if lastAccessedOn not found'} | ${{ lastAccessedOn: undefined }} | ${{ lastAccessedOn: WITHIN_AN_HOUR }} | ${{ lastAccessedOn: Date.now(), frequency: 2 }}
+ `('$desc', ({ existingProps, newProps, expected }) => {
const newItem = {
- ...mockedProject,
- lastAccessedOn: newTimestamp,
+ ...EXISTING_ITEM,
+ ...newProps,
};
- const result = updateExistingFrequentItem(mockedProject, newItem);
-
- expect(result.frequency).toBe(mockedProject.frequency + 1);
- });
-
- it('does not update item if accessed within the hour', () => {
- const newItem = {
- ...mockedProject,
- lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS,
+ const existingItem = {
+ ...EXISTING_ITEM,
+ ...existingProps,
};
- const result = updateExistingFrequentItem(mockedProject, newItem);
- expect(result.frequency).toBe(mockedProject.frequency);
+ const result = updateExistingFrequentItem(existingItem, newItem);
+
+ expect(result).toEqual({
+ ...newItem,
+ ...expected,
+ });
});
});
diff --git a/spec/frontend/issuable_create/components/issuable_form_spec.js b/spec/frontend/issuable_create/components/issuable_form_spec.js
index a074fddf091..30b116bc35c 100644
--- a/spec/frontend/issuable_create/components/issuable_form_spec.js
+++ b/spec/frontend/issuable_create/components/issuable_form_spec.js
@@ -23,6 +23,9 @@ const createComponent = ({
<button class="js-issuable-save">Submit issuable</button>
`,
},
+ stubs: {
+ MarkdownField,
+ },
});
};
diff --git a/spec/frontend/monitoring/components/charts/time_series_spec.js b/spec/frontend/monitoring/components/charts/time_series_spec.js
index 754ddd96c9b..ea6e4f4a5ed 100644
--- a/spec/frontend/monitoring/components/charts/time_series_spec.js
+++ b/spec/frontend/monitoring/components/charts/time_series_spec.js
@@ -51,6 +51,8 @@ describe('Time series component', () => {
},
stubs: {
GlPopover: true,
+ GlLineChart,
+ GlAreaChart,
},
attachTo: document.body,
});
@@ -202,7 +204,7 @@ describe('Time series component', () => {
describe('when series is of line type', () => {
beforeEach(() => {
- createWrapper();
+ createWrapper({}, mount);
wrapper.vm.formatTooltipText(mockLineSeriesData());
return wrapper.vm.$nextTick();
});
diff --git a/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js b/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
index 6e98ca28071..dbb9fd5f603 100644
--- a/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdownItem } from '@gitlab/ui';
+import { GlDropdownItem, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import { redirectTo } from '~/lib/utils/url_utility';
@@ -43,6 +43,9 @@ describe('Actions menu', () => {
wrapper = shallowMount(ActionsMenu, {
propsData: { ...dashboardActionsMenuProps, ...props },
store,
+ stubs: {
+ GlModal,
+ },
...options,
});
};
@@ -82,7 +85,7 @@ describe('Actions menu', () => {
it('modal for custom metrics form is rendered', () => {
expect(findAddMetricModal().exists()).toBe(true);
- expect(findAddMetricModal().attributes().modalid).toBe('addMetric');
+ expect(findAddMetricModal().props('modalId')).toBe('addMetric');
});
it('add metric modal submit button exists', () => {
diff --git a/spec/frontend/nav/components/responsive_app_spec.js b/spec/frontend/nav/components/responsive_app_spec.js
index 7221ea2c5cd..e1b443745e3 100644
--- a/spec/frontend/nav/components/responsive_app_spec.js
+++ b/spec/frontend/nav/components/responsive_app_spec.js
@@ -111,6 +111,7 @@ describe('~/nav/components/responsive_app.vue', () => {
containerClass: 'gl-px-3',
frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.namespace,
frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.vuexModule,
+ currentItem: {},
linksPrimary: TEST_NAV_DATA.views.projects.linksPrimary,
linksSecondary: TEST_NAV_DATA.views.projects.linksSecondary,
};
@@ -118,6 +119,7 @@ describe('~/nav/components/responsive_app.vue', () => {
containerClass: 'gl-px-3',
frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_GROUPS.namespace,
frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_GROUPS.vuexModule,
+ currentItem: {},
linksPrimary: TEST_NAV_DATA.views.groups.linksPrimary,
linksSecondary: TEST_NAV_DATA.views.groups.linksSecondary,
};
diff --git a/spec/frontend/nav/components/top_nav_container_view_spec.js b/spec/frontend/nav/components/top_nav_container_view_spec.js
index 06d2179b859..0218f09af0a 100644
--- a/spec/frontend/nav/components/top_nav_container_view_spec.js
+++ b/spec/frontend/nav/components/top_nav_container_view_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import { merge } from 'lodash';
import { nextTick } from 'vue';
import FrequentItemsApp from '~/frequent_items/components/app.vue';
import { FREQUENT_ITEMS_PROJECTS } from '~/frequent_items/constants';
@@ -82,7 +83,9 @@ describe('~/nav/components/top_nav_container_view.vue', () => {
it('renders frequent items app', () => {
expect(findFrequentItemsApp()).toEqual({
vuexModule: DEFAULT_PROPS.frequentItemsVuexModule,
- props: expect.objectContaining(TEST_OTHER_PROPS),
+ props: expect.objectContaining(
+ merge({ currentItem: { lastAccessedOn: Date.now() } }, TEST_OTHER_PROPS),
+ ),
attributes: expect.objectContaining(EXTRA_ATTRS),
});
});
diff --git a/spec/frontend/notes/components/discussion_notes_spec.js b/spec/frontend/notes/components/discussion_notes_spec.js
index cd24b9afbdf..59ac75f00e6 100644
--- a/spec/frontend/notes/components/discussion_notes_spec.js
+++ b/spec/frontend/notes/components/discussion_notes_spec.js
@@ -1,5 +1,5 @@
import { getByRole } from '@testing-library/dom';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, mount } from '@vue/test-utils';
import '~/behaviors/markdown/render_gfm';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
import NoteableNote from '~/notes/components/noteable_note.vue';
@@ -23,8 +23,8 @@ describe('DiscussionNotes', () => {
let wrapper;
const getList = () => getByRole(wrapper.element, 'list');
- const createComponent = (props) => {
- wrapper = shallowMount(DiscussionNotes, {
+ const createComponent = (props, mountingMethod = shallowMount) => {
+ wrapper = mountingMethod(DiscussionNotes, {
store,
propsData: {
discussion: discussionMock,
@@ -33,7 +33,11 @@ describe('DiscussionNotes', () => {
...props,
},
scopedSlots: {
- footer: '<p slot-scope="{ showReplies }">showReplies:{{showReplies}}</p>',
+ footer: `
+ <template #default="{ showReplies }">
+ <p>showReplies:{{ showReplies }}</p>,
+ </template>
+ `,
},
slots: {
'avatar-badge': '<span class="avatar-badge-slot-content" />',
@@ -112,7 +116,7 @@ describe('DiscussionNotes', () => {
});
it('passes down avatar-badge slot content', () => {
- createComponent();
+ createComponent({}, mount);
expect(wrapper.find('.avatar-badge-slot-content').exists()).toBe(true);
});
});
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
index c80ccfa8256..f9be0796546 100644
--- a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
+++ b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
@@ -155,7 +155,7 @@ describe('ForkForm component', () => {
describe('forks namespaces', () => {
beforeEach(() => {
mockGetRequest({ namespaces: MOCK_NAMESPACES_RESPONSE });
- createComponent();
+ createFullComponent();
});
it('make GET request from endpoint', async () => {
diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap
index c37f6415898..fc51825f15b 100644
--- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap
+++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/ci_cd_analytics_area_chart_spec.js.snap
@@ -21,11 +21,7 @@ exports[`CiCdAnalyticsAreaChart matches the snapshot 1`] = `
option="[object Object]"
thresholds=""
width="0"
- >
- <template />
-
- <template />
- </glareachart-stub>
+ />
</div>
</div>
`;
diff --git a/spec/frontend/terraform/components/terraform_list_spec.js b/spec/frontend/terraform/components/terraform_list_spec.js
index 882b7b55b3e..c622f86072d 100644
--- a/spec/frontend/terraform/components/terraform_list_spec.js
+++ b/spec/frontend/terraform/components/terraform_list_spec.js
@@ -47,6 +47,9 @@ describe('TerraformList', () => {
localVue,
apolloProvider,
propsData,
+ stubs: {
+ GlTab,
+ },
});
};
diff --git a/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
index 5d09af50420..8214cedc4a1 100644
--- a/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
@@ -63,7 +63,7 @@ describe('Commits edit component', () => {
beforeEach(() => {
createComponent({
header: `<div class="test-header">${testCommitMessage}</div>`,
- checkbox: `<label slot="checkbox" class="test-checkbox">${testLabel}</label >`,
+ checkbox: `<label class="test-checkbox">${testLabel}</label >`,
});
});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 2d00cd8e8d4..cd77d442cbf 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -70,6 +70,9 @@ const createComponent = (customConfig = {}, mergeRequestWidgetGraphql = false) =
mergeRequestWidgetGraphql,
},
},
+ stubs: {
+ CommitEdit,
+ },
});
};
diff --git a/spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
index eacc41ccdad..01fc6210d3f 100644
--- a/spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
@@ -121,7 +121,9 @@ describe('ImageDiffViewer', () => {
:new-size="newSize"
:old-size="oldSize"
>
- <span slot="image-overlay" class="overlay">test</span>
+ <template #image-overlay>
+ <span class="overlay">test</span>
+ </template>
</image-diff-viewer>
`),
}).$mount();
diff --git a/spec/frontend/vue_shared/components/paginated_list_spec.js b/spec/frontend/vue_shared/components/paginated_list_spec.js
index c0ee49f194f..9f819cc4e94 100644
--- a/spec/frontend/vue_shared/components/paginated_list_spec.js
+++ b/spec/frontend/vue_shared/components/paginated_list_spec.js
@@ -7,9 +7,11 @@ describe('Pagination links component', () => {
let glPaginatedList;
const template = `
- <div class="slot" slot-scope="{ listItem }">
- <span class="item">Item Name: {{listItem.id}}</span>
- </div>
+ <template #default="{ listItem }">
+ <div class="slot">
+ <span class="item">Item Name: {{ listItem.id }}</span>
+ </div>
+ </template>
`;
const props = {
diff --git a/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap b/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap
index add0c36a120..cdfe311acd9 100644
--- a/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap
+++ b/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/resizable_chart_container_spec.js.snap
@@ -2,20 +2,22 @@
exports[`Resizable Chart Container renders the component 1`] = `
<div>
- <div
- class="slot"
- >
- <span
- class="width"
+ <template>
+ <div
+ class="slot"
>
- 0
- </span>
-
- <span
- class="height"
- >
- 0
- </span>
- </div>
+ <span
+ class="width"
+ >
+ 0
+ </span>
+
+ <span
+ class="height"
+ >
+ 0
+ </span>
+ </div>
+ </template>
</div>
`;
diff --git a/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js b/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js
index 1fce3c5d0b0..40f0c0f29f2 100644
--- a/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js
+++ b/spec/frontend/vue_shared/components/resizable_chart/resizable_chart_container_spec.js
@@ -16,10 +16,12 @@ describe('Resizable Chart Container', () => {
wrapper = mount(ResizableChartContainer, {
scopedSlots: {
default: `
- <div class="slot" slot-scope="{ width, height }">
- <span class="width">{{width}}</span>
- <span class="height">{{height}}</span>
- </div>
+ <template #default="{ width, height }">
+ <div class="slot">
+ <span class="width">{{width}}</span>
+ <span class="height">{{height}}</span>
+ </div>
+ </template>
`,
},
});
diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb
index 8ccbd983ba1..f99ac84175c 100644
--- a/spec/models/service_desk_setting_spec.rb
+++ b/spec/models/service_desk_setting_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe ServiceDeskSetting do
it { is_expected.to validate_length_of(:outgoing_name).is_at_most(255) }
it { is_expected.to validate_length_of(:project_key).is_at_most(255) }
it { is_expected.to allow_value('abc123_').for(:project_key) }
- it { is_expected.not_to allow_value('abc 12').for(:project_key) }
+ it { is_expected.not_to allow_value('abc 12').for(:project_key).with_message("can contain only lowercase letters, digits, and '_'.") }
it { is_expected.not_to allow_value('Big val').for(:project_key) }
describe '.valid_issue_template' do
diff --git a/spec/requests/api/graphql/mutations/packages/destroy_package_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_package_spec.rb
new file mode 100644
index 00000000000..e5ced419ecf
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/packages/destroy_package_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Destroying a package' do
+ using RSpec::Parameterized::TableSyntax
+
+ include GraphqlHelpers
+
+ let_it_be_with_reload(:package) { create(:package) }
+ let_it_be(:user) { create(:user) }
+
+ let(:project) { package.project }
+ let(:id) { package.to_global_id.to_s }
+
+ let(:query) do
+ <<~GQL
+ errors
+ GQL
+ end
+
+ let(:params) { { id: id } }
+ let(:mutation) { graphql_mutation(:destroy_package, params, query) }
+ let(:mutation_response) { graphql_mutation_response(:destroyPackage) }
+
+ shared_examples 'destroying the package' do
+ it 'destroy the package' do
+ expect(::Packages::DestroyPackageService)
+ .to receive(:new).with(container: package, current_user: user).and_call_original
+
+ expect { mutation_request }.to change { ::Packages::Package.count }.by(-1)
+ end
+
+ it_behaves_like 'returning response status', :success
+ end
+
+ shared_examples 'denying the mutation request' do
+ it 'does not destroy the package' do
+ expect(::Packages::DestroyPackageService)
+ .not_to receive(:new).with(container: package, current_user: user)
+
+ expect { mutation_request }.not_to change { ::Packages::Package.count }
+
+ expect(mutation_response).to be_nil
+ end
+
+ it_behaves_like 'returning response status', :success
+ end
+
+ describe 'post graphql mutation' do
+ subject(:mutation_request) { post_graphql_mutation(mutation, current_user: user) }
+
+ context 'with valid id' do
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'destroying the package'
+ :developer | 'denying the mutation request'
+ :reporter | 'denying the mutation request'
+ :guest | 'denying the mutation request'
+ :anonymous | 'denying the mutation request'
+ end
+
+ with_them do
+ before do
+ project.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+
+ context 'with invalid id' do
+ let(:params) { { id: 'gid://gitlab/Packages::Package/5555' } }
+
+ it_behaves_like 'denying the mutation request'
+ end
+
+ context 'when an error occures' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'returns the errors in the response' do
+ allow_next_found_instance_of(::Packages::Package) do |package|
+ allow(package).to receive(:destroy!).and_raise(StandardError)
+ end
+
+ mutation_request
+
+ expect(mutation_response['errors']).to eq(['Failed to remove the package'])
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/destroy_package_service_spec.rb b/spec/services/packages/destroy_package_service_spec.rb
new file mode 100644
index 00000000000..92db8da968c
--- /dev/null
+++ b/spec/services/packages/destroy_package_service_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::DestroyPackageService do
+ let_it_be(:user) { create(:user) }
+
+ let!(:package) { create(:npm_package) }
+
+ describe '#execute' do
+ subject(:service) { described_class.new(container: package, current_user: user) }
+
+ context 'when the user is authorized' do
+ before do
+ package.project.add_maintainer(user)
+ end
+
+ context 'when the destroy is successfull' do
+ it 'destroy the package' do
+ expect(package).to receive(:sync_maven_metadata).and_call_original
+ expect { service.execute }.to change { Packages::Package.count }.by(-1)
+ end
+
+ it 'returns a success ServiceResponse' do
+ response = service.execute
+
+ expect(response).to be_a(ServiceResponse)
+ expect(response).to be_success
+ expect(response.message).to eq("Package was successfully deleted")
+ end
+ end
+
+ context 'when the destroy is not successful' do
+ before do
+ allow(package).to receive(:destroy!).and_raise(StandardError, "test")
+ end
+
+ it 'returns an error ServiceResponse' do
+ response = service.execute
+
+ expect(package).not_to receive(:sync_maven_metadata)
+ expect(response).to be_a(ServiceResponse)
+ expect(response).to be_error
+ expect(response.message).to eq("Failed to remove the package")
+ expect(response.status).to eq(:error)
+ end
+ end
+ end
+
+ context 'when the user is not authorized' do
+ it 'returns an error ServiceResponse' do
+ response = service.execute
+
+ expect(response).to be_a(ServiceResponse)
+ expect(response).to be_error
+ expect(response.message).to eq("You don't have access to this package")
+ expect(response.status).to eq(:error)
+ end
+ end
+ end
+end