diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-24 12:09:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-24 12:09:42 +0000 |
commit | 729e3765d5feb762df1ccfbc228a8dd4662aa3f9 (patch) | |
tree | f326420fc64999c6bcc28816ed54f0972fb46459 /spec | |
parent | 6f7881ee9dcec34141a8f34fc814b56b366d2b48 (diff) | |
download | gitlab-ce-729e3765d5feb762df1ccfbc228a8dd4662aa3f9.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
34 files changed, 784 insertions, 273 deletions
diff --git a/spec/controllers/projects/releases/evidences_controller_spec.rb b/spec/controllers/projects/releases/evidences_controller_spec.rb new file mode 100644 index 00000000000..d3808087681 --- /dev/null +++ b/spec/controllers/projects/releases/evidences_controller_spec.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Releases::EvidencesController do + let!(:project) { create(:project, :repository, :public) } + let_it_be(:private_project) { create(:project, :repository, :private) } + let_it_be(:developer) { create(:user) } + let_it_be(:reporter) { create(:user) } + let(:user) { developer } + + before do + project.add_developer(developer) + project.add_reporter(reporter) + end + + shared_examples_for 'successful request' do + it 'renders a 200' do + subject + + expect(response).to have_gitlab_http_status(:success) + end + end + + shared_examples_for 'not found' do + it 'renders 404' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + describe 'GET #show' do + let_it_be(:tag_name) { "v1.1.0-evidence" } + let!(:release) { create(:release, :with_evidence, project: project, tag: tag_name) } + let(:evidence) { release.evidences.first } + let(:tag) { CGI.escape(release.tag) } + let(:format) { :json } + + subject do + get :show, params: { + namespace_id: project.namespace.to_param, + project_id: project, + tag: tag, + id: evidence.id, + format: format + } + end + + before do + sign_in(user) + end + + context 'when the user is a developer' do + it 'returns the correct evidence summary as a json' do + subject + + expect(json_response).to eq(evidence.summary) + end + + context 'when the release was created before evidence existed' do + before do + evidence.destroy + end + + it_behaves_like 'not found' + end + end + + context 'when the user is a guest for the project' do + before do + project.add_guest(user) + end + + context 'when the project is private' do + let(:project) { private_project } + + it_behaves_like 'not found' + end + + context 'when the project is public' do + it_behaves_like 'successful request' + end + end + + context 'when release is associated to a milestone which includes an issue' do + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:milestone) { create(:milestone, project: project, issues: [issue]) } + let_it_be(:release) { create(:release, project: project, tag: tag_name, milestones: [milestone]) } + + before do + create(:evidence, release: release) + end + + shared_examples_for 'does not show the issue in evidence' do + it do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['release']['milestones'] + .all? { |milestone| milestone['issues'].nil? }).to eq(true) + end + end + + shared_examples_for 'evidence not found' do + it do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + shared_examples_for 'safely expose evidence' do + it_behaves_like 'does not show the issue in evidence' + + context 'when the issue is confidential' do + let(:issue) { create(:issue, :confidential, project: project) } + + it_behaves_like 'does not show the issue in evidence' + end + + context 'when the user is the author of the confidential issue' do + let(:issue) { create(:issue, :confidential, project: project, author: user) } + + it_behaves_like 'does not show the issue in evidence' + end + + context 'when project is private' do + let!(:project) { create(:project, :repository, :private) } + + it_behaves_like 'evidence not found' + end + + context 'when project restricts the visibility of issues to project members only' do + let!(:project) { create(:project, :repository, :issues_private) } + + it_behaves_like 'evidence not found' + end + end + + context 'when user is non-project member' do + let(:user) { create(:user) } + + it_behaves_like 'safely expose evidence' + end + + context 'when user is auditor', if: Gitlab.ee? do + let(:user) { create(:user, :auditor) } + + it_behaves_like 'safely expose evidence' + end + + context 'when external authorization control is enabled' do + let(:user) { create(:user) } + + before do + stub_application_setting(external_authorization_service_enabled: true) + end + + it_behaves_like 'evidence not found' + end + end + end +end diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index ca073c520cd..4c957e22d24 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -3,11 +3,11 @@ require 'spec_helper' describe Projects::ReleasesController do - let!(:project) { create(:project, :repository, :public) } - let!(:private_project) { create(:project, :repository, :private) } - let(:user) { developer } - let(:developer) { create(:user) } - let(:reporter) { create(:user) } + let!(:project) { create(:project, :repository, :public) } + let_it_be(:private_project) { create(:project, :repository, :private) } + let_it_be(:developer) { create(:user) } + let_it_be(:reporter) { create(:user) } + let_it_be(:user) { developer } let!(:release_1) { create(:release, project: project, released_at: Time.zone.parse('2018-10-18')) } let!(:release_2) { create(:release, project: project, released_at: Time.zone.parse('2019-10-19')) } @@ -295,141 +295,6 @@ describe Projects::ReleasesController do end end - describe 'GET #evidence' do - let_it_be(:tag_name) { "v1.1.0-evidence" } - let!(:release) { create(:release, :with_evidence, project: project, tag: tag_name) } - let(:tag) { CGI.escape(release.tag) } - let(:format) { :json } - - subject do - get :evidence, params: { - namespace_id: project.namespace, - project_id: project, - tag: tag, - format: format - } - end - - before do - sign_in(user) - end - - context 'when the user is a developer' do - it 'returns the correct evidence summary as a json' do - subject - - expect(json_response).to eq(release.evidence.summary) - end - - context 'when the release was created before evidence existed' do - before do - release.evidence.destroy - end - - it 'returns an empty json' do - subject - - expect(json_response).to eq({}) - end - end - end - - context 'when the user is a guest for the project' do - before do - project.add_guest(user) - end - - context 'when the project is private' do - let(:project) { private_project } - - it_behaves_like 'not found' - end - - context 'when the project is public' do - it_behaves_like 'successful request' - end - end - - context 'when release is associated to a milestone which includes an issue' do - let_it_be(:project) { create(:project, :repository, :public) } - let_it_be(:issue) { create(:issue, project: project) } - let_it_be(:milestone) { create(:milestone, project: project, issues: [issue]) } - let_it_be(:release) { create(:release, project: project, tag: tag_name, milestones: [milestone]) } - - before do - create(:evidence, release: release) - end - - shared_examples_for 'does not show the issue in evidence' do - it do - subject - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['release']['milestones'] - .all? { |milestone| milestone['issues'].nil? }).to eq(true) - end - end - - shared_examples_for 'evidence not found' do - it do - subject - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - shared_examples_for 'safely expose evidence' do - it_behaves_like 'does not show the issue in evidence' - - context 'when the issue is confidential' do - let(:issue) { create(:issue, :confidential, project: project) } - - it_behaves_like 'does not show the issue in evidence' - end - - context 'when the user is the author of the confidential issue' do - let(:issue) { create(:issue, :confidential, project: project, author: user) } - - it_behaves_like 'does not show the issue in evidence' - end - - context 'when project is private' do - let!(:project) { create(:project, :repository, :private) } - - it_behaves_like 'evidence not found' - end - - context 'when project restricts the visibility of issues to project members only' do - let!(:project) { create(:project, :repository, :issues_private) } - - it_behaves_like 'evidence not found' - end - end - - context 'when user is non-project member' do - let(:user) { create(:user) } - - it_behaves_like 'safely expose evidence' - end - - context 'when user is auditor', if: Gitlab.ee? do - let(:user) { create(:user, :auditor) } - - it_behaves_like 'safely expose evidence' - end - - context 'when external authorization control is enabled' do - let(:user) { create(:user) } - - before do - stub_application_setting(external_authorization_service_enabled: true) - end - - it_behaves_like 'evidence not found' - end - end - end - private def get_index diff --git a/spec/factories/events.rb b/spec/factories/events.rb index b4285627de3..5b456bb58ff 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -25,12 +25,12 @@ FactoryBot.define do factory :wiki_page_event do action { Event::CREATED } + project { @overrides[:wiki_page]&.project || create(:project, :wiki_repo) } + target { create(:wiki_page_meta, :for_wiki_page, wiki_page: wiki_page) } transient do wiki_page { create(:wiki_page, project: project) } end - - target { create(:wiki_page_meta, :for_wiki_page, wiki_page: wiki_page) } end end diff --git a/spec/factories/evidences.rb b/spec/factories/evidences.rb index 964f232a1c9..77116d8e9ed 100644 --- a/spec/factories/evidences.rb +++ b/spec/factories/evidences.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true FactoryBot.define do - factory :evidence do + factory :evidence, class: 'Releases::Evidence' do release end end diff --git a/spec/finders/events_finder_spec.rb b/spec/finders/events_finder_spec.rb index 5c28b31e8c8..443e9ab4bc4 100644 --- a/spec/finders/events_finder_spec.rb +++ b/spec/finders/events_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe EventsFinder do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } let(:other_user) { create(:user) } let(:project1) { create(:project, :private, creator_id: user.id, namespace: user.namespace) } @@ -20,7 +20,7 @@ describe EventsFinder do let(:opened_merge_request3) { create(:merge_request, source_project: project1, author: other_user) } let!(:other_developer_event) { create(:event, project: project1, author: other_user, target: opened_merge_request3, action: Event::CREATED) } - let(:public_project) { create(:project, :public, creator_id: user.id, namespace: user.namespace) } + let_it_be(:public_project) { create(:project, :public, creator_id: user.id, namespace: user.namespace) } let(:confidential_issue) { create(:closed_issue, confidential: true, project: public_project, author: user) } let!(:confidential_event) { create(:event, project: public_project, author: user, target: confidential_issue, action: Event::CLOSED) } @@ -59,6 +59,32 @@ describe EventsFinder do end end + describe 'wiki events feature flag' do + let_it_be(:events) { create_list(:wiki_page_event, 3, project: public_project) } + + subject(:finder) { described_class.new(source: public_project, target_type: 'wiki', current_user: user) } + + context 'the wiki_events feature flag is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'omits the wiki page events' do + expect(finder.execute).to be_empty + end + end + + context 'the wiki_events feature flag is enabled' do + before do + stub_feature_flags(wiki_events: true) + end + + it 'can find the wiki events' do + expect(finder.execute).to match_array(events) + end + end + end + context 'dashboard events' do before do project1.add_developer(other_user) diff --git a/spec/fixtures/api/schemas/public_api/v4/release.json b/spec/fixtures/api/schemas/public_api/v4/release.json index a239be09919..02e23d2732d 100644 --- a/spec/fixtures/api/schemas/public_api/v4/release.json +++ b/spec/fixtures/api/schemas/public_api/v4/release.json @@ -22,6 +22,10 @@ "commit_path": { "type": "string" }, "tag_path": { "type": "string" }, "name": { "type": "string" }, + "evidences": { + "type": "array", + "items": { "$ref": "release/evidence.json" } + }, "assets": { "required": ["count", "links", "sources"], "properties": { diff --git a/spec/fixtures/api/schemas/public_api/v4/release/evidence.json b/spec/fixtures/api/schemas/public_api/v4/release/evidence.json new file mode 100644 index 00000000000..fbebac0acaa --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/release/evidence.json @@ -0,0 +1,14 @@ +{ + "type": "object", + "required" : [ + "sha", + "filepath", + "collected_at" + ], + "properties" : { + "sha": { "type": "string" }, + "filepath": { "type": "string" }, + "collected_at": { "type": "date" } + }, + "additionalProperties": false +} diff --git a/spec/javascripts/locale/index_spec.js b/spec/frontend/locale/index_spec.js index 29b0b21eed7..346ed5182f4 100644 --- a/spec/javascripts/locale/index_spec.js +++ b/spec/frontend/locale/index_spec.js @@ -1,11 +1,9 @@ import { createDateTimeFormat, languageCode } from '~/locale'; -import { setLanguage } from '../helpers/locale_helper'; +import { setLanguage } from 'helpers/locale_helper'; describe('locale', () => { - afterEach(() => { - setLanguage(null); - }); + afterEach(() => setLanguage(null)); describe('languageCode', () => { it('parses the lang attribute', () => { @@ -22,14 +20,12 @@ describe('locale', () => { }); describe('createDateTimeFormat', () => { - beforeEach(() => { - setLanguage('de'); - }); + beforeEach(() => setLanguage('en')); it('creates an instance of Intl.DateTimeFormat', () => { const dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' }); - expect(dateFormat.format(new Date(2015, 6, 3))).toBe('3. Juli 2015'); + expect(dateFormat.format(new Date(2015, 6, 3))).toBe('July 3, 2015'); }); }); }); diff --git a/spec/frontend/notes/components/discussion_counter_spec.js b/spec/frontend/notes/components/discussion_counter_spec.js index c9375df07e8..77603c16f82 100644 --- a/spec/frontend/notes/components/discussion_counter_spec.js +++ b/spec/frontend/notes/components/discussion_counter_spec.js @@ -75,17 +75,66 @@ describe('DiscussionCounter component', () => { }); it.each` - title | resolved | hasNextBtn | isActive | icon | groupLength - ${'hasNextButton'} | ${false} | ${true} | ${false} | ${'check-circle'} | ${2} - ${'allResolved'} | ${true} | ${false} | ${true} | ${'check-circle-filled'} | ${0} - `('renders correctly if $title', ({ resolved, hasNextBtn, isActive, icon, groupLength }) => { + title | resolved | isActive | icon | groupLength + ${'not allResolved'} | ${false} | ${false} | ${'check-circle'} | ${3} + ${'allResolved'} | ${true} | ${true} | ${'check-circle-filled'} | ${1} + `('renders correctly if $title', ({ resolved, isActive, icon, groupLength }) => { updateStore({ resolvable: true, resolved }); wrapper = shallowMount(DiscussionCounter, { store, localVue }); - expect(wrapper.find(`.has-next-btn`).exists()).toBe(hasNextBtn); expect(wrapper.find(`.is-active`).exists()).toBe(isActive); expect(wrapper.find({ name: icon }).exists()).toBe(true); expect(wrapper.findAll('[role="group"').length).toBe(groupLength); }); }); + + describe('toggle all threads button', () => { + let toggleAllButton; + const updateStoreWithExpanded = expanded => { + const discussion = { ...discussionMock, expanded }; + store.commit(types.SET_INITIAL_DISCUSSIONS, [discussion]); + store.dispatch('updateResolvableDiscussionsCounts'); + wrapper = shallowMount(DiscussionCounter, { store, localVue }); + toggleAllButton = wrapper.find('.toggle-all-discussions-btn'); + }; + + afterEach(() => wrapper.destroy()); + + it('calls button handler when clicked', () => { + updateStoreWithExpanded(true); + + wrapper.setMethods({ handleExpandDiscussions: jest.fn() }); + toggleAllButton.trigger('click'); + + expect(wrapper.vm.handleExpandDiscussions).toHaveBeenCalledTimes(1); + }); + + it('collapses all discussions if expanded', () => { + updateStoreWithExpanded(true); + + expect(wrapper.vm.allExpanded).toBe(true); + expect(toggleAllButton.find({ name: 'angle-up' }).exists()).toBe(true); + + toggleAllButton.trigger('click'); + + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.vm.allExpanded).toBe(false); + expect(toggleAllButton.find({ name: 'angle-down' }).exists()).toBe(true); + }); + }); + + it('expands all discussions if collapsed', () => { + updateStoreWithExpanded(false); + + expect(wrapper.vm.allExpanded).toBe(false); + expect(toggleAllButton.find({ name: 'angle-down' }).exists()).toBe(true); + + toggleAllButton.trigger('click'); + + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.vm.allExpanded).toBe(true); + expect(toggleAllButton.find({ name: 'angle-up' }).exists()).toBe(true); + }); + }); + }); }); diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js index ee772afbc03..ea5658821b1 100644 --- a/spec/frontend/notes/stores/mutation_spec.js +++ b/spec/frontend/notes/stores/mutation_spec.js @@ -329,6 +329,52 @@ describe('Notes Store mutations', () => { }); }); + describe('SET_EXPAND_DISCUSSIONS', () => { + it('should succeed when discussions are null', () => { + const state = {}; + + mutations.SET_EXPAND_DISCUSSIONS(state, { discussionIds: null, expanded: true }); + + expect(state).toEqual({}); + }); + + it('should succeed when discussions are empty', () => { + const state = {}; + + mutations.SET_EXPAND_DISCUSSIONS(state, { discussionIds: [], expanded: true }); + + expect(state).toEqual({}); + }); + + it('should open all closed discussions', () => { + const discussion1 = Object.assign({}, discussionMock, { id: 0, expanded: false }); + const discussion2 = Object.assign({}, discussionMock, { id: 1, expanded: true }); + const discussionIds = [discussion1.id, discussion2.id]; + + const state = { discussions: [discussion1, discussion2] }; + + mutations.SET_EXPAND_DISCUSSIONS(state, { discussionIds, expanded: true }); + + state.discussions.forEach(discussion => { + expect(discussion.expanded).toEqual(true); + }); + }); + + it('should close all opened discussions', () => { + const discussion1 = Object.assign({}, discussionMock, { id: 0, expanded: false }); + const discussion2 = Object.assign({}, discussionMock, { id: 1, expanded: true }); + const discussionIds = [discussion1.id, discussion2.id]; + + const state = { discussions: [discussion1, discussion2] }; + + mutations.SET_EXPAND_DISCUSSIONS(state, { discussionIds, expanded: false }); + + state.discussions.forEach(discussion => { + expect(discussion.expanded).toEqual(false); + }); + }); + }); + describe('UPDATE_NOTE', () => { it('should update a note', () => { const state = { diff --git a/spec/frontend/releases/components/evidence_block_spec.js b/spec/frontend/releases/components/evidence_block_spec.js index c76a0e04dce..ba60a79e464 100644 --- a/spec/frontend/releases/components/evidence_block_spec.js +++ b/spec/frontend/releases/components/evidence_block_spec.js @@ -1,7 +1,6 @@ import { mount } from '@vue/test-utils'; -import { GlLink } from '@gitlab/ui'; +import { GlLink, GlIcon } from '@gitlab/ui'; import { truncateSha } from '~/lib/utils/text_utility'; -import Icon from '~/vue_shared/components/icon.vue'; import { release as originalRelease } from '../mock_data'; import EvidenceBlock from '~/releases/components/evidence_block.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; @@ -32,11 +31,11 @@ describe('Evidence Block', () => { }); it('renders the evidence icon', () => { - expect(wrapper.find(Icon).props('name')).toBe('review-list'); + expect(wrapper.find(GlIcon).props('name')).toBe('review-list'); }); it('renders the title for the dowload link', () => { - expect(wrapper.find(GlLink).text()).toBe(`${release.tagName}-evidence.json`); + expect(wrapper.find(GlLink).text()).toBe('v1.1.2-evidences-1.json'); }); it('renders the correct hover text for the download', () => { @@ -44,19 +43,19 @@ describe('Evidence Block', () => { }); it('renders the correct file link for download', () => { - expect(wrapper.find(GlLink).attributes().download).toBe(`${release.tagName}-evidence.json`); + expect(wrapper.find(GlLink).attributes().download).toBe('v1.1.2-evidences-1.json'); }); describe('sha text', () => { it('renders the short sha initially', () => { - expect(wrapper.find('.js-short').text()).toBe(truncateSha(release.evidenceSha)); + expect(wrapper.find('.js-short').text()).toBe(truncateSha(release.evidences[0].sha)); }); it('renders the long sha after expansion', () => { wrapper.find('.js-text-expander-prepend').trigger('click'); return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find('.js-expanded').text()).toBe(release.evidenceSha); + expect(wrapper.find('.js-expanded').text()).toBe(release.evidences[0].sha); }); }); }); @@ -72,7 +71,7 @@ describe('Evidence Block', () => { it('copies the sha', () => { expect(wrapper.find(ClipboardButton).attributes('data-clipboard-text')).toBe( - release.evidenceSha, + release.evidences[0].sha, ); }); }); diff --git a/spec/frontend/releases/mock_data.js b/spec/frontend/releases/mock_data.js index 85e6bab71ba..bd5fc86275e 100644 --- a/spec/frontend/releases/mock_data.js +++ b/spec/frontend/releases/mock_data.js @@ -43,7 +43,6 @@ export const release = { description_html: '<p data-sourcepos="1:1-1:21" dir="auto">A super nice release!</p>', created_at: '2019-08-26T17:54:04.952Z', released_at: '2019-08-26T17:54:04.807Z', - evidence_sha: 'fb3a125fd69a0e5048ebfb0ba43eb32ce4911520dd8d', author: { id: 1, name: 'Administrator', @@ -69,10 +68,28 @@ export const release = { commit_path: '/root/release-test/commit/c22b0728d1b465f82898c884d32b01aa642f96c1', upcoming_release: false, milestones, + evidences: [ + { + filepath: + 'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/1.json', + sha: 'fb3a125fd69a0e5048ebfb0ba43eb32ce4911520dd8d', + collected_at: '2018-10-19 15:43:20 +0200', + }, + { + filepath: + 'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/2.json', + sha: '6ebd17a66e6a861175735416e49cf677678029805712dd71bb805c609e2d9108', + collected_at: '2018-10-19 15:43:20 +0200', + }, + { + filepath: + 'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidences/3.json', + sha: '2f65beaf275c3cb4b4e24fb01d481cc475d69c957830833f15338384816b5cba', + collected_at: '2018-10-19 15:43:20 +0200', + }, + ], assets: { count: 5, - evidence_file_path: - 'https://20592.qa-tunnel.gitlab.info/root/test-deployments/-/releases/v1.1.2/evidence.json', sources: [ { format: 'zip', diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb index 7cfef9b4cc7..4467c228e96 100644 --- a/spec/graphql/resolvers/issues_resolver_spec.rb +++ b/spec/graphql/resolvers/issues_resolver_spec.rb @@ -7,15 +7,20 @@ describe Resolvers::IssuesResolver do let(:current_user) { create(:user) } - context "with a project" do - let_it_be(:project) { create(:project) } - let_it_be(:milestone) { create(:milestone, project: project) } - let_it_be(:assignee) { create(:user) } - let_it_be(:issue1) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago, milestone: milestone) } - let_it_be(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago, assignees: [assignee]) } - let_it_be(:label1) { create(:label, project: project) } - let_it_be(:label2) { create(:label, project: project) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:other_project) { create(:project, group: group) } + + let_it_be(:milestone) { create(:milestone, project: project) } + let_it_be(:assignee) { create(:user) } + let_it_be(:issue1) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago, milestone: milestone) } + let_it_be(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago, assignees: [assignee]) } + let_it_be(:issue3) { create(:issue, project: other_project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago, assignees: [assignee]) } + let_it_be(:issue4) { create(:issue) } + let_it_be(:label1) { create(:label, project: project) } + let_it_be(:label2) { create(:label, project: project) } + context "with a project" do before do project.add_developer(current_user) create(:label_link, label: label1, target: issue1) @@ -184,6 +189,20 @@ describe Resolvers::IssuesResolver do end end + context "with a group" do + before do + group.add_developer(current_user) + end + + describe '#resolve' do + it 'finds all group issues' do + result = resolve(described_class, obj: group, ctx: { current_user: current_user }) + + expect(result).to contain_exactly(issue1, issue2, issue3) + end + end + end + context "when passing a non existent, batch loaded project" do let(:project) do BatchLoader::GraphQL.for("non-existent-path").batch do |_fake_paths, loader, _| diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb index f92dca11136..6bdbec1203c 100644 --- a/spec/helpers/nav_helper_spec.rb +++ b/spec/helpers/nav_helper_spec.rb @@ -117,4 +117,24 @@ describe NavHelper, :do_not_mock_admin_mode do it { is_expected.to all(be_a(String)) } end + + describe '.show_user_notification_dot?' do + subject { helper.show_user_notification_dot? } + + context 'when experiment is disabled' do + before do + allow(helper).to receive(:experiment_enabled?).with(:ci_notification_dot).and_return(false) + end + + it { is_expected.to be_falsey } + end + + context 'when experiment is enabled' do + before do + allow(helper).to receive(:experiment_enabled?).with(:ci_notification_dot).and_return(true) + end + + it { is_expected.to be_truthy } + end + end end diff --git a/spec/lib/api/entities/release_spec.rb b/spec/lib/api/entities/release_spec.rb index f0bbaa35efe..c45dbc15856 100644 --- a/spec/lib/api/entities/release_spec.rb +++ b/spec/lib/api/entities/release_spec.rb @@ -4,26 +4,29 @@ require 'spec_helper' describe API::Entities::Release do let_it_be(:project) { create(:project) } - let_it_be(:user) { create(:user) } - let(:entity) { described_class.new(release, current_user: user) } - - describe 'evidence' do - let(:release) { create(:release, :with_evidence, project: project) } - - subject { entity.as_json } + let_it_be(:release) { create(:release, :with_evidence, project: project) } + let(:evidence) { release.evidences.first } + let(:user) { create(:user) } + let(:entity) { described_class.new(release, current_user: user).as_json } + describe 'evidences' do context 'when the current user can download code' do + let(:entity_evidence) { entity[:evidences].first } + it 'exposes the evidence sha and the json path' do allow(Ability).to receive(:allowed?).and_call_original allow(Ability).to receive(:allowed?) .with(user, :download_code, project).and_return(true) - expect(subject[:evidence_sha]).to eq(release.evidence_sha) - expect(subject[:assets][:evidence_file_path]).to eq( - Gitlab::Routing.url_helpers.evidence_project_release_url(project, - release.tag, - format: :json) - ) + expect(entity_evidence[:sha]).to eq(evidence.summary_sha) + expect(entity_evidence[:collected_at]).to eq(evidence.collected_at) + expect(entity_evidence[:filepath]).to eq( + Gitlab::Routing.url_helpers.namespace_project_evidence_url( + namespace_id: project.namespace, + project_id: project, + tag: release, + id: evidence.id, + format: :json)) end end @@ -33,8 +36,7 @@ describe API::Entities::Release do allow(Ability).to receive(:allowed?) .with(user, :download_code, project).and_return(false) - expect(subject.keys).not_to include(:evidence_sha) - expect(subject[:assets].keys).not_to include(:evidence_file_path) + expect(entity.keys).not_to include(:evidences) end end end @@ -45,7 +47,7 @@ describe API::Entities::Release do let(:issue_title) { 'title="%s"' % issue.title } let(:release) { create(:release, project: project, description: "Now shipping #{issue.to_reference}") } - subject(:description_html) { entity.as_json[:description_html] } + subject(:description_html) { entity.as_json['description_html'] } it 'renders special references if current user has access' do project.add_reporter(user) diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb index e35698f6030..da6e1f9458f 100644 --- a/spec/lib/event_filter_spec.rb +++ b/spec/lib/event_filter_spec.rb @@ -28,6 +28,8 @@ describe EventFilter do let_it_be(:comments_event) { create(:event, :commented, project: public_project, target: public_project) } let_it_be(:joined_event) { create(:event, :joined, project: public_project, target: public_project) } let_it_be(:left_event) { create(:event, :left, project: public_project, target: public_project) } + let_it_be(:wiki_page_event) { create(:wiki_page_event) } + let_it_be(:wiki_page_update_event) { create(:wiki_page_event, :updated) } let(:filtered_events) { described_class.new(filter).apply_filter(Event.all) } @@ -77,6 +79,34 @@ describe EventFilter do it 'returns all events' do expect(filtered_events).to eq(Event.all) end + + context 'the :wiki_events filter is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not return wiki events' do + expect(filtered_events).to eq(Event.not_wiki_page) + end + end + end + + context 'with the "wiki" filter' do + let(:filter) { described_class::WIKI } + + it 'returns only wiki page events' do + expect(filtered_events).to contain_exactly(wiki_page_event, wiki_page_update_event) + end + + context 'the :wiki_events filter is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not return wiki events' do + expect(filtered_events).not_to include(wiki_page_event, wiki_page_update_event) + end + end end context 'with an unknown filter' do @@ -85,6 +115,16 @@ describe EventFilter do it 'returns all events' do expect(filtered_events).to eq(Event.all) end + + context 'the :wiki_events filter is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not return wiki events' do + expect(filtered_events).to eq(Event.not_wiki_page) + end + end end context 'with a nil filter' do @@ -93,6 +133,16 @@ describe EventFilter do it 'returns all events' do expect(filtered_events).to eq(Event.all) end + + context 'the :wiki_events filter is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not return wiki events' do + expect(filtered_events).to eq(Event.not_wiki_page) + end + end end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index b3f279344b1..0ed5a1e7b49 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -94,7 +94,7 @@ releases: - links - milestone_releases - milestones -- evidence +- evidences links: - release project_members: diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 5b0444c394e..533458afd73 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -134,7 +134,7 @@ Release: - created_at - updated_at - released_at -Evidence: +Releases::Evidence: - id - summary - created_at diff --git a/spec/models/event_collection_spec.rb b/spec/models/event_collection_spec.rb index e6f80a4c4d0..6d1954700bf 100644 --- a/spec/models/event_collection_spec.rb +++ b/spec/models/event_collection_spec.rb @@ -8,22 +8,68 @@ describe EventCollection do let_it_be(:project) { create(:project_empty_repo, group: group) } let_it_be(:projects) { Project.where(id: project.id) } let_it_be(:user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request) } context 'with project events' do - before do - 20.times do - event = create(:push_event, project: project, author: user) - - create(:push_event_payload, event: event) + let_it_be(:push_event_payloads) do + Array.new(9) do + create(:push_event_payload, + event: create(:push_event, project: project, author: user)) end - - create(:closed_issue_event, project: project, author: user) end - it 'returns an Array of events' do + let_it_be(:merge_request_events) { create_list(:event, 10, :commented, project: project, target: merge_request) } + let_it_be(:closed_issue_event) { create(:closed_issue_event, project: project, author: user) } + let_it_be(:wiki_page_event) { create(:wiki_page_event, project: project) } + let(:push_events) { push_event_payloads.map(&:event) } + + it 'returns an Array of events', :aggregate_failures do + most_recent_20_events = [ + wiki_page_event, + closed_issue_event, + *push_events, + *merge_request_events + ].sort_by(&:id).reverse.take(20) events = described_class.new(projects).to_a expect(events).to be_an_instance_of(Array) + expect(events).to match_array(most_recent_20_events) + end + + context 'the wiki_events feature flag is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'omits the wiki page events when using to_a' do + events = described_class.new(projects).to_a + + expect(events).not_to include(wiki_page_event) + end + + it 'omits the wiki page events when using all_project_events' do + events = described_class.new(projects).all_project_events + + expect(events).not_to include(wiki_page_event) + end + end + + context 'the wiki_events feature flag is enabled' do + before do + stub_feature_flags(wiki_events: true) + end + + it 'includes the wiki page events when using to_a' do + events = described_class.new(projects).to_a + + expect(events).to include(wiki_page_event) + end + + it 'includes the wiki page events when using all_project_events' do + events = described_class.new(projects).all_project_events + + expect(events).to include(wiki_page_event) + end end it 'applies a limit to the number of events' do @@ -44,12 +90,25 @@ describe EventCollection do expect(events).to be_empty end - it 'allows filtering of events using an EventFilter' do + it 'allows filtering of events using an EventFilter, returning single item' do filter = EventFilter.new(EventFilter::ISSUE) events = described_class.new(projects, filter: filter).to_a - expect(events.length).to eq(1) - expect(events[0].action).to eq(Event::CLOSED) + expect(events).to contain_exactly(closed_issue_event) + end + + it 'allows filtering of events using an EventFilter, returning several items' do + filter = EventFilter.new(EventFilter::COMMENTS) + events = described_class.new(projects, filter: filter).to_a + + expect(events).to match_array(merge_request_events) + end + + it 'allows filtering of events using an EventFilter, returning pushes' do + filter = EventFilter.new(EventFilter::PUSH) + events = described_class.new(projects, filter: filter).to_a + + expect(events).to match_array(push_events) end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index b2676a79b55..3239c7a843a 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -454,9 +454,10 @@ describe Event do end end - describe '.for_wiki_page' do + describe 'wiki_page predicate scopes' do let_it_be(:events) do [ + create(:push_event), create(:closed_issue_event), create(:wiki_page_event), create(:closed_issue_event), @@ -465,10 +466,22 @@ describe Event do ] end - it 'only contains the wiki page events' do - wiki_events = events.select(&:wiki_page?) + describe '.for_wiki_page' do + it 'only contains the wiki page events' do + wiki_events = events.select(&:wiki_page?) - expect(described_class.for_wiki_page).to match_array(wiki_events) + expect(events).not_to match_array(wiki_events) + expect(described_class.for_wiki_page).to match_array(wiki_events) + end + end + + describe '.not_wiki_page' do + it 'does not contain the wiki page events' do + non_wiki_events = events.reject(&:wiki_page?) + + expect(events).not_to match_array(non_wiki_events) + expect(described_class.not_wiki_page).to match_array(non_wiki_events) + end end end diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 3884b8138be..8b1b738ab58 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Release do it { is_expected.to have_many(:links).class_name('Releases::Link') } it { is_expected.to have_many(:milestones) } it { is_expected.to have_many(:milestone_releases) } - it { is_expected.to have_one(:evidence) } + it { is_expected.to have_many(:evidences).class_name('Releases::Evidence') } end describe 'validation' do @@ -97,7 +97,7 @@ RSpec.describe Release do describe '#create_evidence!' do context 'when a release is created' do it 'creates one Evidence object too' do - expect { release_with_evidence }.to change(Evidence, :count).by(1) + expect { release_with_evidence }.to change(Releases::Evidence, :count).by(1) end end end @@ -106,7 +106,7 @@ RSpec.describe Release do it 'also deletes the associated evidence' do release_with_evidence - expect { release_with_evidence.destroy }.to change(Evidence, :count).by(-1) + expect { release_with_evidence.destroy }.to change(Releases::Evidence, :count).by(-1) end end end @@ -155,7 +155,7 @@ RSpec.describe Release do context 'when a release was created with evidence collection' do let!(:release) { create(:release, :with_evidence) } - it { is_expected.to eq(release.evidence.summary_sha) } + it { is_expected.to eq(release.evidences.first.summary_sha) } end end @@ -171,7 +171,7 @@ RSpec.describe Release do context 'when a release was created with evidence collection' do let!(:release) { create(:release, :with_evidence) } - it { is_expected.to eq(release.evidence.summary) } + it { is_expected.to eq(release.evidences.first.summary) } end end diff --git a/spec/models/evidence_spec.rb b/spec/models/releases/evidence_spec.rb index 8f534517fc1..d38d2021117 100644 --- a/spec/models/evidence_spec.rb +++ b/spec/models/releases/evidence_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Evidence do +describe Releases::Evidence do let_it_be(:project) { create(:project) } let(:release) { create(:release, project: project) } let(:schema_file) { 'evidences/evidence' } diff --git a/spec/presenters/release_presenter_spec.rb b/spec/presenters/release_presenter_spec.rb index 82f312622ff..d1f023b8760 100644 --- a/spec/presenters/release_presenter_spec.rb +++ b/spec/presenters/release_presenter_spec.rb @@ -112,28 +112,4 @@ describe ReleasePresenter do it { is_expected.to be_nil } end end - - describe '#evidence_file_path' do - subject { presenter.evidence_file_path } - - context 'without evidence' do - it { is_expected.to be_falsy } - end - - context 'with evidence' do - let(:release) { create :release, :with_evidence, project: project } - - specify do - is_expected.to match /#{evidence_project_release_url(project, release.tag, format: :json)}/ - end - end - - context 'when a tag contains a slash' do - let(:release) { create :release, :with_evidence, project: project, tag: 'debian/2.4.0-1' } - - specify do - is_expected.to match /#{evidence_project_release_url(project, CGI.escape(release.tag), format: :json)}/ - end - end - end end diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index acf3bb3482a..decdcc66327 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -114,6 +114,26 @@ describe API::Events do expect(json_response.size).to eq(1) end + context 'when the list of events includes wiki page events' do + it 'returns information about the wiki event', :aggregate_failures do + page = create(:wiki_page, project: private_project) + [Event::CREATED, Event::UPDATED, Event::DESTROYED].each do |action| + create(:wiki_page_event, wiki_page: page, action: action, author: user) + end + + get api("/users/#{user.id}/events", user) + + wiki_events = json_response.select { |e| e['target_type'] == 'WikiPage::Meta' } + action_names = wiki_events.map { |e| e['action_name'] } + titles = wiki_events.map { |e| e['target_title'] } + slugs = wiki_events.map { |e| e.dig('wiki_page', 'slug') } + + expect(action_names).to contain_exactly('created', 'updated', 'destroyed') + expect(titles).to all(eq(page.title)) + expect(slugs).to all(eq(page.slug)) + end + end + context 'when the list of events includes push events' do let(:event) do create(:push_event, author: user, project: private_project) diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb index a38d1857076..c7b537a9923 100644 --- a/spec/requests/api/graphql/group_query_spec.rb +++ b/spec/requests/api/graphql/group_query_spec.rb @@ -51,6 +51,7 @@ describe 'getting group information', :do_not_mock_admin_mode do it "returns one of user1's groups" do project = create(:project, namespace: group2, path: 'Foo') + issue = create(:issue, project: create(:project, group: group1)) create(:project_group_link, project: project, group: group1) post_graphql(group_query(group1), current_user: user1) @@ -67,6 +68,8 @@ describe 'getting group information', :do_not_mock_admin_mode do expect(graphql_data['group']['fullName']).to eq(group1.full_name) expect(graphql_data['group']['fullPath']).to eq(group1.full_path) expect(graphql_data['group']['parentId']).to eq(group1.parent_id) + expect(graphql_data['group']['issues']['nodes'].count).to eq(1) + expect(graphql_data['group']['issues']['nodes'][0]['iid']).to eq(issue.iid.to_s) end it "does not return a non existing group" do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index cec4995c620..ea60f783b48 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -71,6 +71,7 @@ describe API::Groups do expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.length).to eq(1) + expect(json_response.first['created_at']).to be_present expect(json_response) .to satisfy_one { |group| group['name'] == group1.name } end @@ -121,6 +122,15 @@ describe API::Groups do expect(json_response).to be_an Array expect(json_response.first).not_to include 'statistics' end + + it "includes a created_at timestamp" do + get api("/groups", user1) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['created_at']).to be_present + end end context "when authenticated as admin" do @@ -152,6 +162,15 @@ describe API::Groups do expect(json_response.first).not_to include('statistics') end + it "includes a created_at timestamp" do + get api("/groups", admin) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['created_at']).to be_present + end + it "includes statistics if requested" do attributes = { storage_size: 1158, @@ -357,6 +376,7 @@ describe API::Groups do expect(response).to have_gitlab_http_status(:ok) expect(json_response).not_to include('runners_token') + expect(json_response).to include('created_at') end it 'returns only public projects in the group' do @@ -407,6 +427,7 @@ describe API::Groups do expect(json_response['full_name']).to eq(group1.full_name) expect(json_response['full_path']).to eq(group1.full_path) expect(json_response['parent_id']).to eq(group1.parent_id) + expect(json_response['created_at']).to be_present expect(json_response['projects']).to be_an Array expect(json_response['projects'].length).to eq(2) expect(json_response['shared_projects']).to be_an Array @@ -613,6 +634,7 @@ describe API::Groups do expect(json_response['subgroup_creation_level']).to eq("maintainer") expect(json_response['request_access_enabled']).to eq(true) expect(json_response['parent_id']).to eq(nil) + expect(json_response['created_at']).to be_present expect(json_response['projects']).to be_an Array expect(json_response['projects'].length).to eq(2) expect(json_response['shared_projects']).to be_an Array diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index 41999ca6e60..e66e999dc27 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -104,6 +104,21 @@ describe API::Releases do expect(json_response.first['upcoming_release']).to eq(false) end + it 'avoids N+1 queries' do + create(:release, :with_evidence, project: project, tag: 'v0.1', author: maintainer) + + control_count = ActiveRecord::QueryRecorder.new do + get api("/projects/#{project.id}/releases", maintainer) + end.count + + create(:release, :with_evidence, project: project, tag: 'v0.1', author: maintainer) + create(:release, :with_evidence, project: project, tag: 'v0.1', author: maintainer) + + expect do + get api("/projects/#{project.id}/releases", maintainer) + end.not_to exceed_query_limit(control_count) + end + context 'when tag does not exist in git repository' do let!(:release) { create(:release, project: project, tag: 'v1.1.5') } @@ -725,7 +740,7 @@ describe API::Releases do end it 'does not create an Evidence object', :sidekiq_inline do - expect { subject }.not_to change(Evidence, :count) + expect { subject }.not_to change(Releases::Evidence, :count) end it 'is a historical release' do @@ -755,7 +770,7 @@ describe API::Releases do end it 'creates Evidence', :sidekiq_inline do - expect { subject }.to change(Evidence, :count).by(1) + expect { subject }.to change(Releases::Evidence, :count).by(1) end it 'is not a historical release' do @@ -785,7 +800,7 @@ describe API::Releases do end it 'creates Evidence', :sidekiq_inline do - expect { subject }.to change(Evidence, :count).by(1) + expect { subject }.to change(Releases::Evidence, :count).by(1) end it 'is not a historical release' do diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index a8ddca0cdf3..0a8a4d5bf58 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -153,6 +153,46 @@ describe EventCreateService do end end + describe '#wiki_event' do + let_it_be(:user) { create(:user) } + let_it_be(:wiki_page) { create(:wiki_page) } + let_it_be(:meta) { create(:wiki_page_meta, :for_wiki_page, wiki_page: wiki_page) } + + Event::WIKI_ACTIONS.each do |action| + context "The action is #{action}" do + let(:event) { service.wiki_event(meta, user, action) } + + it 'creates the event' do + expect(event).to have_attributes( + wiki_page?: true, + valid?: true, + persisted?: true, + action: action, + wiki_page: wiki_page + ) + end + + context 'the feature is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not create the event' do + expect { event }.not_to change(Event, :count) + end + end + end + end + + (Event::ACTIONS.values - Event::WIKI_ACTIONS).each do |bad_action| + context "The action is #{bad_action}" do + it 'raises an error' do + expect { service.wiki_event(meta, user, bad_action) }.to raise_error(described_class::IllegalActionError) + end + end + end + end + describe '#push', :clean_gitlab_redis_shared_state do let(:project) { create(:project) } let(:user) { create(:user) } diff --git a/spec/services/wiki_pages/base_service_spec.rb b/spec/services/wiki_pages/base_service_spec.rb index 2e70246c6f2..4c44c195ac8 100644 --- a/spec/services/wiki_pages/base_service_spec.rb +++ b/spec/services/wiki_pages/base_service_spec.rb @@ -6,22 +6,24 @@ describe WikiPages::BaseService do let(:project) { double('project') } let(:user) { double('user') } - subject(:service) { described_class.new(project, user, {}) } - describe '#increment_usage' do counter = Gitlab::UsageDataCounters::WikiPageCounter error = counter::UnknownEvent - it 'raises an error on unknown events' do - expect { subject.send(:increment_usage, :bad_event) }.to raise_error error - end + let(:subject) { bad_service_class.new(project, user, {}) } - context 'the event is valid' do - counter::KNOWN_EVENTS.each do |e| - it "updates the #{e} counter" do - expect { subject.send(:increment_usage, e) }.to change { counter.read(e) } + context 'the class implements usage_counter_action incorrectly' do + let(:bad_service_class) do + Class.new(described_class) do + def usage_counter_action + :bad_event + end end end + + it 'raises an error on unknown events' do + expect { subject.send(:increment_usage) }.to raise_error(error) + end end end end diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb index ef03a2e9788..d63d62e9492 100644 --- a/spec/services/wiki_pages/create_service_spec.rb +++ b/spec/services/wiki_pages/create_service_spec.rb @@ -5,19 +5,16 @@ require 'spec_helper' describe WikiPages::CreateService do let(:project) { create(:project, :wiki_repo) } let(:user) { create(:user) } + let(:page_title) { 'Title' } let(:opts) do { - title: 'Title', + title: page_title, content: 'Content for wiki page', format: 'markdown' } end - let(:bad_opts) do - { title: '' } - end - subject(:service) { described_class.new(project, user, opts) } before do @@ -35,8 +32,7 @@ describe WikiPages::CreateService do end it 'executes webhooks' do - expect(service).to receive(:execute_hooks).once - .with(instance_of(WikiPage), 'create') + expect(service).to receive(:execute_hooks).once.with(WikiPage) service.execute end @@ -47,8 +43,41 @@ describe WikiPages::CreateService do expect { service.execute }.to change { counter.read(:create) }.by 1 end + shared_examples 'correct event created' do + it 'creates appropriate events' do + expect { service.execute }.to change { Event.count }.by 1 + + expect(Event.recent.first).to have_attributes( + action: Event::CREATED, + target: have_attributes(canonical_slug: page_title) + ) + end + end + + context 'the new page is at the top level' do + let(:page_title) { 'root-level-page' } + + include_examples 'correct event created' + end + + context 'the new page is in a subsection' do + let(:page_title) { 'subsection/page' } + + include_examples 'correct event created' + end + + context 'the feature is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not record the activity' do + expect { service.execute }.not_to change(Event, :count) + end + end + context 'when the options are bad' do - subject(:service) { described_class.new(project, user, bad_opts) } + let(:page_title) { '' } it 'does not count a creation event' do counter = Gitlab::UsageDataCounters::WikiPageCounter @@ -56,6 +85,10 @@ describe WikiPages::CreateService do expect { service.execute }.not_to change { counter.read(:create) } end + it 'does not record the activity' do + expect { service.execute }.not_to change(Event, :count) + end + it 'reports the error' do expect(service.execute).to be_invalid .and have_attributes(errors: be_present) diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb index 350a7eb123b..e205bedfdb9 100644 --- a/spec/services/wiki_pages/destroy_service_spec.rb +++ b/spec/services/wiki_pages/destroy_service_spec.rb @@ -15,8 +15,7 @@ describe WikiPages::DestroyService do describe '#execute' do it 'executes webhooks' do - expect(service).to receive(:execute_hooks).once - .with(instance_of(WikiPage), 'delete') + expect(service).to receive(:execute_hooks).once.with(page) service.execute(page) end @@ -27,10 +26,29 @@ describe WikiPages::DestroyService do expect { service.execute(page) }.to change { counter.read(:delete) }.by 1 end + it 'creates a new wiki page deletion event' do + expect { service.execute(page) }.to change { Event.count }.by 1 + + expect(Event.recent.first).to have_attributes( + action: Event::DESTROYED, + target: have_attributes(canonical_slug: page.slug) + ) + end + it 'does not increment the delete count if the deletion failed' do counter = Gitlab::UsageDataCounters::WikiPageCounter expect { service.execute(nil) }.not_to change { counter.read(:delete) } end end + + context 'the feature is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not record the activity' do + expect { service.execute(page) }.not_to change(Event, :count) + end + end end diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb index d5f46e7b2db..3eb486833e6 100644 --- a/spec/services/wiki_pages/update_service_spec.rb +++ b/spec/services/wiki_pages/update_service_spec.rb @@ -6,20 +6,17 @@ describe WikiPages::UpdateService do let(:project) { create(:project) } let(:user) { create(:user) } let(:page) { create(:wiki_page) } + let(:page_title) { 'New Title' } let(:opts) do { content: 'New content for wiki page', format: 'markdown', message: 'New wiki message', - title: 'New Title' + title: page_title } end - let(:bad_opts) do - { title: '' } - end - subject(:service) { described_class.new(project, user, opts) } before do @@ -34,12 +31,11 @@ describe WikiPages::UpdateService do expect(updated_page.message).to eq(opts[:message]) expect(updated_page.content).to eq(opts[:content]) expect(updated_page.format).to eq(opts[:format].to_sym) - expect(updated_page.title).to eq(opts[:title]) + expect(updated_page.title).to eq(page_title) end it 'executes webhooks' do - expect(service).to receive(:execute_hooks).once - .with(instance_of(WikiPage), 'update') + expect(service).to receive(:execute_hooks).once.with(WikiPage) service.execute(page) end @@ -50,8 +46,42 @@ describe WikiPages::UpdateService do expect { service.execute page }.to change { counter.read(:update) }.by 1 end + shared_examples 'adds activity event' do + it 'adds a new wiki page activity event' do + expect { service.execute(page) }.to change { Event.count }.by 1 + + expect(Event.recent.first).to have_attributes( + action: Event::UPDATED, + wiki_page: page, + target_title: page.title + ) + end + end + + context 'the page is at the top level' do + let(:page_title) { 'Top level page' } + + include_examples 'adds activity event' + end + + context 'the page is in a subsection' do + let(:page_title) { 'Subsection / secondary page' } + + include_examples 'adds activity event' + end + + context 'the feature is disabled' do + before do + stub_feature_flags(wiki_events: false) + end + + it 'does not record the activity' do + expect { service.execute(page) }.not_to change(Event, :count) + end + end + context 'when the options are bad' do - subject(:service) { described_class.new(project, user, bad_opts) } + let(:page_title) { '' } it 'does not count an edit event' do counter = Gitlab::UsageDataCounters::WikiPageCounter @@ -59,6 +89,10 @@ describe WikiPages::UpdateService do expect { service.execute page }.not_to change { counter.read(:update) } end + it 'does not record the activity' do + expect { service.execute page }.not_to change(Event, :count) + end + it 'reports the error' do expect(service.execute page).to be_invalid .and have_attributes(errors: be_present) diff --git a/spec/support/helpers/stub_experiments.rb b/spec/support/helpers/stub_experiments.rb index 7a5a188ab4d..ff3b02dc3f6 100644 --- a/spec/support/helpers/stub_experiments.rb +++ b/spec/support/helpers/stub_experiments.rb @@ -8,6 +8,8 @@ module StubExperiments # Examples # - `stub_experiment(signup_flow: false)` ... Disable `signup_flow` experiment globally. def stub_experiment(experiments) + allow(Gitlab::Experimentation).to receive(:enabled?).and_call_original + experiments.each do |experiment_key, enabled| allow(Gitlab::Experimentation).to receive(:enabled?).with(experiment_key) { enabled } end @@ -20,6 +22,8 @@ module StubExperiments # Examples # - `stub_experiment_for_user(signup_flow: false)` ... Disable `signup_flow` experiment for user. def stub_experiment_for_user(experiments) + allow(Gitlab::Experimentation).to receive(:enabled_for_user?).and_call_original + experiments.each do |experiment_key, enabled| allow(Gitlab::Experimentation).to receive(:enabled_for_user?).with(experiment_key, anything) { enabled } end diff --git a/spec/workers/create_evidence_worker_spec.rb b/spec/workers/create_evidence_worker_spec.rb index 364b2098251..9b8314122cd 100644 --- a/spec/workers/create_evidence_worker_spec.rb +++ b/spec/workers/create_evidence_worker_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' describe CreateEvidenceWorker do let!(:release) { create(:release) } - it 'creates a new Evidence' do - expect { described_class.new.perform(release.id) }.to change(Evidence, :count).by(1) + it 'creates a new Evidence record' do + expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1) end end |