diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-04 15:10:36 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-04 15:10:36 +0000 |
commit | be4b3134a282f7a8812306777abd2d3150deecdc (patch) | |
tree | 0563327ce590b415047686c6feff43496742b49a /spec | |
parent | 998adcc422d4161515bf2960ef4dce71258f69a3 (diff) | |
download | gitlab-ce-be4b3134a282f7a8812306777abd2d3150deecdc.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
35 files changed, 350 insertions, 129 deletions
diff --git a/spec/controllers/concerns/graceful_timeout_handling_spec.rb b/spec/controllers/concerns/graceful_timeout_handling_spec.rb index cece36f06b2..e496d12856b 100644 --- a/spec/controllers/concerns/graceful_timeout_handling_spec.rb +++ b/spec/controllers/concerns/graceful_timeout_handling_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GracefulTimeoutHandling, type: :controller do skip_before_action :authenticate_user! def index - raise ActiveRecord::QueryCanceled.new + raise ActiveRecord::QueryCanceled end end diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 081927ea73c..776ed9774b1 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -201,7 +201,7 @@ RSpec.describe Projects::LabelsController do context 'service raising InvalidRecord' do before do expect_any_instance_of(Labels::PromoteService).to receive(:execute) do |label| - raise ActiveRecord::RecordInvalid.new(label_1) + raise ActiveRecord::RecordInvalid, label_1 end end diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb index 1b1152897fc..d9e3bfd6a9c 100644 --- a/spec/features/merge_request/user_resolves_conflicts_spec.rb +++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb @@ -173,7 +173,7 @@ RSpec.describe 'Merge request > User resolves conflicts', :js do end it "renders bad name without xss issues" do - expect(find('.resolve-conflicts-form .resolve-info')).to have_content(bad_branch_name) + expect(find('[data-testid="resolve-info"]')).to have_content(bad_branch_name) end end end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index acfb7c2602a..64250931006 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -160,18 +160,67 @@ RSpec.describe 'Runners' do end end - context 'when application settings have shared_runners_text' do - let(:shared_runners_text) { 'custom **shared** runners description' } - let(:shared_runners_html) { 'custom shared runners description' } + context 'shared runner text' do + context 'when application settings have no shared_runners_text' do + it 'user sees default shared runners description' do + visit project_runners_path(project) - before do - stub_application_setting(shared_runners_text: shared_runners_text) + page.within("[data-testid='shared-runners-description']") do + expect(page).to have_content('The same shared runner executes code from multiple projects') + end + end end - it 'user sees shared runners description' do - visit project_runners_path(project) + context 'when application settings have shared_runners_text' do + let(:shared_runners_text) { 'custom **shared** runners description' } + let(:shared_runners_html) { 'custom shared runners description' } + + before do + stub_application_setting(shared_runners_text: shared_runners_text) + end + + it 'user sees shared runners description' do + visit project_runners_path(project) + + page.within("[data-testid='shared-runners-description']") do + expect(page).not_to have_content('The same shared runner executes code from multiple projects') + expect(page).to have_content(shared_runners_html) + end + end + end + + context 'when application settings have an unsafe link in shared_runners_text' do + let(:shared_runners_text) { '<a href="javascript:alert(\'xss\')">link</a>' } + + before do + stub_application_setting(shared_runners_text: shared_runners_text) + end + + it 'user sees no link' do + visit project_runners_path(project) + + page.within("[data-testid='shared-runners-description']") do + expect(page).to have_content('link') + expect(page).not_to have_link('link') + end + end + end - expect(page.find('.shared-runners-description')).to have_content(shared_runners_html) + context 'when application settings have an unsafe image in shared_runners_text' do + let(:shared_runners_text) { '<img src="404.png" onerror="alert(\'xss\')"/>' } + + before do + stub_application_setting(shared_runners_text: shared_runners_text) + end + + it 'user sees image safely' do + visit project_runners_path(project) + + page.within("[data-testid='shared-runners-description']") do + expect(page).to have_css('img') + expect(page).not_to have_css('img[onerror]') + end + end end end end @@ -190,7 +239,7 @@ RSpec.describe 'Runners' do click_on 'Enable shared runners' - expect(page.find('.shared-runners-description')).to have_content('Disable shared runners') + expect(page.find("[data-testid='shared-runners-description']")).to have_content('Disable shared runners') expect(page).not_to have_selector('#toggle-shared-runners-form') end end diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js index 7281d2fde1d..e324f071966 100644 --- a/spec/frontend/issuable_list/components/issuable_item_spec.js +++ b/spec/frontend/issuable_list/components/issuable_item_spec.js @@ -453,5 +453,31 @@ describe('IssuableItem', () => { expect(updatedAtEl.find('span').attributes('title')).toBe('Sep 10, 2020 11:41am GMT+0000'); expect(updatedAtEl.text()).toBe(wrapper.vm.updatedAt); }); + + describe('when issuable is closed', () => { + it('renders issuable card with a closed style', () => { + wrapper = createComponent({ issuable: { ...mockIssuable, closedAt: '2020-12-10' } }); + + expect(wrapper.classes()).toContain('closed'); + }); + }); + + describe('when issuable was created within the past 24 hours', () => { + it('renders issuable card with a recently-created style', () => { + wrapper = createComponent({ + issuable: { ...mockIssuable, createdAt: '2020-12-10T12:34:56' }, + }); + + expect(wrapper.classes()).toContain('today'); + }); + }); + + describe('when issuable was created earlier than the past 24 hours', () => { + it('renders issuable card without a recently-created style', () => { + wrapper = createComponent({ issuable: { ...mockIssuable, createdAt: '2020-12-09' } }); + + expect(wrapper.classes()).not.toContain('today'); + }); + }); }); }); diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index 9a4c2edc01a..c0b3ad0f21f 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -15,10 +15,10 @@ import { PAGE_SIZE, PAGE_SIZE_MANUAL, RELATIVE_POSITION_ASC, - sortOptions, sortParams, } from '~/issues_list/constants'; import eventHub from '~/issues_list/eventhub'; +import { getSortOptions } from '~/issues_list/utils'; import axios from '~/lib/utils/axios_utils'; import { setUrlParams } from '~/lib/utils/url_utility'; @@ -35,7 +35,9 @@ describe('IssuesListApp component', () => { emptyStateSvgPath: 'empty-state.svg', endpoint: 'api/endpoint', exportCsvPath: 'export/csv/path', + hasBlockedIssuesFeature: true, hasIssues: true, + hasIssueWeightsFeature: true, isSignedIn: false, issuesPath: 'path/to/issues', jiraIntegrationPath: 'jira/integration/path', @@ -43,7 +45,6 @@ describe('IssuesListApp component', () => { projectLabelsPath: 'project/labels/path', projectPath: 'path/to/project', rssPath: 'rss/path', - showImportButton: true, showNewIssueLink: true, signInPath: 'sign/in/path', }; @@ -105,7 +106,7 @@ describe('IssuesListApp component', () => { namespace: defaultProvide.projectPath, recentSearchesStorageKey: 'issues', searchInputPlaceholder: 'Search or filter results…', - sortOptions, + sortOptions: getSortOptions(true, true), initialSortBy: CREATED_DESC, tabs: IssuableListTabs, currentTab: IssuableStates.Opened, @@ -142,18 +143,33 @@ describe('IssuesListApp component', () => { }); }); - it('renders csv import/export component', async () => { - const search = '?page=1&search=refactor&state=opened&order_by=created_at&sort=desc'; + describe('csv import/export component', () => { + describe('when user is signed in', () => { + it('renders', async () => { + const search = '?page=1&search=refactor&state=opened&order_by=created_at&sort=desc'; - global.jsdom.reconfigure({ url: `${TEST_HOST}${search}` }); + global.jsdom.reconfigure({ url: `${TEST_HOST}${search}` }); - wrapper = mountComponent({ mountFn: mount }); + wrapper = mountComponent({ + provide: { ...defaultProvide, isSignedIn: true }, + mountFn: mount, + }); - await waitForPromises(); + await waitForPromises(); - expect(findCsvImportExportButtons().props()).toMatchObject({ - exportCsvPath: `${defaultProvide.exportCsvPath}${search}`, - issuableCount: xTotal, + expect(findCsvImportExportButtons().props()).toMatchObject({ + exportCsvPath: `${defaultProvide.exportCsvPath}${search}`, + issuableCount: xTotal, + }); + }); + }); + + describe('when user is not signed in', () => { + it('does not render', () => { + wrapper = mountComponent({ provide: { ...defaultProvide, isSignedIn: false } }); + + expect(findCsvImportExportButtons().exists()).toBe(false); + }); }); }); @@ -369,11 +385,11 @@ describe('IssuesListApp component', () => { it('shows Jira integration information', () => { const paragraphs = wrapper.findAll('p'); - expect(paragraphs.at(2).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); - expect(paragraphs.at(3).text()).toContain( + expect(paragraphs.at(1).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); + expect(paragraphs.at(2).text()).toContain( 'Enable the Jira integration to view your Jira issues in GitLab.', ); - expect(paragraphs.at(4).text()).toContain( + expect(paragraphs.at(3).text()).toContain( IssuesListApp.i18n.jiraIntegrationSecondaryMessage, ); expect(findGlLink().text()).toBe('Enable the Jira integration'); diff --git a/spec/frontend/issues_list/utils_spec.js b/spec/frontend/issues_list/utils_spec.js index fbd0a535174..00b387263ea 100644 --- a/spec/frontend/issues_list/utils_spec.js +++ b/spec/frontend/issues_list/utils_spec.js @@ -6,6 +6,7 @@ import { convertToUrlParams, getFilterTokens, getSortKey, + getSortOptions, } from '~/issues_list/utils'; describe('getSortKey', () => { @@ -15,6 +16,39 @@ describe('getSortKey', () => { }); }); +describe('getSortOptions', () => { + describe.each` + hasIssueWeightsFeature | hasBlockedIssuesFeature | length | containsWeight | containsBlocking + ${false} | ${false} | ${8} | ${false} | ${false} + ${true} | ${false} | ${9} | ${true} | ${false} + ${false} | ${true} | ${9} | ${false} | ${true} + ${true} | ${true} | ${10} | ${true} | ${true} + `( + 'when hasIssueWeightsFeature=$hasIssueWeightsFeature and hasBlockedIssuesFeature=$hasBlockedIssuesFeature', + ({ + hasIssueWeightsFeature, + hasBlockedIssuesFeature, + length, + containsWeight, + containsBlocking, + }) => { + const sortOptions = getSortOptions(hasIssueWeightsFeature, hasBlockedIssuesFeature); + + it('returns the correct length of sort options', () => { + expect(sortOptions).toHaveLength(length); + }); + + it(`${containsWeight ? 'contains' : 'does not contain'} weight option`, () => { + expect(sortOptions.some((option) => option.title === 'Weight')).toBe(containsWeight); + }); + + it(`${containsBlocking ? 'contains' : 'does not contain'} blocking option`, () => { + expect(sortOptions.some((option) => option.title === 'Blocking')).toBe(containsBlocking); + }); + }, + ); +}); + describe('getFilterTokens', () => { it('returns filtered tokens given "window.location.search"', () => { expect(getFilterTokens(locationSearch)).toEqual(filteredTokens); diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js index 3f91b44ae5a..9d1135e26c8 100644 --- a/spec/frontend/jobs/components/table/job_table_app_spec.js +++ b/spec/frontend/jobs/components/table/job_table_app_spec.js @@ -1,5 +1,5 @@ -import { GlSkeletonLoader, GlAlert } from '@gitlab/ui'; -import { createLocalVue, shallowMount } from '@vue/test-utils'; +import { GlSkeletonLoader, GlAlert, GlEmptyState } from '@gitlab/ui'; +import { createLocalVue, mount, shallowMount } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; @@ -7,7 +7,7 @@ import getJobsQuery from '~/jobs/components/table/graphql/queries/get_jobs.query import JobsTable from '~/jobs/components/table/jobs_table.vue'; import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue'; import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue'; -import { mockJobsQueryResponse } from '../../mock_data'; +import { mockJobsQueryResponse, mockJobsQueryEmptyResponse } from '../../mock_data'; const projectPath = 'gitlab-org/gitlab'; const localVue = createLocalVue(); @@ -18,11 +18,13 @@ describe('Job table app', () => { const successHandler = jest.fn().mockResolvedValue(mockJobsQueryResponse); const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); + const emptyHandler = jest.fn().mockResolvedValue(mockJobsQueryEmptyResponse); const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); const findTable = () => wrapper.findComponent(JobsTable); const findTabs = () => wrapper.findComponent(JobsTableTabs); const findAlert = () => wrapper.findComponent(GlAlert); + const findEmptyState = () => wrapper.findComponent(GlEmptyState); const createMockApolloProvider = (handler) => { const requestHandlers = [[getJobsQuery, handler]]; @@ -30,8 +32,8 @@ describe('Job table app', () => { return createMockApollo(requestHandlers); }; - const createComponent = (handler = successHandler) => { - wrapper = shallowMount(JobsTableApp, { + const createComponent = (handler = successHandler, mountFn = shallowMount) => { + wrapper = mountFn(JobsTableApp, { provide: { projectPath, }, @@ -85,4 +87,24 @@ describe('Job table app', () => { expect(findAlert().exists()).toBe(true); }); }); + + describe('empty state', () => { + it('should display empty state if there are no jobs and tab scope is null', async () => { + createComponent(emptyHandler, mount); + + await waitForPromises(); + + expect(findEmptyState().exists()).toBe(true); + expect(findTable().exists()).toBe(false); + }); + + it('should not display empty state if there are jobs and tab scope is not null', async () => { + createComponent(successHandler, mount); + + await waitForPromises(); + + expect(findEmptyState().exists()).toBe(false); + expect(findTable().exists()).toBe(true); + }); + }); }); diff --git a/spec/frontend/jobs/components/table/jobs_table_empty_state_spec.js b/spec/frontend/jobs/components/table/jobs_table_empty_state_spec.js new file mode 100644 index 00000000000..05b066a9edc --- /dev/null +++ b/spec/frontend/jobs/components/table/jobs_table_empty_state_spec.js @@ -0,0 +1,37 @@ +import { GlEmptyState } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import JobsTableEmptyState from '~/jobs/components/table/jobs_table_empty_state.vue'; + +describe('Jobs table empty state', () => { + let wrapper; + + const pipelineEditorPath = '/root/project/-/ci/editor'; + const emptyStateSvgPath = 'assets/jobs-empty-state.svg'; + + const findEmptyState = () => wrapper.findComponent(GlEmptyState); + + const createComponent = () => { + wrapper = shallowMount(JobsTableEmptyState, { + provide: { + pipelineEditorPath, + emptyStateSvgPath, + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + it('displays empty state', () => { + expect(findEmptyState().exists()).toBe(true); + }); + + it('links to the pipeline editor', () => { + expect(findEmptyState().props('primaryButtonLink')).toBe(pipelineEditorPath); + }); + + it('shows an empty state image', () => { + expect(findEmptyState().props('svgPath')).toBe(emptyStateSvgPath); + }); +}); diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js index bdedbffff22..d2c065a3aba 100644 --- a/spec/frontend/jobs/mock_data.js +++ b/spec/frontend/jobs/mock_data.js @@ -1501,3 +1501,11 @@ export const mockJobsQueryResponse = { }, }, }; + +export const mockJobsQueryEmptyResponse = { + data: { + project: { + jobs: [], + }, + }, +}; diff --git a/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js b/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js index d175b541edb..a09edb50f20 100644 --- a/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js +++ b/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js @@ -82,20 +82,20 @@ describe('Merge Conflict Resolver App', () => { const interactiveButton = findFileInteractiveButton(findFiles().at(0)); const inlineButton = findFileInlineButton(findFiles().at(0)); - expect(interactiveButton.classes('active')).toBe(true); - expect(inlineButton.classes('active')).toBe(false); + expect(interactiveButton.props('selected')).toBe(true); + expect(inlineButton.props('selected')).toBe(false); }); it('clicking inline set inline as default', async () => { mountComponent(); const inlineButton = findFileInlineButton(findFiles().at(0)); - expect(inlineButton.classes('active')).toBe(false); + expect(inlineButton.props('selected')).toBe(false); - inlineButton.trigger('click'); + inlineButton.vm.$emit('click'); await wrapper.vm.$nextTick(); - expect(inlineButton.classes('active')).toBe(true); + expect(inlineButton.props('selected')).toBe(true); }); it('inline mode shows a inline-conflict-lines', () => { @@ -110,7 +110,7 @@ describe('Merge Conflict Resolver App', () => { it('parallel mode shows a parallel-conflict-lines', async () => { mountComponent(); - findSideBySideButton().trigger('click'); + findSideBySideButton().vm.$emit('click'); await wrapper.vm.$nextTick(); const parallelConflictLinesComponent = findParallelConflictLines(findFiles().at(0)); diff --git a/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js b/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js index d4e2483b765..273235fe197 100644 --- a/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js +++ b/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js @@ -1,3 +1,4 @@ +import { GlDatepicker } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; @@ -23,6 +24,7 @@ describe('Sidebar date Widget', () => { const findEditableItem = () => wrapper.findComponent(SidebarEditableItem); const findPopoverIcon = () => wrapper.find('[data-testid="inherit-date-popover"]'); + const findDatePicker = () => wrapper.find(GlDatepicker); const createComponent = ({ dueDateQueryHandler = jest.fn().mockResolvedValue(issuableDueDateResponse()), @@ -50,6 +52,7 @@ describe('Sidebar date Widget', () => { }, stubs: { SidebarEditableItem, + GlDatepicker, }, }); }; @@ -109,6 +112,11 @@ describe('Sidebar date Widget', () => { it('emits `dueDateUpdated` event with the date payload', () => { expect(wrapper.emitted('dueDateUpdated')).toEqual([[date]]); }); + + it('uses a correct prop to set the initial date for GlDatePicker', () => { + expect(findDatePicker().props('value')).toBe(null); + expect(findDatePicker().props('defaultDate')).toEqual(wrapper.vm.parsedDate); + }); }); it.each` diff --git a/spec/lib/gitlab/alert_management/payload/base_spec.rb b/spec/lib/gitlab/alert_management/payload/base_spec.rb index e093b3587c2..d3c1a96253c 100644 --- a/spec/lib/gitlab/alert_management/payload/base_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/base_spec.rb @@ -89,6 +89,12 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do it { is_expected.to be_nil } end + + context 'with time in seconds' do + let(:raw_payload) { { 'test' => 1618877936 } } + + it { is_expected.to be_nil } + end end context 'with an integer type provided' do diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index b735ac7940b..cc9f005b189 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -176,7 +176,7 @@ RSpec.describe Gitlab::Database do closed_pool = pool - raise error.new('boom') + raise error, 'boom' end rescue error end diff --git a/spec/lib/gitlab/external_authorization/access_spec.rb b/spec/lib/gitlab/external_authorization/access_spec.rb index a6773cc19e1..812ef2b54e9 100644 --- a/spec/lib/gitlab/external_authorization/access_spec.rb +++ b/spec/lib/gitlab/external_authorization/access_spec.rb @@ -82,7 +82,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache context 'when the request fails' do before do allow(fake_client).to receive(:request_access) do - raise ::Gitlab::ExternalAuthorization::RequestFailed.new('Service unavailable') + raise ::Gitlab::ExternalAuthorization::RequestFailed, 'Service unavailable' end end diff --git a/spec/lib/gitlab/external_authorization/client_spec.rb b/spec/lib/gitlab/external_authorization/client_spec.rb index c08da382486..b907b0bb262 100644 --- a/spec/lib/gitlab/external_authorization/client_spec.rb +++ b/spec/lib/gitlab/external_authorization/client_spec.rb @@ -71,7 +71,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Client do end it 'wraps exceptions if the request fails' do - expect(Gitlab::HTTP).to receive(:post) { raise Gitlab::HTTP::BlockedUrlError.new('the request broke') } + expect(Gitlab::HTTP).to receive(:post) { raise Gitlab::HTTP::BlockedUrlError, 'the request broke' } expect { client.request_access } .to raise_error(::Gitlab::ExternalAuthorization::RequestFailed) diff --git a/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb b/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb index e448277b307..2c9da0f6606 100644 --- a/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb +++ b/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::Git::WrapsGitalyErrors do mapping.each do |grpc_error, error| it "wraps #{grpc_error} in a #{error}" do - expect { wrapper.wrapped_gitaly_errors { raise grpc_error.new('wrapped') } } + expect { wrapper.wrapped_gitaly_errors { raise grpc_error, 'wrapped' } } .to raise_error(error) end end diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 3e5fbbfe823..028208a56f1 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -178,7 +178,7 @@ RSpec.describe Ci::PipelineSchedule do context 'when record is invalid' do before do - allow(pipeline_schedule).to receive(:save!) { raise ActiveRecord::RecordInvalid.new(pipeline_schedule) } + allow(pipeline_schedule).to receive(:save!) { raise ActiveRecord::RecordInvalid, pipeline_schedule } end it 'nullifies the next run at' do diff --git a/spec/models/namespace/traversal_hierarchy_spec.rb b/spec/models/namespace/traversal_hierarchy_spec.rb index b166d541171..baefc161691 100644 --- a/spec/models/namespace/traversal_hierarchy_spec.rb +++ b/spec/models/namespace/traversal_hierarchy_spec.rb @@ -86,7 +86,7 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model do connection_double = double(:connection) allow(Namespace).to receive(:connection).and_return(connection_double) - allow(connection_double).to receive(:exec_query) { raise ActiveRecord::Deadlocked.new } + allow(connection_double).to receive(:exec_query) { raise ActiveRecord::Deadlocked } end it { expect { subject }.to raise_error(ActiveRecord::Deadlocked) } diff --git a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb index 63bcec4b52a..ba7a01a2cd9 100644 --- a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb +++ b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb @@ -13,7 +13,7 @@ RSpec.describe API::APIGuard::AdminModeMiddleware, :request_store do let(:app) do Class.new(API::API) do get 'willfail' do - raise StandardError.new('oh noes!') + raise StandardError, 'oh noes!' end end end diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 8160a94aef2..ce0018d6d0d 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -39,7 +39,7 @@ RSpec.describe API::Helpers do end def error!(message, status, header) - raise StandardError.new("#{status} - #{message}") + raise StandardError, "#{status} - #{message}" end def set_param(key, value) diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index dcef0653ec0..f3d100449a1 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -18,7 +18,7 @@ RSpec.describe API::Releases do project.add_developer(developer) end - describe 'GET /projects/:id/releases' do + describe 'GET /projects/:id/releases', :use_clean_rails_redis_caching do context 'when there are two releases' do let!(:release_1) do create(:release, @@ -147,6 +147,60 @@ RSpec.describe API::Releases do end.not_to exceed_all_query_limit(control_count) end + it 'serializes releases for the first time and read cached data from the second time' do + create_list(:release, 2, project: project) + + expect(API::Entities::Release) + .to receive(:represent).with(instance_of(Release), any_args) + .twice + + 5.times { get api("/projects/#{project.id}/releases", maintainer) } + end + + it 'increments the cache key when link is updated' do + releases = create_list(:release, 2, project: project) + + expect(API::Entities::Release) + .to receive(:represent).with(instance_of(Release), any_args) + .exactly(4).times + + 2.times { get api("/projects/#{project.id}/releases", maintainer) } + + releases.each { |release| create(:release_link, release: release) } + + 3.times { get api("/projects/#{project.id}/releases", maintainer) } + end + + it 'increments the cache key when evidence is updated' do + releases = create_list(:release, 2, project: project) + + expect(API::Entities::Release) + .to receive(:represent).with(instance_of(Release), any_args) + .exactly(4).times + + 2.times { get api("/projects/#{project.id}/releases", maintainer) } + + releases.each { |release| create(:evidence, release: release) } + + 3.times { get api("/projects/#{project.id}/releases", maintainer) } + end + + context 'when api_caching_releases feature flag is disabled' do + before do + stub_feature_flags(api_caching_releases: false) + end + + it 'serializes releases everytime' do + create_list(:release, 2, project: project) + + expect(API::Entities::Release) + .to receive(:represent).with(kind_of(ActiveRecord::Relation), any_args) + .exactly(5).times + + 5.times { get api("/projects/#{project.id}/releases", maintainer) } + end + end + context 'when tag does not exist in git repository' do let!(:release) { create(:release, project: project, tag: 'v1.1.5') } @@ -230,6 +284,20 @@ RSpec.describe API::Releases do end end end + + context 'when releases are public and request user is absent' do + let(:project) { create(:project, :repository, :public) } + + it 'returns the releases' do + create(:release, project: project, tag: 'v0.1') + + get api("/projects/#{project.id}/releases") + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.count).to eq(1) + expect(json_response.first['tag_name']).to eq('v0.1') + end + end end describe 'GET /projects/:id/releases/:tag_name' do diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb index 84c64508a8d..86a6cdee52d 100644 --- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb +++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb @@ -48,7 +48,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do context 'with resolving payload' do let(:prometheus_status) { 'resolved' } - it_behaves_like 'processes prometheus recovery alert' + it_behaves_like 'processes recovery alert' end context 'environment given' do diff --git a/spec/services/auto_merge/base_service_spec.rb b/spec/services/auto_merge/base_service_spec.rb index 1d33dc15838..3f535b83788 100644 --- a/spec/services/auto_merge/base_service_spec.rb +++ b/spec/services/auto_merge/base_service_spec.rb @@ -84,7 +84,7 @@ RSpec.describe AutoMerge::BaseService do context 'when failed to save merge request' do before do - allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid.new } + allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid } end it 'does not yield block' do @@ -195,7 +195,7 @@ RSpec.describe AutoMerge::BaseService do context 'when failed to save' do before do - allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid.new } + allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid } end it 'does not yield block' do @@ -213,7 +213,7 @@ RSpec.describe AutoMerge::BaseService do context 'when failed to save merge request' do before do - allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid.new } + allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid } end it 'returns error status' do @@ -260,7 +260,7 @@ RSpec.describe AutoMerge::BaseService do context 'when failed to save' do before do - allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid.new } + allow(merge_request).to receive(:save!) { raise ActiveRecord::RecordInvalid } end it 'returns error status' do diff --git a/spec/services/branches/delete_service_spec.rb b/spec/services/branches/delete_service_spec.rb index 291431c1723..727cadc5a50 100644 --- a/spec/services/branches/delete_service_spec.rb +++ b/spec/services/branches/delete_service_spec.rb @@ -41,7 +41,7 @@ RSpec.describe Branches::DeleteService do context 'when Gitlab::Git::CommandError is raised' do before do allow(repository).to receive(:rm_branch) do - raise Gitlab::Git::CommandError.new('Could not update patch') + raise Gitlab::Git::CommandError, 'Could not update patch' end end diff --git a/spec/services/ci/parse_dotenv_artifact_service_spec.rb b/spec/services/ci/parse_dotenv_artifact_service_spec.rb index 8a96479bc6f..7536e04f2de 100644 --- a/spec/services/ci/parse_dotenv_artifact_service_spec.rb +++ b/spec/services/ci/parse_dotenv_artifact_service_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do context 'when parse error happens' do before do - allow(service).to receive(:scan_line!) { raise described_class::ParserError.new('Invalid Format') } + allow(service).to receive(:scan_line!) { raise described_class::ParserError, 'Invalid Format' } end it 'returns error' do diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb index 938165a807c..01dce0dfbce 100644 --- a/spec/services/merge_requests/merge_to_ref_service_spec.rb +++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb @@ -94,7 +94,7 @@ RSpec.describe MergeRequests::MergeToRefService do it 'returns an error when Gitlab::Git::CommandError is raised during merge' do allow(project.repository).to receive(:merge_to_ref) do - raise Gitlab::Git::CommandError.new('Failed to create merge commit') + raise Gitlab::Git::CommandError, 'Failed to create merge commit' end result = service.execute(merge_request) diff --git a/spec/support/helpers/board_helpers.rb b/spec/support/helpers/board_helpers.rb index 6e145fed733..dff3f9d976c 100644 --- a/spec/support/helpers/board_helpers.rb +++ b/spec/support/helpers/board_helpers.rb @@ -4,6 +4,7 @@ module BoardHelpers def click_card(card) within card do first('.board-card-number').click + wait_for_requests end end end diff --git a/spec/support/helpers/dns_helpers.rb b/spec/support/helpers/dns_helpers.rb index 1795b0a9ac3..ba32ccbb6f1 100644 --- a/spec/support/helpers/dns_helpers.rb +++ b/spec/support/helpers/dns_helpers.rb @@ -18,7 +18,7 @@ module DnsHelpers def stub_invalid_dns! allow(Addrinfo).to receive(:getaddrinfo).with(/\Afoobar\.\w|(\d{1,3}\.){4,}\d{1,3}\z/i, anything, nil, :STREAM) do - raise SocketError.new("getaddrinfo: Name or service not known") + raise SocketError, "getaddrinfo: Name or service not known" end end diff --git a/spec/support/helpers/next_found_instance_of.rb b/spec/support/helpers/next_found_instance_of.rb index feb63f90211..c8cdbaf2c5d 100644 --- a/spec/support/helpers/next_found_instance_of.rb +++ b/spec/support/helpers/next_found_instance_of.rb @@ -22,7 +22,7 @@ module NextFoundInstanceOf private def check_if_active_record!(klass) - raise ArgumentError.new(ERROR_MESSAGE) unless klass < ActiveRecord::Base + raise ArgumentError, ERROR_MESSAGE unless klass < ActiveRecord::Base end def stub_allocate(target, klass) diff --git a/spec/support/helpers/redis_without_keys.rb b/spec/support/helpers/redis_without_keys.rb index e030f1028f7..ff64a3cf08e 100644 --- a/spec/support/helpers/redis_without_keys.rb +++ b/spec/support/helpers/redis_without_keys.rb @@ -4,7 +4,7 @@ class Redis ForbiddenCommand = Class.new(StandardError) def keys(*args) - raise ForbiddenCommand.new("Don't use `Redis#keys` as it iterates over all "\ - "keys in redis. Use `Redis#scan_each` instead.") + raise ForbiddenCommand, "Don't use `Redis#keys` as it iterates over all "\ + "keys in redis. Use `Redis#scan_each` instead." end end diff --git a/spec/support/helpers/require_migration.rb b/spec/support/helpers/require_migration.rb index c2902aa4ec7..b5313e78f5d 100644 --- a/spec/support/helpers/require_migration.rb +++ b/spec/support/helpers/require_migration.rb @@ -20,7 +20,7 @@ class RequireMigration class << self def require_migration!(file_name) file_paths = search_migration_file(file_name) - raise AutoLoadError.new(file_name) unless file_paths.first + raise AutoLoadError, file_name unless file_paths.first require file_paths.first end diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb index e299a83cf97..86e7da5bcbe 100644 --- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb @@ -111,67 +111,3 @@ RSpec.shared_examples 'processes recovery alert' do end end end - -RSpec.shared_examples 'processes prometheus recovery alert' do - context 'seen for the first time' do - it_behaves_like 'does not create an alert management alert' - it_behaves_like 'does not send alert notification emails' - it_behaves_like 'does not process incident issues' - end - - context 'for an existing alert with the same fingerprint' do - let_it_be(:gitlab_fingerprint) { Digest::SHA1.hexdigest(fingerprint) } - - context 'which is triggered' do - let_it_be(:alert) { create(:alert_management_alert, :triggered, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) } - - it_behaves_like 'resolves an existing alert management alert' - it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert - it_behaves_like 'sends alert notification emails if enabled' - it_behaves_like 'closes related incident if enabled' - it_behaves_like 'writes a warning to the log for a failed alert status update' - - it_behaves_like 'does not create an alert management alert' - it_behaves_like 'does not process incident issues' - it_behaves_like 'does not add an alert management alert event' - end - - context 'which is ignored' do - let_it_be(:alert) { create(:alert_management_alert, :ignored, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) } - - it_behaves_like 'resolves an existing alert management alert' - it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert - it_behaves_like 'sends alert notification emails if enabled' - it_behaves_like 'closes related incident if enabled' - it_behaves_like 'writes a warning to the log for a failed alert status update' - - it_behaves_like 'does not create an alert management alert' - it_behaves_like 'does not process incident issues' - it_behaves_like 'does not add an alert management alert event' - end - - context 'which is acknowledged' do - let_it_be(:alert) { create(:alert_management_alert, :acknowledged, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) } - - it_behaves_like 'resolves an existing alert management alert' - it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert - it_behaves_like 'sends alert notification emails if enabled' - it_behaves_like 'closes related incident if enabled' - it_behaves_like 'writes a warning to the log for a failed alert status update' - - it_behaves_like 'does not create an alert management alert' - it_behaves_like 'does not process incident issues' - it_behaves_like 'does not add an alert management alert event' - end - - context 'which is resolved' do - let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) } - - it_behaves_like 'does not create an alert management alert' - it_behaves_like 'does not send alert notification emails' - it_behaves_like 'does not change the alert end time' - it_behaves_like 'does not process incident issues' - it_behaves_like 'does not add an alert management alert event' - end - end -end diff --git a/spec/support/shared_examples/services/alert_management_shared_examples.rb b/spec/support/shared_examples/services/alert_management_shared_examples.rb index a0e084967e5..827ae42f970 100644 --- a/spec/support/shared_examples/services/alert_management_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb @@ -48,9 +48,19 @@ end RSpec.shared_examples 'processes never-before-seen recovery alert' do it_behaves_like 'creates an alert management alert or errors' - it_behaves_like 'creates expected system notes for alert', :new_alert + it_behaves_like 'creates expected system notes for alert', :new_alert, :recovery_alert, :resolve_alert it_behaves_like 'sends alert notification emails if enabled' - it_behaves_like 'processes incident issues if enabled' + it_behaves_like 'does not process incident issues' + it_behaves_like 'writes a warning to the log for a failed alert status update' do + let(:alert) { nil } # Ensure the next alert id is used + end + + it 'resolves the alert' do + subject + + expect(AlertManagement::Alert.last.ended_at).to be_present + expect(AlertManagement::Alert.last.resolved?).to be(true) + end end RSpec.shared_examples 'processes one firing and one resolved prometheus alerts' do @@ -58,10 +68,10 @@ RSpec.shared_examples 'processes one firing and one resolved prometheus alerts' expect(Gitlab::AppLogger).not_to receive(:warn) expect { subject } - .to change(AlertManagement::Alert, :count).by(1) - .and change(Note, :count).by(1) + .to change(AlertManagement::Alert, :count).by(2) + .and change(Note, :count).by(4) end it_behaves_like 'processes incident issues' - it_behaves_like 'sends alert notification emails', count: 1 + it_behaves_like 'sends alert notification emails', count: 2 end diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb index 95f0f971787..84396c675b9 100644 --- a/spec/support/stored_repositories.rb +++ b/spec/support/stored_repositories.rb @@ -3,7 +3,7 @@ RSpec.configure do |config| config.before(:each, :broken_storage) do allow(Gitlab::GitalyClient).to receive(:call) do - raise GRPC::Unavailable.new('Gitaly broken in this spec') + raise GRPC::Unavailable, 'Gitaly broken in this spec' end end end |