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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-11-16 12:13:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-16 12:13:21 +0300
commit2c90b9b579fbfe3db191a032d2cb176761605a02 (patch)
treed9819280a1ec64ff82c31ce6081e00745a9648b4 /spec
parentccca6cec346d169fa2521c390760af9bd885ea77 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/profiles/two_factor_auths_controller_spec.rb2
-rw-r--r--spec/features/profiles/two_factor_auths_spec.rb13
-rw-r--r--spec/frontend/boards/components/issue_board_filtered_search_spec.js26
-rw-r--r--spec/frontend/boards/mock_data.js13
-rw-r--r--spec/frontend/notes/components/discussion_counter_spec.js6
-rw-r--r--spec/frontend/notes/stores/actions_spec.js91
-rw-r--r--spec/frontend/notes/stores/mutation_spec.js10
-rw-r--r--spec/lib/api/helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/job_coordinator_spec.rb30
-rw-r--r--spec/lib/gitlab/security/scan_configuration_spec.rb64
-rw-r--r--spec/models/concerns/noteable_spec.rb64
-rw-r--r--spec/presenters/projects/security/configuration_presenter_spec.rb301
-rw-r--r--spec/requests/api/ci/jobs_spec.rb2
-rw-r--r--spec/requests/api/files_spec.rb44
-rw-r--r--spec/requests/api/project_snapshots_spec.rb1
-rw-r--r--spec/requests/api/project_snippets_spec.rb1
-rw-r--r--spec/requests/api/repositories_spec.rb2
-rw-r--r--spec/requests/api/snippets_spec.rb1
-rw-r--r--spec/requests/projects/issues_controller_spec.rb71
-rw-r--r--spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb12
-rw-r--r--spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb2
-rw-r--r--spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb11
-rw-r--r--spec/support/database/cross-database-modification-allowlist.yml9
-rw-r--r--spec/support/shared_examples/requests/snippet_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb25
25 files changed, 380 insertions, 424 deletions
diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb
index 48331d74dcc..47086ccdd2c 100644
--- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb
+++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb
@@ -175,7 +175,7 @@ RSpec.describe Profiles::TwoFactorAuthsController do
it 'assigns error' do
go
- expect(assigns[:error]).to eq _('Invalid pin code')
+ expect(assigns[:error]).to eq({ message: 'Invalid pin code.' })
end
it 'assigns qr_code' do
diff --git a/spec/features/profiles/two_factor_auths_spec.rb b/spec/features/profiles/two_factor_auths_spec.rb
index fcb857df93f..a9256a73d7b 100644
--- a/spec/features/profiles/two_factor_auths_spec.rb
+++ b/spec/features/profiles/two_factor_auths_spec.rb
@@ -45,6 +45,19 @@ RSpec.describe 'Two factor auths' do
expect(page).to have_content('Status: Enabled')
end
end
+
+ context 'when invalid pin is provided' do
+ let_it_be(:user) { create(:omniauth_user) }
+
+ it 'renders a error alert with a link to the troubleshooting section' do
+ visit profile_two_factor_auth_path
+
+ fill_in 'pin_code', with: '123'
+ click_button 'Register with two-factor app'
+
+ expect(page).to have_link('Try the troubleshooting steps here.', href: help_page_path('user/profile/account/two_factor_authentication.md', anchor: 'troubleshooting'))
+ end
+ end
end
context 'when user has two-factor authentication enabled' do
diff --git a/spec/frontend/boards/components/issue_board_filtered_search_spec.js b/spec/frontend/boards/components/issue_board_filtered_search_spec.js
index 200469b3f2a..45c5c87d800 100644
--- a/spec/frontend/boards/components/issue_board_filtered_search_spec.js
+++ b/spec/frontend/boards/components/issue_board_filtered_search_spec.js
@@ -11,11 +11,11 @@ describe('IssueBoardFilter', () => {
const findBoardsFilteredSearch = () => wrapper.findComponent(BoardFilteredSearch);
- const createComponent = ({ epicFeatureAvailable = false } = {}) => {
+ const createComponent = ({ isSignedIn = false } = {}) => {
wrapper = shallowMount(IssueBoardFilteredSpec, {
propsData: { fullPath: 'gitlab-org', boardType: 'group' },
provide: {
- epicFeatureAvailable,
+ isSignedIn,
},
});
};
@@ -45,10 +45,24 @@ describe('IssueBoardFilter', () => {
expect(findBoardsFilteredSearch().exists()).toBe(true);
});
- it('passes the correct tokens to BoardFilteredSearch', () => {
- const tokens = mockTokens(fetchLabelsSpy, fetchAuthorsSpy, wrapper.vm.fetchMilestones);
+ it.each`
+ isSignedIn
+ ${true}
+ ${false}
+ `(
+ 'passes the correct tokens to BoardFilteredSearch when user sign in is $isSignedIn',
+ ({ isSignedIn }) => {
+ createComponent({ isSignedIn });
- expect(findBoardsFilteredSearch().props('tokens')).toEqual(tokens);
- });
+ const tokens = mockTokens(
+ fetchLabelsSpy,
+ fetchAuthorsSpy,
+ wrapper.vm.fetchMilestones,
+ isSignedIn,
+ );
+
+ expect(findBoardsFilteredSearch().props('tokens')).toEqual(tokens);
+ },
+ );
});
});
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 1898ee87d7e..bba8b9ecc15 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -4,6 +4,7 @@ import { ListType } from '~/boards/constants';
import { __ } from '~/locale';
import { DEFAULT_MILESTONES_GRAPHQL } from '~/vue_shared/components/filtered_search_bar/constants';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import WeightToken from '~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue';
@@ -549,7 +550,16 @@ export const mockMoveData = {
...mockMoveIssueParams,
};
-export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
+export const mockEmojiToken = {
+ type: 'my_reaction_emoji',
+ icon: 'thumb-up',
+ title: 'My-Reaction',
+ unique: true,
+ token: EmojiToken,
+ fetchEmojis: expect.any(Function),
+};
+
+export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, hasEmoji) => [
{
icon: 'user',
title: __('Assignee'),
@@ -590,6 +600,7 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
symbol: '~',
fetchLabels,
},
+ ...(hasEmoji ? [mockEmojiToken] : []),
{
icon: 'clock',
title: __('Milestone'),
diff --git a/spec/frontend/notes/components/discussion_counter_spec.js b/spec/frontend/notes/components/discussion_counter_spec.js
index 9db0f823d84..c454d502beb 100644
--- a/spec/frontend/notes/components/discussion_counter_spec.js
+++ b/spec/frontend/notes/components/discussion_counter_spec.js
@@ -53,7 +53,7 @@ describe('DiscussionCounter component', () => {
describe('has no resolvable discussions', () => {
it('does not render', () => {
- store.commit(types.SET_INITIAL_DISCUSSIONS, [{ ...discussionMock, resolvable: false }]);
+ store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [{ ...discussionMock, resolvable: false }]);
store.dispatch('updateResolvableDiscussionsCounts');
wrapper = shallowMount(DiscussionCounter, { store, localVue });
@@ -64,7 +64,7 @@ describe('DiscussionCounter component', () => {
describe('has resolvable discussions', () => {
const updateStore = (note = {}) => {
discussionMock.notes[0] = { ...discussionMock.notes[0], ...note };
- store.commit(types.SET_INITIAL_DISCUSSIONS, [discussionMock]);
+ store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [discussionMock]);
store.dispatch('updateResolvableDiscussionsCounts');
};
@@ -97,7 +97,7 @@ describe('DiscussionCounter component', () => {
let toggleAllButton;
const updateStoreWithExpanded = (expanded) => {
const discussion = { ...discussionMock, expanded };
- store.commit(types.SET_INITIAL_DISCUSSIONS, [discussion]);
+ store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [discussion]);
store.dispatch('updateResolvableDiscussionsCounts');
wrapper = shallowMount(DiscussionCounter, { store, localVue });
toggleAllButton = wrapper.find('.toggle-all-discussions-btn');
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 2ff65d3f47e..bbe074f0105 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -119,7 +119,7 @@ describe('Actions Notes Store', () => {
actions.setInitialNotes,
[individualNote],
{ notes: [] },
- [{ type: 'SET_INITIAL_DISCUSSIONS', payload: [individualNote] }],
+ [{ type: 'ADD_OR_UPDATE_DISCUSSIONS', payload: [individualNote] }],
[],
done,
);
@@ -1395,4 +1395,93 @@ describe('Actions Notes Store', () => {
);
});
});
+
+ describe('fetchDiscussions', () => {
+ const discussion = { notes: [] };
+
+ afterEach(() => {
+ window.gon = {};
+ });
+
+ it('updates the discussions and dispatches `updateResolvableDiscussionsCounts`', (done) => {
+ axiosMock.onAny().reply(200, { discussion });
+ testAction(
+ actions.fetchDiscussions,
+ {},
+ null,
+ [
+ { type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } },
+ { type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false },
+ ],
+ [{ type: 'updateResolvableDiscussionsCounts' }],
+ done,
+ );
+ });
+
+ it('dispatches `fetchDiscussionsBatch` action if `paginatedIssueDiscussions` feature flag is enabled', (done) => {
+ window.gon = { features: { paginatedIssueDiscussions: true } };
+
+ testAction(
+ actions.fetchDiscussions,
+ { path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },
+ null,
+ [],
+ [
+ {
+ type: 'fetchDiscussionsBatch',
+ payload: {
+ config: {
+ params: { notes_filter: 'test-filter', persist_filter: 'test-persist-filter' },
+ },
+ path: 'test-path',
+ perPage: 20,
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('fetchDiscussionsBatch', () => {
+ const discussion = { notes: [] };
+
+ const config = {
+ params: { notes_filter: 'test-filter', persist_filter: 'test-persist-filter' },
+ };
+
+ const actionPayload = { config, path: 'test-path', perPage: 20 };
+
+ it('updates the discussions and dispatches `updateResolvableDiscussionsCounts if there are no headers', (done) => {
+ axiosMock.onAny().reply(200, { discussion }, {});
+ testAction(
+ actions.fetchDiscussionsBatch,
+ actionPayload,
+ null,
+ [
+ { type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } },
+ { type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false },
+ ],
+ [{ type: 'updateResolvableDiscussionsCounts' }],
+ done,
+ );
+ });
+
+ it('dispatches itself if there is `x-next-page-cursor` header', (done) => {
+ axiosMock.onAny().reply(200, { discussion }, { 'x-next-page-cursor': 1 });
+ testAction(
+ actions.fetchDiscussionsBatch,
+ actionPayload,
+ null,
+ [{ type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } }],
+ [
+ {
+ type: 'fetchDiscussionsBatch',
+ payload: { ...actionPayload, perPage: 30, cursor: 1 },
+ },
+ ],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js
index 99e24f724f4..c9e24039b64 100644
--- a/spec/frontend/notes/stores/mutation_spec.js
+++ b/spec/frontend/notes/stores/mutation_spec.js
@@ -159,7 +159,7 @@ describe('Notes Store mutations', () => {
});
});
- describe('SET_INITIAL_DISCUSSIONS', () => {
+ describe('ADD_OR_UPDATE_DISCUSSIONS', () => {
it('should set the initial notes received', () => {
const state = {
discussions: [],
@@ -169,15 +169,17 @@ describe('Notes Store mutations', () => {
individual_note: true,
notes: [
{
+ id: 100,
note: '1',
},
{
+ id: 101,
note: '2',
},
],
};
- mutations.SET_INITIAL_DISCUSSIONS(state, [note, legacyNote]);
+ mutations.ADD_OR_UPDATE_DISCUSSIONS(state, [note, legacyNote]);
expect(state.discussions[0].id).toEqual(note.id);
expect(state.discussions[1].notes[0].note).toBe(legacyNote.notes[0].note);
@@ -190,7 +192,7 @@ describe('Notes Store mutations', () => {
discussions: [],
};
- mutations.SET_INITIAL_DISCUSSIONS(state, [
+ mutations.ADD_OR_UPDATE_DISCUSSIONS(state, [
{
...note,
diff_file: {
@@ -208,7 +210,7 @@ describe('Notes Store mutations', () => {
discussions: [],
};
- mutations.SET_INITIAL_DISCUSSIONS(state, [
+ mutations.ADD_OR_UPDATE_DISCUSSIONS(state, [
{
...note,
diff_file: {
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index 37e040a422b..2277bd78e86 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -351,12 +351,14 @@ RSpec.describe API::Helpers do
let(:send_git_blob) do
subject.send(:send_git_blob, repository, blob)
+ subject.header
end
before do
allow(subject).to receive(:env).and_return({})
allow(subject).to receive(:content_type)
allow(subject).to receive(:header).and_return({})
+ allow(subject).to receive(:body).and_return('')
allow(Gitlab::Workhorse).to receive(:send_git_blob)
end
diff --git a/spec/lib/gitlab/background_migration/job_coordinator_spec.rb b/spec/lib/gitlab/background_migration/job_coordinator_spec.rb
index 5e029f304c9..a0543ca9958 100644
--- a/spec/lib/gitlab/background_migration/job_coordinator_spec.rb
+++ b/spec/lib/gitlab/background_migration/job_coordinator_spec.rb
@@ -73,6 +73,25 @@ RSpec.describe Gitlab::BackgroundMigration::JobCoordinator do
coordinator.steal('Foo')
end
+ it 'sets up the shared connection while stealing jobs' do
+ connection = double('connection')
+ allow(coordinator).to receive(:connection).and_return(connection)
+
+ expect(coordinator).to receive(:with_shared_connection).and_call_original
+
+ expect(queue[0]).to receive(:delete).and_return(true)
+
+ expect(coordinator).to receive(:perform).with('Foo', [10, 20]) do
+ expect(Gitlab::Database::SharedModel.connection).to be(connection)
+ end
+
+ coordinator.steal('Foo') do
+ expect(Gitlab::Database::SharedModel.connection).to be(connection)
+
+ true # the job is only performed if the block returns true
+ end
+ end
+
it 'does not steal job that has already been taken' do
expect(queue[0]).to receive(:delete).and_return(false)
@@ -194,13 +213,20 @@ RSpec.describe Gitlab::BackgroundMigration::JobCoordinator do
describe '#perform' do
let(:migration) { spy(:migration) }
+ let(:connection) { double('connection') }
before do
stub_const('Gitlab::BackgroundMigration::Foo', migration)
+
+ allow(coordinator).to receive(:connection).and_return(connection)
end
- it 'performs a background migration' do
- expect(migration).to receive(:perform).with(10, 20).once
+ it 'performs a background migration with the configured shared connection' do
+ expect(coordinator).to receive(:with_shared_connection).and_call_original
+
+ expect(migration).to receive(:perform).with(10, 20).once do
+ expect(Gitlab::Database::SharedModel.connection).to be(connection)
+ end
coordinator.perform('Foo', [10, 20])
end
diff --git a/spec/lib/gitlab/security/scan_configuration_spec.rb b/spec/lib/gitlab/security/scan_configuration_spec.rb
deleted file mode 100644
index 9b62c13f3bb..00000000000
--- a/spec/lib/gitlab/security/scan_configuration_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ::Gitlab::Security::ScanConfiguration do
- let_it_be(:project) { create(:project, :repository) }
-
- let(:scan) { described_class.new(project: project, type: type, configured: configured) }
-
- describe '#available?' do
- subject { scan.available? }
-
- let(:configured) { true }
-
- context 'with a core scanner' do
- let(:type) { :sast }
-
- it { is_expected.to be_truthy }
- end
-
- context 'with custom scanner' do
- let(:type) { :my_scanner }
-
- it { is_expected.to be_falsey }
- end
- end
-
- describe '#configured?' do
- subject { scan.configured? }
-
- let(:type) { :sast }
- let(:configured) { false }
-
- it { is_expected.to be_falsey }
- end
-
- describe '#configuration_path' do
- subject { scan.configuration_path }
-
- let(:configured) { true }
-
- context 'with a non configurable scaner' do
- let(:type) { :secret_detection }
-
- it { is_expected.to be_nil }
- end
-
- context 'with licensed scanner for FOSS environment' do
- let(:type) { :dast }
-
- before do
- stub_env('FOSS_ONLY', '1')
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with custom scanner' do
- let(:type) { :my_scanner }
-
- it { is_expected.to be_nil }
- end
- end
-end
diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb
index 38766d8decd..81ae30b7116 100644
--- a/spec/models/concerns/noteable_spec.rb
+++ b/spec/models/concerns/noteable_spec.rb
@@ -77,6 +77,70 @@ RSpec.describe Noteable do
end
end
+ describe '#discussion_root_note_ids' do
+ let!(:label_event) { create(:resource_label_event, merge_request: subject) }
+ let!(:system_note) { create(:system_note, project: project, noteable: subject) }
+ let!(:milestone_event) { create(:resource_milestone_event, merge_request: subject) }
+ let!(:state_event) { create(:resource_state_event, merge_request: subject) }
+
+ it 'returns ordered discussion_ids and synthetic note ids' do
+ discussions = subject.discussion_root_note_ids(notes_filter: UserPreference::NOTES_FILTERS[:all_notes]).map do |n|
+ { table_name: n.table_name, discussion_id: n.discussion_id, id: n.id }
+ end
+
+ expect(discussions).to match([
+ a_hash_including(table_name: 'notes', discussion_id: active_diff_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: active_diff_note3.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: outdated_diff_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: discussion_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_diff_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_note2.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_discussion_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_discussion_note3.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: note2.discussion_id),
+ a_hash_including(table_name: 'resource_label_events', id: label_event.id),
+ a_hash_including(table_name: 'notes', discussion_id: system_note.discussion_id),
+ a_hash_including(table_name: 'resource_milestone_events', id: milestone_event.id),
+ a_hash_including(table_name: 'resource_state_events', id: state_event.id)
+ ])
+ end
+
+ it 'filters by comments only' do
+ discussions = subject.discussion_root_note_ids(notes_filter: UserPreference::NOTES_FILTERS[:only_comments]).map do |n|
+ { table_name: n.table_name, discussion_id: n.discussion_id, id: n.id }
+ end
+
+ expect(discussions).to match([
+ a_hash_including(table_name: 'notes', discussion_id: active_diff_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: active_diff_note3.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: outdated_diff_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: discussion_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_diff_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_note2.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_discussion_note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: commit_discussion_note3.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: note1.discussion_id),
+ a_hash_including(table_name: 'notes', discussion_id: note2.discussion_id)
+ ])
+ end
+
+ it 'filters by system notes only' do
+ discussions = subject.discussion_root_note_ids(notes_filter: UserPreference::NOTES_FILTERS[:only_activity]).map do |n|
+ { table_name: n.table_name, discussion_id: n.discussion_id, id: n.id }
+ end
+
+ expect(discussions).to match([
+ a_hash_including(table_name: 'resource_label_events', id: label_event.id),
+ a_hash_including(table_name: 'notes', discussion_id: system_note.discussion_id),
+ a_hash_including(table_name: 'resource_milestone_events', id: milestone_event.id),
+ a_hash_including(table_name: 'resource_state_events', id: state_event.id)
+ ])
+ end
+ end
+
describe '#grouped_diff_discussions' do
let(:grouped_diff_discussions) { subject.grouped_diff_discussions }
diff --git a/spec/presenters/projects/security/configuration_presenter_spec.rb b/spec/presenters/projects/security/configuration_presenter_spec.rb
deleted file mode 100644
index 836753d0483..00000000000
--- a/spec/presenters/projects/security/configuration_presenter_spec.rb
+++ /dev/null
@@ -1,301 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::Security::ConfigurationPresenter do
- include Gitlab::Routing.url_helpers
- using RSpec::Parameterized::TableSyntax
-
- let(:project_with_repo) { create(:project, :repository) }
- let(:project_with_no_repo) { create(:project) }
- let(:current_user) { create(:user) }
- let(:presenter) { described_class.new(project, current_user: current_user) }
-
- before do
- stub_licensed_features(licensed_scan_types.to_h { |type| [type, true] })
-
- stub_feature_flags(corpus_management: false)
- end
-
- describe '#to_html_data_attribute' do
- subject(:html_data) { presenter.to_html_data_attribute }
-
- context 'when latest default branch pipeline`s source is not auto devops' do
- let(:project) { project_with_repo }
-
- let(:pipeline) do
- create(
- :ci_pipeline,
- project: project,
- ref: project.default_branch,
- sha: project.commit.sha
- )
- end
-
- let!(:build_sast) { create(:ci_build, :sast, pipeline: pipeline) }
- let!(:build_dast) { create(:ci_build, :dast, pipeline: pipeline) }
- let!(:build_license_scanning) { create(:ci_build, :license_scanning, pipeline: pipeline) }
-
- it 'includes links to auto devops and secure product docs' do
- expect(html_data[:auto_devops_help_page_path]).to eq(help_page_path('topics/autodevops/index'))
- expect(html_data[:help_page_path]).to eq(help_page_path('user/application_security/index'))
- end
-
- it 'returns info that Auto DevOps is not enabled' do
- expect(html_data[:auto_devops_enabled]).to eq(false)
- expect(html_data[:auto_devops_path]).to eq(project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
- end
-
- it 'includes a link to the latest pipeline' do
- expect(html_data[:latest_pipeline_path]).to eq(project_pipeline_path(project, pipeline))
- end
-
- it 'has stubs for autofix' do
- expect(html_data.keys).to include(:can_toggle_auto_fix_settings, :auto_fix_enabled, :auto_fix_user_path)
- end
-
- context "while retrieving information about user's ability to enable auto_devops" do
- where(:is_admin, :archived, :feature_available, :result) do
- true | true | true | false
- false | true | true | false
- true | false | true | true
- false | false | true | false
- true | true | false | false
- false | true | false | false
- true | false | false | false
- false | false | false | false
- end
-
- with_them do
- before do
- allow_next_instance_of(described_class) do |presenter|
- allow(presenter).to receive(:can?).and_return(is_admin)
- allow(presenter).to receive(:archived?).and_return(archived)
- allow(presenter).to receive(:feature_available?).and_return(feature_available)
- end
- end
-
- it 'includes can_enable_auto_devops' do
- expect(html_data[:can_enable_auto_devops]).to eq(result)
- end
- end
- end
-
- it 'includes feature information' do
- feature = Gitlab::Json.parse(html_data[:features]).find { |scan| scan['type'] == 'sast' }
-
- expect(feature['type']).to eq('sast')
- expect(feature['configured']).to eq(true)
- expect(feature['configuration_path']).to eq(project_security_configuration_sast_path(project))
- expect(feature['available']).to eq(true)
- end
-
- context 'when checking features configured status' do
- let(:features) { Gitlab::Json.parse(html_data[:features]) }
-
- where(:type, :configured) do
- :dast | true
- :dast_profiles | true
- :sast | true
- :sast_iac | false
- :container_scanning | false
- :cluster_image_scanning | false
- :dependency_scanning | false
- :license_scanning | true
- :secret_detection | false
- :coverage_fuzzing | false
- :api_fuzzing | false
- :corpus_management | true
- end
-
- with_them do
- it 'returns proper configuration status' do
- feature = features.find { |scan| scan['type'] == type.to_s }
-
- expect(feature['configured']).to eq(configured)
- end
- end
- end
-
- context 'when the job has more than one report' do
- let(:features) { Gitlab::Json.parse(html_data[:features]) }
-
- let!(:artifacts) do
- { artifacts: { reports: { other_job: ['gl-other-report.json'], sast: ['gl-sast-report.json'] } } }
- end
-
- let!(:complicated_job) { build_stubbed(:ci_build, options: artifacts) }
-
- before do
- allow_next_instance_of(::Security::SecurityJobsFinder) do |finder|
- allow(finder).to receive(:execute).and_return([complicated_job])
- end
- end
-
- where(:type, :configured) do
- :dast | false
- :dast_profiles | true
- :sast | true
- :sast_iac | false
- :container_scanning | false
- :cluster_image_scanning | false
- :dependency_scanning | false
- :license_scanning | true
- :secret_detection | false
- :coverage_fuzzing | false
- :api_fuzzing | false
- :corpus_management | true
- end
-
- with_them do
- it 'properly detects security jobs' do
- feature = features.find { |scan| scan['type'] == type.to_s }
-
- expect(feature['configured']).to eq(configured)
- end
- end
- end
-
- it 'includes a link to the latest pipeline' do
- expect(subject[:latest_pipeline_path]).to eq(project_pipeline_path(project, pipeline))
- end
-
- context "while retrieving information about gitlab ci file" do
- context 'when a .gitlab-ci.yml file exists' do
- let!(:ci_config) do
- project.repository.create_file(
- project.creator,
- Gitlab::FileDetector::PATTERNS[:gitlab_ci],
- 'contents go here',
- message: 'test',
- branch_name: 'master')
- end
-
- it 'expects gitlab_ci_present to be true' do
- expect(html_data[:gitlab_ci_present]).to eq(true)
- end
- end
-
- context 'when a .gitlab-ci.yml file does not exist' do
- it 'expects gitlab_ci_present to be false if the file is not present' do
- expect(html_data[:gitlab_ci_present]).to eq(false)
- end
- end
- end
-
- it 'includes the path to gitlab_ci history' do
- expect(subject[:gitlab_ci_history_path]).to eq(project_blame_path(project, 'master/.gitlab-ci.yml'))
- end
- end
-
- context 'when the project is empty' do
- let(:project) { project_with_no_repo }
-
- it 'includes a blank gitlab_ci history path' do
- expect(html_data[:gitlab_ci_history_path]).to eq('')
- end
- end
-
- context 'when the project has no default branch set' do
- let(:project) { project_with_repo }
-
- it 'includes the path to gitlab_ci history' do
- allow(project).to receive(:default_branch).and_return(nil)
-
- expect(html_data[:gitlab_ci_history_path]).to eq(project_blame_path(project, 'master/.gitlab-ci.yml'))
- end
- end
-
- context "when the latest default branch pipeline's source is auto devops" do
- let(:project) { project_with_repo }
-
- let(:pipeline) do
- create(
- :ci_pipeline,
- :auto_devops_source,
- project: project,
- ref: project.default_branch,
- sha: project.commit.sha
- )
- end
-
- let!(:build_sast) { create(:ci_build, :sast, pipeline: pipeline, status: 'success') }
- let!(:build_dast) { create(:ci_build, :dast, pipeline: pipeline, status: 'success') }
- let!(:ci_build) { create(:ci_build, :secret_detection, pipeline: pipeline, status: 'pending') }
-
- it 'reports that auto devops is enabled' do
- expect(html_data[:auto_devops_enabled]).to be_truthy
- end
-
- context 'when gathering feature data' do
- let(:features) { Gitlab::Json.parse(html_data[:features]) }
-
- where(:type, :configured) do
- :dast | true
- :dast_profiles | true
- :sast | true
- :sast_iac | false
- :container_scanning | false
- :cluster_image_scanning | false
- :dependency_scanning | false
- :license_scanning | false
- :secret_detection | true
- :coverage_fuzzing | false
- :api_fuzzing | false
- :corpus_management | true
- end
-
- with_them do
- it 'reports that all scanners are configured for which latest pipeline has builds' do
- feature = features.find { |scan| scan['type'] == type.to_s }
-
- expect(feature['configured']).to eq(configured)
- end
- end
- end
- end
-
- context 'when the project has no default branch pipeline' do
- let(:project) { project_with_repo }
-
- it 'reports that auto devops is disabled' do
- expect(html_data[:auto_devops_enabled]).to be_falsy
- end
-
- it 'includes a link to CI pipeline docs' do
- expect(html_data[:latest_pipeline_path]).to eq(help_page_path('ci/pipelines'))
- end
-
- context 'when gathering feature data' do
- let(:features) { Gitlab::Json.parse(html_data[:features]) }
-
- where(:type, :configured) do
- :dast | false
- :dast_profiles | true
- :sast | false
- :sast_iac | false
- :container_scanning | false
- :cluster_image_scanning | false
- :dependency_scanning | false
- :license_scanning | false
- :secret_detection | false
- :coverage_fuzzing | false
- :api_fuzzing | false
- :corpus_management | true
- end
-
- with_them do
- it 'reports all security jobs as unconfigured with exception of "fake" jobs' do
- feature = features.find { |scan| scan['type'] == type.to_s }
-
- expect(feature['configured']).to eq(configured)
- end
- end
- end
- end
-
- def licensed_scan_types
- ::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types - [:cluster_image_scanning]
- end
- end
-end
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 580f3d9e92d..410020b68cd 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -578,6 +578,7 @@ RSpec.describe API::Ci::Jobs do
expect(response.headers.to_h)
.to include('Content-Type' => 'application/json',
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
+ expect(response.parsed_body).to be_empty
end
context 'when artifacts are locked' do
@@ -948,6 +949,7 @@ RSpec.describe API::Ci::Jobs do
expect(response.headers.to_h)
.to include('Content-Type' => 'application/json',
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
+ expect(response.parsed_body).to be_empty
end
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 0b898496dd6..6aa12b6ff48 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -47,6 +47,15 @@ RSpec.describe API::Files do
"/projects/#{project.id}/repository/files/#{file_path}"
end
+ def expect_to_send_git_blob(url, params)
+ expect(Gitlab::Workhorse).to receive(:send_git_blob)
+
+ get url, params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.parsed_body).to be_empty
+ end
+
context 'http headers' do
it 'converts value into string' do
helper.set_http_headers(test: 1)
@@ -257,11 +266,7 @@ RSpec.describe API::Files do
it 'returns raw file info' do
url = route(file_path) + "/raw"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
-
- get api(url, api_user, **options), params: params
-
- expect(response).to have_gitlab_http_status(:ok)
+ expect_to_send_git_blob(api(url, api_user, **options), params)
expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
@@ -523,11 +528,8 @@ RSpec.describe API::Files do
it 'returns raw file info' do
url = route(file_path) + "/raw"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
- get api(url, current_user), params: params
-
- expect(response).to have_gitlab_http_status(:ok)
+ expect_to_send_git_blob(api(url, current_user), params)
end
context 'when ref is not provided' do
@@ -537,39 +539,29 @@ RSpec.describe API::Files do
it 'returns response :ok', :aggregate_failures do
url = route(file_path) + "/raw"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
- get api(url, current_user), params: {}
-
- expect(response).to have_gitlab_http_status(:ok)
+ expect_to_send_git_blob(api(url, current_user), {})
end
end
it 'returns raw file info for files with dots' do
url = route('.gitignore') + "/raw"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
- get api(url, current_user), params: params
-
- expect(response).to have_gitlab_http_status(:ok)
+ expect_to_send_git_blob(api(url, current_user), params)
end
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
- get api(route(file_path) + "/raw", current_user), params: params
-
- expect(response).to have_gitlab_http_status(:ok)
+ expect_to_send_git_blob(api(route(file_path) + "/raw", current_user), params)
end
it 'sets no-cache headers' do
url = route('.gitignore') + "/raw"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
- get api(url, current_user), params: params
+ expect_to_send_git_blob(api(url, current_user), params)
expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
@@ -633,11 +625,9 @@ RSpec.describe API::Files do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
- expect(Gitlab::Workhorse).to receive(:send_git_blob)
+ url = api(route(file_path) + "/raw", personal_access_token: token)
- get api(route(file_path) + "/raw", personal_access_token: token), params: params
-
- expect(response).to have_gitlab_http_status(:ok)
+ expect_to_send_git_blob(url, params)
end
end
end
diff --git a/spec/requests/api/project_snapshots_spec.rb b/spec/requests/api/project_snapshots_spec.rb
index f23e374407b..33c86d56ed4 100644
--- a/spec/requests/api/project_snapshots_spec.rb
+++ b/spec/requests/api/project_snapshots_spec.rb
@@ -29,6 +29,7 @@ RSpec.describe API::ProjectSnapshots do
repository: repository.gitaly_repository
).to_json
)
+ expect(response.parsed_body).to be_empty
end
it 'returns authentication error as project owner' do
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 8cd1f15a88d..512cbf7c321 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -400,6 +400,7 @@ RSpec.describe API::ProjectSnippets do
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq 'text/plain'
+ expect(response.parsed_body).to be_empty
end
it 'returns 404 for invalid snippet id' do
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 35e8ff2eeee..f3146480be2 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -197,6 +197,7 @@ RSpec.describe API::Repositories do
expect(response).to have_gitlab_http_status(:ok)
expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ expect(response.parsed_body).to be_empty
end
it 'sets inline content disposition by default' do
@@ -274,6 +275,7 @@ RSpec.describe API::Repositories do
expect(type).to eq('git-archive')
expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.tar.gz/)
+ expect(response.parsed_body).to be_empty
end
it 'returns the repository archive archive.zip' do
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index f4d15d0525e..dd5e6ac8a5e 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -113,6 +113,7 @@ RSpec.describe API::Snippets, factory_default: :keep do
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq 'text/plain'
expect(headers['Content-Disposition']).to match(/^inline/)
+ expect(response.parsed_body).to be_empty
end
it 'returns 404 for invalid snippet id' do
diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb
new file mode 100644
index 00000000000..f44b1f4d502
--- /dev/null
+++ b/spec/requests/projects/issues_controller_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::IssuesController do
+ let_it_be(:issue) { create(:issue) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { issue.project }
+ let_it_be(:user) { issue.author }
+
+ before do
+ login_as(user)
+ end
+
+ describe 'GET #discussions' do
+ let_it_be(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
+ let_it_be(:discussion_reply) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, in_reply_to: discussion) }
+ let_it_be(:state_event) { create(:resource_state_event, issue: issue) }
+ let_it_be(:discussion_2) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
+ let_it_be(:discussion_3) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
+
+ context 'pagination' do
+ def get_discussions(**params)
+ get discussions_project_issue_path(project, issue, params: params.merge(format: :json))
+ end
+
+ it 'returns paginated notes and cursor based on per_page param' do
+ get_discussions(per_page: 2)
+
+ discussions = Gitlab::Json.parse(response.body)
+ notes = discussions.flat_map { |d| d['notes'] }
+
+ expect(discussions.count).to eq(2)
+ expect(notes).to match([
+ a_hash_including('id' => discussion.id.to_s),
+ a_hash_including('id' => discussion_reply.id.to_s),
+ a_hash_including('type' => 'StateNote')
+ ])
+
+ cursor = response.header['X-Next-Page-Cursor']
+ expect(cursor).to be_present
+
+ get_discussions(per_page: 1, cursor: cursor)
+
+ discussions = Gitlab::Json.parse(response.body)
+ notes = discussions.flat_map { |d| d['notes'] }
+
+ expect(discussions.count).to eq(1)
+ expect(notes).to match([
+ a_hash_including('id' => discussion_2.id.to_s)
+ ])
+ end
+
+ context 'when paginated_issue_discussions is disabled' do
+ before do
+ stub_feature_flags(paginated_issue_discussions: false)
+ end
+
+ it 'returns all discussions and ignores per_page param' do
+ get_discussions(per_page: 2)
+
+ discussions = Gitlab::Json.parse(response.body)
+ notes = discussions.flat_map { |d| d['notes'] }
+
+ expect(discussions.count).to eq(4)
+ expect(notes.count).to eq(5)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb b/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
index cb42ad5b617..71b1d0993ee 100644
--- a/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
+++ b/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
@@ -4,18 +4,20 @@ require 'spec_helper'
RSpec.describe ResourceEvents::SyntheticLabelNotesBuilderService do
describe '#execute' do
- let!(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
- let!(:issue) { create(:issue, author: user) }
+ let_it_be(:issue) { create(:issue, author: user) }
- let!(:event1) { create(:resource_label_event, issue: issue) }
- let!(:event2) { create(:resource_label_event, issue: issue) }
- let!(:event3) { create(:resource_label_event, issue: issue) }
+ let_it_be(:event1) { create(:resource_label_event, issue: issue) }
+ let_it_be(:event2) { create(:resource_label_event, issue: issue) }
+ let_it_be(:event3) { create(:resource_label_event, issue: issue) }
it 'returns the expected synthetic notes' do
notes = ResourceEvents::SyntheticLabelNotesBuilderService.new(issue, user).execute
expect(notes.size).to eq(3)
end
+
+ it_behaves_like 'filters by paginated notes', :resource_label_event
end
end
diff --git a/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb b/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
index 1b35e224e98..9c6b6a33b57 100644
--- a/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
+++ b/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
@@ -24,5 +24,7 @@ RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService do
'removed milestone'
])
end
+
+ it_behaves_like 'filters by paginated notes', :resource_milestone_event
end
end
diff --git a/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb b/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
new file mode 100644
index 00000000000..79500f3768b
--- /dev/null
+++ b/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ResourceEvents::SyntheticStateNotesBuilderService do
+ describe '#execute' do
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'filters by paginated notes', :resource_state_event
+ end
+end
diff --git a/spec/support/database/cross-database-modification-allowlist.yml b/spec/support/database/cross-database-modification-allowlist.yml
index e0166f2ae3f..e4dc958830f 100644
--- a/spec/support/database/cross-database-modification-allowlist.yml
+++ b/spec/support/database/cross-database-modification-allowlist.yml
@@ -8,14 +8,6 @@
- "./ee/spec/models/group_member_spec.rb"
- "./ee/spec/replicators/geo/pipeline_artifact_replicator_spec.rb"
- "./ee/spec/replicators/geo/terraform_state_version_replicator_spec.rb"
-- "./ee/spec/requests/api/graphql/mutations/dast/profiles/create_spec.rb"
-- "./ee/spec/requests/api/graphql/mutations/dast/profiles/run_spec.rb"
-- "./ee/spec/requests/api/graphql/mutations/dast/profiles/update_spec.rb"
-- "./ee/spec/requests/api/graphql/mutations/dast_on_demand_scans/create_spec.rb"
-- "./ee/spec/services/app_sec/dast/profiles/create_service_spec.rb"
-- "./ee/spec/services/app_sec/dast/profiles/update_service_spec.rb"
-- "./ee/spec/services/app_sec/dast/scans/create_service_spec.rb"
-- "./ee/spec/services/app_sec/dast/scans/run_service_spec.rb"
- "./ee/spec/services/ci/destroy_pipeline_service_spec.rb"
- "./ee/spec/services/ci/retry_build_service_spec.rb"
- "./ee/spec/services/ci/subscribe_bridge_service_spec.rb"
@@ -25,7 +17,6 @@
- "./ee/spec/services/ee/users/destroy_service_spec.rb"
- "./ee/spec/services/projects/transfer_service_spec.rb"
- "./ee/spec/services/security/security_orchestration_policies/rule_schedule_service_spec.rb"
-- "./ee/spec/services/vulnerability_feedback/create_service_spec.rb"
- "./spec/controllers/abuse_reports_controller_spec.rb"
- "./spec/controllers/admin/spam_logs_controller_spec.rb"
- "./spec/controllers/admin/users_controller_spec.rb"
diff --git a/spec/support/shared_examples/requests/snippet_shared_examples.rb b/spec/support/shared_examples/requests/snippet_shared_examples.rb
index dae3a3e74be..b13c4da0bed 100644
--- a/spec/support/shared_examples/requests/snippet_shared_examples.rb
+++ b/spec/support/shared_examples/requests/snippet_shared_examples.rb
@@ -86,6 +86,7 @@ RSpec.shared_examples 'snippet blob content' do
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ expect(response.parsed_body).to be_empty
end
context 'when snippet repository is empty' do
diff --git a/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
new file mode 100644
index 00000000000..716bee39fca
--- /dev/null
+++ b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'filters by paginated notes' do |event_type|
+ let(:event) { create(event_type) } # rubocop:disable Rails/SaveBang
+
+ before do
+ create(event_type, issue: event.issue)
+ end
+
+ it 'only returns given notes' do
+ paginated_notes = { event_type.to_s.pluralize => [double(id: event.id)] }
+ notes = described_class.new(event.issue, user, paginated_notes: paginated_notes).execute
+
+ expect(notes.size).to eq(1)
+ expect(notes.first.event).to eq(event)
+ end
+
+ context 'when paginated notes is empty' do
+ it 'does not return any notes' do
+ notes = described_class.new(event.issue, user, paginated_notes: {}).execute
+
+ expect(notes.size).to eq(0)
+ end
+ end
+end