diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-07 18:12:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-07 18:12:19 +0300 |
commit | 9a940dabf04df126e7978c0ab4b8770b86dcaaa8 (patch) | |
tree | f8b244f0cd4bf455015b97ba72ea3d4b51419d05 /spec | |
parent | 8e81ce50767bd5c785072c2487ffb61fe075977c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
29 files changed, 456 insertions, 196 deletions
diff --git a/spec/channels/application_cable/connection_spec.rb b/spec/channels/application_cable/connection_spec.rb index 4943669bde0..fa2518e1970 100644 --- a/spec/channels/application_cable/connection_spec.rb +++ b/spec/channels/application_cable/connection_spec.rb @@ -43,6 +43,16 @@ RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_sessions do end end + context 'when bearer header is provided' do + let(:user_pat) { create(:personal_access_token) } + + it 'finds user by PAT' do + connect(ActionCable.server.config.mount_path, headers: { Authorization: "Bearer #{user_pat.token}" }) + + expect(connection.current_user).to eq(user_pat.user) + end + end + context 'when session cookie is not set' do it 'sets current_user to nil' do connect diff --git a/spec/features/projects/work_items/work_item_children_spec.rb b/spec/features/projects/work_items/work_item_children_spec.rb index 0970752157d..28f7ee2db10 100644 --- a/spec/features/projects/work_items/work_item_children_spec.rb +++ b/spec/features/projects/work_items/work_item_children_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Work item children', :js, feature_category: :team_planning do it 'are not displayed when issue does not have work item children', :aggregate_failures do page.within('[data-testid="work-item-links"]') do - expect(find('[data-testid="links-empty"]')).to have_content(_('No tasks are currently assigned.')) + expect(find('[data-testid="links-empty"]')).to have_content(_('No child items are currently assigned.')) expect(page).not_to have_selector('[data-testid="add-links-form"]') expect(page).not_to have_selector('[data-testid="links-child"]') end diff --git a/spec/frontend/projects/components/shared/delete_modal_spec.js b/spec/frontend/projects/components/shared/delete_modal_spec.js index c6213fd4b6d..7e040db4beb 100644 --- a/spec/frontend/projects/components/shared/delete_modal_spec.js +++ b/spec/frontend/projects/components/shared/delete_modal_spec.js @@ -49,7 +49,7 @@ describe('DeleteModal', () => { attributes: { variant: 'danger', disabled: true, - 'data-qa-selector': 'confirm_delete_button', + 'data-testid': 'confirm-delete-button', }, }, actionCancel: { diff --git a/spec/frontend/projects/settings_service_desk/components/custom_email_spec.js b/spec/frontend/projects/settings_service_desk/components/custom_email_spec.js index faef932fc7f..0a593f3812a 100644 --- a/spec/frontend/projects/settings_service_desk/components/custom_email_spec.js +++ b/spec/frontend/projects/settings_service_desk/components/custom_email_spec.js @@ -3,7 +3,6 @@ import { mount } from '@vue/test-utils'; import { nextTick } from 'vue'; import CustomEmail from '~/projects/settings_service_desk/components/custom_email.vue'; import { - I18N_VERIFICATION_ERRORS, I18N_STATE_VERIFICATION_STARTED, I18N_STATE_VERIFICATION_FAILED, I18N_STATE_VERIFICATION_FAILED_RESET_PARAGRAPH, @@ -15,6 +14,7 @@ describe('CustomEmail', () => { let wrapper; const defaultProps = { + incomingEmail: 'incoming+test-1-issue-@example.com', customEmail: 'user@example.com', smtpAddress: 'smtp.example.com', verificationState: 'started', @@ -70,19 +70,21 @@ describe('CustomEmail', () => { }); describe('verification error', () => { - it.each([ - 'smtp_host_issue', - 'invalid_credentials', - 'mail_not_received_within_timeframe', - 'incorrect_from', - 'incorrect_token', - 'read_timeout', - ])('displays %s label and description', (error) => { + it.each` + error | label | description + ${'smtp_host_issue'} | ${'SMTP host issue'} | ${'A connection to the specified host could not be made or an SSL issue occurred.'} + ${'invalid_credentials'} | ${'Invalid credentials'} | ${'The given credentials (username and password) were rejected by the SMTP server, or you need to explicitly set an authentication method.'} + ${'mail_not_received_within_timeframe'} | ${'Verification email not received within timeframe'} | ${"The verification email wasn't received in time. There is a 30 minutes timeframe for verification emails to appear in your instance's Service Desk. Make sure that you have set up email forwarding correctly."} + ${'incorrect_from'} | ${'Incorrect From header'} | ${'Check your forwarding settings and make sure the original email sender remains in the From header.'} + ${'incorrect_token'} | ${'Incorrect verification token'} | ${"The received email didn't contain the verification token that was sent to your email address."} + ${'read_timeout'} | ${'Read timeout'} | ${'The SMTP server did not respond in time.'} + ${'incorrect_forwarding_target'} | ${'Incorrect forwarding target'} | ${`Forward all emails to the custom email address to ${defaultProps.incomingEmail}`} + `('displays $error label and description', ({ error, label, description }) => { createWrapper({ verificationError: error }); const text = wrapper.text(); - expect(text).toContain(I18N_VERIFICATION_ERRORS[error].label); - expect(text).toContain(I18N_VERIFICATION_ERRORS[error].description); + expect(text).toContain(label); + expect(text).toContain(description); }); }); diff --git a/spec/frontend/projects/settings_service_desk/components/custom_email_wrapper_spec.js b/spec/frontend/projects/settings_service_desk/components/custom_email_wrapper_spec.js index 174e05ceeee..8d3a7a5fde5 100644 --- a/spec/frontend/projects/settings_service_desk/components/custom_email_wrapper_spec.js +++ b/spec/frontend/projects/settings_service_desk/components/custom_email_wrapper_spec.js @@ -38,6 +38,12 @@ describe('CustomEmailWrapper', () => { customEmailEndpoint: '/flightjs/Flight/-/service_desk/custom_email', }; + const defaultCustomEmailProps = { + incomingEmail: defaultProps.incomingEmail, + customEmail: 'user@example.com', + smtpAddress: 'smtp.example.com', + }; + const showToast = jest.fn(); const createWrapper = (props = {}) => { @@ -117,8 +123,7 @@ describe('CustomEmailWrapper', () => { expect(showToast).toHaveBeenCalledWith(I18N_TOAST_SAVED); expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'started', verificationError: null, isEnabled: false, @@ -140,8 +145,7 @@ describe('CustomEmailWrapper', () => { it('displays CustomEmail component', () => { expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'started', verificationError: null, isEnabled: false, @@ -193,8 +197,7 @@ describe('CustomEmailWrapper', () => { it('fetches data from endpoint and displays CustomEmail component', () => { expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'failed', verificationError: 'smtp_host_issue', isEnabled: false, @@ -225,8 +228,7 @@ describe('CustomEmailWrapper', () => { it('fetches data from endpoint and displays CustomEmail component', () => { expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'finished', verificationError: null, isEnabled: false, @@ -257,8 +259,7 @@ describe('CustomEmailWrapper', () => { expect(showToast).toHaveBeenCalledWith(I18N_TOAST_ENABLED); expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'finished', verificationError: null, isEnabled: true, @@ -279,8 +280,7 @@ describe('CustomEmailWrapper', () => { it('fetches data from endpoint and displays CustomEmail component', () => { expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'finished', verificationError: null, isEnabled: true, @@ -301,8 +301,7 @@ describe('CustomEmailWrapper', () => { expect(showToast).toHaveBeenCalledWith(I18N_TOAST_DISABLED); expect(findCustomEmail().props()).toEqual({ - customEmail: 'user@example.com', - smtpAddress: 'smtp.example.com', + ...defaultCustomEmailProps, verificationState: 'finished', verificationError: null, isEnabled: false, diff --git a/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js b/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js index 1376133ec37..02da6079466 100644 --- a/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js +++ b/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js @@ -8,7 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises'; describe('EntitySelect', () => { let wrapper; let fetchItemsMock; - let fetchInitialSelectionTextMock; + let fetchInitialSelectionMock; // Mocks const itemMock = { @@ -96,16 +96,16 @@ describe('EntitySelect', () => { }); it("fetches the initially selected value's name", async () => { - fetchInitialSelectionTextMock = jest.fn().mockImplementation(() => itemMock.text); + fetchInitialSelectionMock = jest.fn().mockImplementation(() => itemMock); createComponent({ props: { - fetchInitialSelectionText: fetchInitialSelectionTextMock, + fetchInitialSelection: fetchInitialSelectionMock, initialSelection: itemMock.value, }, }); await nextTick(); - expect(fetchInitialSelectionTextMock).toHaveBeenCalledTimes(1); + expect(fetchInitialSelectionMock).toHaveBeenCalledTimes(1); expect(findListbox().props('toggleText')).toBe(itemMock.text); }); }); @@ -188,7 +188,7 @@ describe('EntitySelect', () => { findListbox().vm.$emit('reset'); await nextTick(); - expect(Object.keys(wrapper.emitted('input')[2][0]).length).toBe(0); + expect(wrapper.emitted('input')[2][0]).toEqual({}); }); }); }); diff --git a/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js b/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js index 02a85edc523..6dc38bbd0c6 100644 --- a/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js +++ b/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js @@ -1,8 +1,8 @@ import VueApollo from 'vue-apollo'; -import Vue, { nextTick } from 'vue'; -import { GlCollapsibleListbox } from '@gitlab/ui'; +import Vue from 'vue'; +import { GlCollapsibleListbox, GlAlert } from '@gitlab/ui'; import { chunk } from 'lodash'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; import OrganizationSelect from '~/vue_shared/components/entity_select/organization_select.vue'; import EntitySelect from '~/vue_shared/components/entity_select/entity_select.vue'; import { DEFAULT_PER_PAGE } from '~/api'; @@ -17,6 +17,7 @@ import getOrganizationQuery from '~/organizations/shared/graphql/queries/organiz import { organizations as nodes, pageInfo, pageInfoEmpty } from '~/organizations/mock_data'; import waitForPromises from 'helpers/wait_for_promises'; import createMockApollo from 'helpers/mock_apollo_helper'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; Vue.use(VueApollo); @@ -31,11 +32,6 @@ describe('OrganizationSelect', () => { pageInfo, }; - // Stubs - const GlAlert = { - template: '<div><slot /></div>', - }; - // Props const label = 'label'; const description = 'description'; @@ -67,7 +63,7 @@ describe('OrganizationSelect', () => { } = {}) => { mockApollo = createMockApollo(handlers); - wrapper = shallowMountExtended(OrganizationSelect, { + wrapper = mountExtended(OrganizationSelect, { apolloProvider: mockApollo, propsData: { label, @@ -77,10 +73,6 @@ describe('OrganizationSelect', () => { toggleClass, ...props, }, - stubs: { - GlAlert, - EntitySelect, - }, listeners: { input: handleInput, }, @@ -88,10 +80,6 @@ describe('OrganizationSelect', () => { }; const openListbox = () => findListbox().vm.$emit('shown'); - afterEach(() => { - mockApollo = null; - }); - describe('entity_select props', () => { beforeEach(() => { createComponent(); @@ -114,15 +102,16 @@ describe('OrganizationSelect', () => { describe('on mount', () => { it('fetches organizations when the listbox is opened', async () => { createComponent(); - await waitForPromises(); - openListbox(); await waitForPromises(); - expect(findListbox().props('items')).toEqual([ - { text: nodes[0].name, value: 1 }, - { text: nodes[1].name, value: 2 }, - { text: nodes[2].name, value: 3 }, - ]); + + const expectedItems = nodes.map((node) => ({ + ...node, + text: node.name, + value: getIdFromGraphQLId(node.id), + })); + + expect(findListbox().props('items')).toEqual(expectedItems); }); describe('with an initial selection', () => { @@ -136,7 +125,7 @@ describe('OrganizationSelect', () => { it('show an error if fetching initially selected fails', async () => { createComponent({ props: { initialSelection: organization.id }, - handlers: [[getOrganizationQuery, jest.fn().mockRejectedValueOnce(new Error())]], + handlers: [[getOrganizationQuery, jest.fn().mockRejectedValueOnce()]], }); expect(findAlert().exists()).toBe(false); @@ -183,7 +172,6 @@ describe('OrganizationSelect', () => { await waitForPromises(); findListbox().vm.$emit('bottom-reached'); - await nextTick(); await waitForPromises(); }); @@ -198,10 +186,8 @@ describe('OrganizationSelect', () => { it('shows an error when fetching organizations fails', async () => { createComponent({ - handlers: [[getCurrentUserOrganizationsQuery, jest.fn().mockRejectedValueOnce(new Error())]], + handlers: [[getCurrentUserOrganizationsQuery, jest.fn().mockRejectedValueOnce()]], }); - await waitForPromises(); - openListbox(); expect(findAlert().exists()).toBe(false); diff --git a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb index cb2d3fbcae4..e8156091f9d 100644 --- a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb @@ -462,7 +462,7 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se end end - shared_examples 'a handler that does not verify the custom email' do |error_identifier| + shared_examples 'a handler that does not verify the custom email' do it 'does not verify the custom email address' do # project has no owner, so only notify verification triggerer expect(Notify).to receive(:service_desk_verification_result_email).once @@ -477,20 +477,32 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se end end - shared_examples 'a handler that verifies Service Desk custom email verification emails' do + context 'when using incoming_email address' do + before do + stub_incoming_email_setting(enabled: true, address: 'support+%{key}@example.com') + end + it_behaves_like 'an early exiting handler' context 'with valid service desk settings' do let_it_be(:user) { create(:user) } + let_it_be(:credentials) { create(:service_desk_custom_email_credential, project: project) } - let!(:settings) { create(:service_desk_setting, project: project, custom_email: 'custom-support-email@example.com') } - let!(:verification) { create(:service_desk_custom_email_verification, project: project, token: 'ZROT4ZZXA-Y6', triggerer: user) } + let_it_be_with_reload(:settings) do + create(:service_desk_setting, project: project, custom_email: 'custom-support-email@example.com') + end + + let_it_be_with_reload(:verification) do + create(:service_desk_custom_email_verification, project: project, token: 'ZROT4ZZXA-Y6', triggerer: user) + end let(:message_delivery) { instance_double(ActionMailer::MessageDelivery) } - before do + before_all do project.add_maintainer(user) + end + before do allow(message_delivery).to receive(:deliver_later) allow(Notify).to receive(:service_desk_verification_result_email).and_return(message_delivery) end @@ -521,7 +533,9 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se verification.update!(token: 'XXXXXXXXXXXX') end - it_behaves_like 'a handler that does not verify the custom email', 'incorrect_token' + it_behaves_like 'a handler that does not verify the custom email' do + let(:error_identifier) { 'incorrect_token' } + end end context 'and verification email ingested too late' do @@ -529,7 +543,9 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se verification.update!(triggered_at: ServiceDesk::CustomEmailVerification::TIMEFRAME.ago) end - it_behaves_like 'a handler that does not verify the custom email', 'mail_not_received_within_timeframe' + it_behaves_like 'a handler that does not verify the custom email' do + let(:error_identifier) { 'mail_not_received_within_timeframe' } + end end context 'and from header differs from custom email address' do @@ -537,19 +553,13 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se settings.update!(custom_email: 'different-from@example.com') end - it_behaves_like 'a handler that does not verify the custom email', 'incorrect_from' + it_behaves_like 'a handler that does not verify the custom email' do + let(:error_identifier) { 'incorrect_from' } + end end end end - context 'when using incoming_email address' do - before do - stub_incoming_email_setting(enabled: true, address: 'support+%{key}@example.com') - end - - it_behaves_like 'a handler that verifies Service Desk custom email verification emails' - end - context 'when using service_desk_email address' do let(:receiver) { Gitlab::Email::ServiceDeskReceiver.new(email_raw) } @@ -557,7 +567,35 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se stub_service_desk_email_setting(enabled: true, address: 'support+%{key}@example.com') end - it_behaves_like 'a handler that verifies Service Desk custom email verification emails' + it_behaves_like 'an early exiting handler' + + context 'with valid service desk settings' do + let_it_be(:user) { create(:user) } + let_it_be(:credentials) { create(:service_desk_custom_email_credential, project: project) } + + let_it_be_with_reload(:settings) do + create(:service_desk_setting, project: project, custom_email: 'custom-support-email@example.com') + end + + let_it_be_with_reload(:verification) do + create(:service_desk_custom_email_verification, project: project, token: 'ZROT4ZZXA-Y6', triggerer: user) + end + + let(:message_delivery) { instance_double(ActionMailer::MessageDelivery) } + + before_all do + project.add_maintainer(user) + end + + before do + allow(message_delivery).to receive(:deliver_later) + allow(Notify).to receive(:service_desk_verification_result_email).and_return(message_delivery) + end + + it_behaves_like 'a handler that does not verify the custom email' do + let(:error_identifier) { 'incorrect_forwarding_target' } + end + end end end end diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb index d876ac00e93..3ed531a16bc 100644 --- a/spec/mailers/emails/service_desk_spec.rb +++ b/spec/mailers/emails/service_desk_spec.rb @@ -625,5 +625,10 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let(:error_identifier) { 'read_timeout' } let(:expected_text) { 'Read timeout' } end + + it_behaves_like 'a custom email verification process result email with error' do + let(:error_identifier) { 'incorrect_forwarding_target' } + let(:expected_text) { 'Incorrect forwarding target' } + end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 0f786489245..05e3bffbeaf 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -738,6 +738,24 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do end end + describe '#repository_storages_with_default_weight' do + context 'with no extra storage set-up in the config file', fips_mode: false do + it 'keeps existing key restrictions' do + expect(setting.repository_storages_with_default_weight).to eq({ 'default' => 100 }) + end + end + + context 'with extra storage set-up in the config file', fips_mode: false do + before do + stub_storage_settings({ 'default' => {}, 'custom' => {} }) + end + + it 'keeps existing key restrictions' do + expect(setting.repository_storages_with_default_weight).to eq({ 'default' => 100, 'custom' => 0 }) + end + end + end + describe 'setting validated as `addressable_url` configured with external URI' do before do # Use any property that has the `addressable_url` validation. diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 8e3b97e55f3..0ed6f058768 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -1355,6 +1355,30 @@ RSpec.describe Packages::Package, type: :model, feature_category: :package_regis end end + describe '#sync_npm_metadata_cache' do + let_it_be(:package) { create(:npm_package) } + + subject { package.sync_npm_metadata_cache } + + it 'enqueues a sync worker job' do + expect(::Packages::Npm::CreateMetadataCacheWorker) + .to receive(:perform_async).with(package.project_id, package.name) + + subject + end + + context 'with a non npm package' do + let_it_be(:package) { create(:maven_package) } + + it 'does not enqueue a sync worker job' do + expect(::Packages::Npm::CreateMetadataCacheWorker) + .not_to receive(:perform_async) + + subject + end + end + end + describe '#mark_package_files_for_destruction' do let_it_be(:package) { create(:npm_package, :pending_destruction) } diff --git a/spec/requests/acme_challenges_controller_spec.rb b/spec/requests/acme_challenges_controller_spec.rb new file mode 100644 index 00000000000..f37aefed488 --- /dev/null +++ b/spec/requests/acme_challenges_controller_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe AcmeChallengesController, type: :request, feature_category: :pages do + it_behaves_like 'Base action controller' do + subject(:request) { get acme_challenge_path } + end +end diff --git a/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb index d0980a2b43d..084958be1fb 100644 --- a/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb @@ -38,6 +38,26 @@ RSpec.describe 'Destroying multiple packages', feature_category: :package_regist end it_behaves_like 'returning response status', :success + + context 'when npm package' do + let_it_be_with_reload(:packages1) { create_list(:npm_package, 3, project: project1, name: 'test-package-1') } + let_it_be_with_reload(:packages2) { create_list(:npm_package, 2, project: project2, name: 'test-package-2') } + + it 'enqueues the worker to sync a metadata cache' do + arguments = [] + + expect(Packages::Npm::CreateMetadataCacheWorker) + .to receive(:bulk_perform_async_with_contexts).and_wrap_original do |original_method, *args| + packages = args.first + arguments = packages.map(&args.second[:arguments_proc]).uniq + original_method.call(*args) + end + + mutation_request + + expect(arguments).to contain_exactly([project1.id, 'test-package-1'], [project2.id, 'test-package-2']) + end + end end shared_examples 'denying the mutation request' do diff --git a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb index 86167e7116f..6e0e5bd8aae 100644 --- a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb @@ -35,6 +35,17 @@ RSpec.describe 'Destroying a package', feature_category: :package_registry do .to change { ::Packages::Package.pending_destruction.count }.by(1) end + context 'when npm package' do + let_it_be_with_reload(:package) { create(:npm_package) } + + it 'enqueues the worker to sync a metadata cache' do + expect(Packages::Npm::CreateMetadataCacheWorker) + .to receive(:perform_async).with(project.id, package.name) + + mutation_request + end + end + it_behaves_like 'returning response status', :success end diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 46fc61c80d8..c304ae514a6 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -8,6 +8,12 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu let_it_be(:admin) { create(:admin) } describe "GET /application/settings" do + before do + # Testing config file config/gitlab.yml becomes SSOT for this API + # see https://gitlab.com/gitlab-org/gitlab/-/issues/426091#note_1675160909 + stub_storage_settings({ 'default' => {}, 'custom' => {} }) + end + it "returns application settings" do get api("/application/settings", admin) @@ -15,7 +21,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu expect(json_response).to be_an Hash expect(json_response['default_projects_limit']).to eq(42) expect(json_response['password_authentication_enabled_for_web']).to be_truthy - expect(json_response['repository_storages_weighted']).to eq({ 'default' => 100 }) + expect(json_response['repository_storages_weighted']).to eq({ 'default' => 100, 'custom' => 0 }) expect(json_response['password_authentication_enabled']).to be_truthy expect(json_response['plantuml_enabled']).to be_falsey expect(json_response['plantuml_url']).to be_nil @@ -109,7 +115,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu } expect(response).to have_gitlab_http_status(:ok) - expect(json_response['repository_storages_weighted']).to eq({ 'custom' => 75 }) + expect(json_response['repository_storages_weighted']).to eq({ 'default' => 0, 'custom' => 75 }) end context "repository_storages_weighted value is outside a 0-100 range" do @@ -131,7 +137,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu default_projects_limit: 3, default_project_creation: 2, password_authentication_enabled_for_web: false, - repository_storages_weighted: { 'custom' => 100 }, + repository_storages_weighted: { 'default' => 100, 'custom' => 0 }, plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com', diagramsnet_enabled: false, @@ -214,7 +220,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu expect(json_response['default_projects_limit']).to eq(3) expect(json_response['default_project_creation']).to eq(::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) expect(json_response['password_authentication_enabled_for_web']).to be_falsey - expect(json_response['repository_storages_weighted']).to eq({ 'custom' => 100 }) + expect(json_response['repository_storages_weighted']).to eq({ 'default' => 100, 'custom' => 0 }) expect(json_response['plantuml_enabled']).to be_truthy expect(json_response['plantuml_url']).to eq('http://plantuml.example.com') expect(json_response['diagramsnet_enabled']).to be_falsey diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index b63ea71efe6..86c4e04ef71 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -182,6 +182,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile expect(json_response.first).not_to have_key('note') expect(json_response.first).not_to have_key('namespace_id') expect(json_response.first).not_to have_key('created_by') + expect(json_response.first).not_to have_key('email_reset_offered_at') end end @@ -194,6 +195,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile expect(json_response.first).not_to have_key('note') expect(json_response.first).not_to have_key('namespace_id') expect(json_response.first).not_to have_key('created_by') + expect(json_response.first).not_to have_key('email_reset_offered_at') end end @@ -203,6 +205,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile expect(response).to have_gitlab_http_status(:success) expect(json_response.first).to have_key('note') + expect(json_response.first).to have_key('email_reset_offered_at') expect(json_response.first['note']).to eq '2018-11-05 | 2FA removed | user requested | www.gitlab.com' end diff --git a/spec/requests/application_controller_spec.rb b/spec/requests/application_controller_spec.rb new file mode 100644 index 00000000000..52fdf6bc69e --- /dev/null +++ b/spec/requests/application_controller_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ApplicationController, type: :request, feature_category: :shared do + let_it_be(:user) { create(:user) } + + before do + sign_in(user) + end + + it_behaves_like 'Base action controller' do + subject(:request) { get root_path } + end +end diff --git a/spec/requests/chaos_controller_spec.rb b/spec/requests/chaos_controller_spec.rb new file mode 100644 index 00000000000..d2ce618b041 --- /dev/null +++ b/spec/requests/chaos_controller_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ChaosController, type: :request, feature_category: :tooling do + it_behaves_like 'Base action controller' do + before do + # Stub leak_mem so we don't actually leak memory for the base action controller tests. + allow(Gitlab::Chaos).to receive(:leak_mem).with(100, 30.seconds) + end + + subject(:request) { get leakmem_chaos_path } + end +end diff --git a/spec/requests/content_security_policy_spec.rb b/spec/requests/content_security_policy_spec.rb deleted file mode 100644 index 3ce7e33d88a..00000000000 --- a/spec/requests/content_security_policy_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -# The AnonymousController doesn't support setting the CSP -# This is why an arbitrary test request was chosen instead -# of testing in application_controller_spec. -RSpec.describe 'Content Security Policy', feature_category: :application_instrumentation do - let(:snowplow_host) { 'snowplow.example.com' } - let(:vite_origin) { "#{ViteRuby.instance.config.host}:#{ViteRuby.instance.config.port}" } - - shared_examples 'snowplow is not in the CSP' do - it 'does not add the snowplow collector hostname to the CSP' do - get explore_root_url - - expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Content-Security-Policy']).not_to include(snowplow_host) - end - end - - describe 'GET #explore' do - context 'snowplow is enabled' do - before do - stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: snowplow_host) - end - - it 'adds the snowplow collector hostname to the CSP' do - get explore_root_url - - expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Content-Security-Policy']).to include(snowplow_host) - end - end - - context 'snowplow is enabled but host is not configured' do - before do - stub_application_setting(snowplow_enabled: true) - end - - it_behaves_like 'snowplow is not in the CSP' - end - - context 'snowplow is disabled' do - before do - stub_application_setting(snowplow_enabled: false, snowplow_collector_hostname: snowplow_host) - end - - it_behaves_like 'snowplow is not in the CSP' - end - - context 'when vite enabled during development', - quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424334' do - before do - stub_rails_env('development') - stub_feature_flags(vite: true) - - get explore_root_url - end - - it 'adds vite csp' do - expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Content-Security-Policy']).to include(vite_origin) - end - end - - context 'when vite disabled' do - before do - stub_feature_flags(vite: false) - - get explore_root_url - end - - it "doesn't add vite csp" do - expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Content-Security-Policy']).not_to include(vite_origin) - end - end - end -end diff --git a/spec/requests/health_controller_spec.rb b/spec/requests/health_controller_spec.rb index 639f6194af9..5fb2115aac3 100644 --- a/spec/requests/health_controller_spec.rb +++ b/spec/requests/health_controller_spec.rb @@ -73,7 +73,9 @@ RSpec.describe HealthController, feature_category: :database do end describe 'GET /-/readiness' do - subject { get '/-/readiness', params: params, headers: headers } + subject(:request) { get readiness_path, params: params, headers: headers } + + it_behaves_like 'Base action controller' shared_context 'endpoint responding with readiness data' do context 'when requesting instance-checks' do @@ -219,7 +221,6 @@ RSpec.describe HealthController, feature_category: :database do stub_remote_addr(whitelisted_ip) end - it_behaves_like 'endpoint not querying database' it_behaves_like 'endpoint responding with readiness data' context 'when requesting all checks' do @@ -236,7 +237,6 @@ RSpec.describe HealthController, feature_category: :database do stub_remote_addr(not_whitelisted_ip) end - it_behaves_like 'endpoint not querying database' it_behaves_like 'endpoint not found' end @@ -273,7 +273,6 @@ RSpec.describe HealthController, feature_category: :database do stub_remote_addr(whitelisted_ip) end - it_behaves_like 'endpoint not querying database' it_behaves_like 'endpoint responding with liveness data' end @@ -282,7 +281,6 @@ RSpec.describe HealthController, feature_category: :database do stub_remote_addr(not_whitelisted_ip) end - it_behaves_like 'endpoint not querying database' it_behaves_like 'endpoint not found' context 'accessed with valid token' do diff --git a/spec/requests/metrics_controller_spec.rb b/spec/requests/metrics_controller_spec.rb new file mode 100644 index 00000000000..ce96906e020 --- /dev/null +++ b/spec/requests/metrics_controller_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe MetricsController, type: :request, feature_category: :metrics do + it_behaves_like 'Base action controller' do + subject(:request) { get metrics_path } + end +end diff --git a/spec/requests/oauth/authorizations_controller_spec.rb b/spec/requests/oauth/authorizations_controller_spec.rb index 257f238d9ef..7887bf52542 100644 --- a/spec/requests/oauth/authorizations_controller_spec.rb +++ b/spec/requests/oauth/authorizations_controller_spec.rb @@ -20,6 +20,10 @@ RSpec.describe Oauth::AuthorizationsController, feature_category: :system_access end describe 'GET #new' do + it_behaves_like 'Base action controller' do + subject(:request) { get oauth_authorization_path } + end + context 'when application redirect URI has a custom scheme' do context 'when CSP is disabled' do before do diff --git a/spec/requests/registrations_controller_spec.rb b/spec/requests/registrations_controller_spec.rb index 8b857046a4d..71f2f347f0d 100644 --- a/spec/requests/registrations_controller_spec.rb +++ b/spec/requests/registrations_controller_spec.rb @@ -6,7 +6,9 @@ RSpec.describe RegistrationsController, type: :request, feature_category: :syste describe 'POST #create' do let_it_be(:user_attrs) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) } - subject(:create_user) { post user_registration_path, params: { user: user_attrs } } + subject(:request) { post user_registration_path, params: { user: user_attrs } } + + it_behaves_like 'Base action controller' context 'when email confirmation is required' do before do @@ -15,7 +17,7 @@ RSpec.describe RegistrationsController, type: :request, feature_category: :syste end it 'redirects to the `users_almost_there_path`', unless: Gitlab.ee? do - create_user + request expect(response).to redirect_to(users_almost_there_path(email: user_attrs[:email])) end diff --git a/spec/requests/sessions_spec.rb b/spec/requests/sessions_spec.rb index 12939acdd91..337f358d394 100644 --- a/spec/requests/sessions_spec.rb +++ b/spec/requests/sessions_spec.rb @@ -7,6 +7,10 @@ RSpec.describe 'Sessions', feature_category: :system_access do let(:user) { create(:user) } + it_behaves_like 'Base action controller' do + subject(:request) { get new_user_session_path } + end + context 'for authentication', :allow_forgery_protection do it 'logout does not require a csrf token' do login_as(user) diff --git a/spec/services/packages/mark_package_for_destruction_service_spec.rb b/spec/services/packages/mark_package_for_destruction_service_spec.rb index d65e62b84a6..bd69f995c77 100644 --- a/spec/services/packages/mark_package_for_destruction_service_spec.rb +++ b/spec/services/packages/mark_package_for_destruction_service_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Packages::MarkPackageForDestructionService, feature_category: :pa context 'when it is successful' do it 'marks the package and package files as pending destruction' do expect(package).to receive(:sync_maven_metadata).and_call_original + expect(package).to receive(:sync_npm_metadata_cache).and_call_original expect(package).to receive(:mark_package_files_for_destruction).and_call_original expect { service.execute }.to change { package.status }.from('default').to('pending_destruction') end @@ -45,6 +46,7 @@ RSpec.describe Packages::MarkPackageForDestructionService, feature_category: :pa response = service.execute expect(package).not_to receive(:sync_maven_metadata) + expect(package).not_to receive(:sync_npm_metadata_cache) expect(response).to be_a(ServiceResponse) expect(response).to be_error expect(response.message).to eq("Failed to mark the package as pending destruction") diff --git a/spec/services/packages/mark_packages_for_destruction_service_spec.rb b/spec/services/packages/mark_packages_for_destruction_service_spec.rb index 22278f9927d..cd6426d39ad 100644 --- a/spec/services/packages/mark_packages_for_destruction_service_spec.rb +++ b/spec/services/packages/mark_packages_for_destruction_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, feature_category: :package_registry do let_it_be(:project) { create(:project) } - let_it_be_with_reload(:packages) { create_list(:npm_package, 3, project: project) } + let_it_be_with_reload(:packages) { create_list(:nuget_package, 3, project: project) } let(:user) { project.owner } @@ -15,6 +15,17 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, fea describe '#execute' do subject { service.execute } + shared_examples 'returning service response' do |status:, message:, reason: nil| + it 'returns service response' do + subject + + expect(subject).to be_a(ServiceResponse) + expect(subject.status).to eq(status) + expect(subject.message).to eq(message) + expect(subject.reason).to eq(reason) if reason + end + end + context 'when the user is authorized' do before do project.add_maintainer(user) @@ -23,16 +34,16 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, fea context 'when it is successful' do it 'marks the packages as pending destruction' do expect(::Packages::Maven::Metadata::SyncService).not_to receive(:new) + expect(::Packages::Npm::CreateMetadataCacheService).not_to receive(:new) expect { subject }.to change { ::Packages::Package.pending_destruction.count }.from(0).to(3) .and change { Packages::PackageFile.pending_destruction.count }.from(0).to(3) packages.each { |pkg| expect(pkg.reload).to be_pending_destruction } - - expect(subject).to be_a(ServiceResponse) - expect(subject).to be_success - expect(subject.message).to eq('Packages were successfully marked as pending destruction') end + it_behaves_like 'returning service response', status: :success, + message: 'Packages were successfully marked as pending destruction' + context 'with maven packages' do let_it_be_with_reload(:packages) { create_list(:maven_package, 3, project: project) } @@ -42,12 +53,11 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, fea expect { subject }.to change { ::Packages::Package.pending_destruction.count }.from(0).to(3) .and change { Packages::PackageFile.pending_destruction.count }.from(0).to(9) packages.each { |pkg| expect(pkg.reload).to be_pending_destruction } - - expect(subject).to be_a(ServiceResponse) - expect(subject).to be_success - expect(subject.message).to eq('Packages were successfully marked as pending destruction') end + it_behaves_like 'returning service response', status: :success, + message: 'Packages were successfully marked as pending destruction' + context 'without version' do before do ::Packages::Package.id_in(package_ids).update_all(version: nil) @@ -59,12 +69,26 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, fea expect { subject }.to change { ::Packages::Package.pending_destruction.count }.from(0).to(3) .and change { Packages::PackageFile.pending_destruction.count }.from(0).to(9) packages.each { |pkg| expect(pkg.reload).to be_pending_destruction } - - expect(subject).to be_a(ServiceResponse) - expect(subject).to be_success - expect(subject.message).to eq('Packages were successfully marked as pending destruction') end + + it_behaves_like 'returning service response', status: :success, + message: 'Packages were successfully marked as pending destruction' + end + end + + context 'with npm packages' do + let_it_be_with_reload(:packages) { create_list(:npm_package, 3, project: project, name: 'test-package') } + + it 'marks the packages as pending destruction' do + expect(::Packages::Npm::CreateMetadataCacheService).to receive(:new).once.and_call_original + + expect { subject }.to change { ::Packages::Package.pending_destruction.count }.from(0).to(3) + .and change { Packages::PackageFile.pending_destruction.count }.from(0).to(3) + packages.each { |package| expect(package.reload).to be_pending_destruction } end + + it_behaves_like 'returning service response', status: :success, + message: 'Packages were successfully marked as pending destruction' end end @@ -73,7 +97,7 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, fea allow(service).to receive(:can_destroy_packages?).and_raise(StandardError, 'test') end - it 'returns an error ServiceResponse' do + it 'does not mark the packages as pending destruction' do expect(::Packages::Maven::Metadata::SyncService).not_to receive(:new) expect(Gitlab::ErrorTracking).to receive(:track_exception).with( @@ -83,30 +107,25 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, fea expect { subject }.to not_change { ::Packages::Package.pending_destruction.count } .and not_change { ::Packages::PackageFile.pending_destruction.count } - - expect(subject).to be_a(ServiceResponse) - expect(subject).to be_error - expect(subject.message).to eq("Failed to mark the packages as pending destruction") - expect(subject.status).to eq(:error) end + + it_behaves_like 'returning service response', status: :error, + message: 'Failed to mark the packages as pending destruction' end end context 'when the user is not authorized' do let(:user) { nil } - it 'returns an error ServiceResponse' do + it 'does not mark the packages as pending destruction' do expect(::Packages::Maven::Metadata::SyncService).not_to receive(:new) expect { subject }.to not_change { ::Packages::Package.pending_destruction.count } .and not_change { ::Packages::PackageFile.pending_destruction.count } - - expect(subject).to be_a(ServiceResponse) - expect(subject).to be_error - expect(subject.message).to eq("You don't have the permission to perform this action") - expect(subject.status).to eq(:error) - expect(subject.reason).to eq(:unauthorized) end + + it_behaves_like 'returning service response', status: :error, reason: :unauthorized, + message: "You don't have the permission to perform this action" end end end diff --git a/spec/services/service_desk/custom_email_verifications/update_service_spec.rb b/spec/services/service_desk/custom_email_verifications/update_service_spec.rb index 9a51f645b4d..103caf0e6c5 100644 --- a/spec/services/service_desk/custom_email_verifications/update_service_spec.rb +++ b/spec/services/service_desk/custom_email_verifications/update_service_spec.rb @@ -27,6 +27,9 @@ RSpec.describe ServiceDesk::CustomEmailVerifications::UpdateService, feature_cat before do allow(message_delivery).to receive(:deliver_later) allow(Notify).to receive(:service_desk_verification_result_email).and_return(message_delivery) + + stub_incoming_email_setting(enabled: true, address: 'support+%{key}@example.com') + stub_service_desk_email_setting(enabled: true, address: 'contact+%{key}@example.com') end shared_examples 'a failing verification process' do |expected_error_identifier| @@ -118,7 +121,34 @@ RSpec.describe ServiceDesk::CustomEmailVerifications::UpdateService, feature_cat verification.update!(token: 'ZROT4ZZXA-Y6') # token from email fixture end - let(:email_raw) { email_fixture('emails/service_desk_custom_email_address_verification.eml') } + let(:service_desk_address) { project.service_desk_incoming_address } + let(:verification_address) { 'custom-support-email+verify@example.com' } + let(:verification_token) { 'ZROT4ZZXA-Y6' } + let(:shared_email_raw) do + <<~EMAIL + From: Flight Support <custom-support-email@example.com> + Subject: Verify custom email address custom-support-email@example.com for Flight + Auto-Submitted: no + + + This email is auto-generated. It verifies the ownership of the entered Service Desk custom email address and + correct functionality of email forwarding. + + Verification token: #{verification_token} + -- + + You're receiving this email because of your account on 127.0.0.1. + EMAIL + end + + let(:email_raw) do + <<~EMAIL + Delivered-To: #{service_desk_address} + To: #{verification_address} + #{shared_email_raw} + EMAIL + end + let(:mail_object) { Mail::Message.new(email_raw) } it 'verifies and sends result emails' do @@ -160,6 +190,38 @@ RSpec.describe ServiceDesk::CustomEmailVerifications::UpdateService, feature_cat it_behaves_like 'a failing verification process', 'mail_not_received_within_timeframe' end + context 'and service desk address from service_desk_email was used as forwarding target' do + let(:service_desk_address) { project.service_desk_alias_address } + + it_behaves_like 'a failing verification process', 'incorrect_forwarding_target' + + context 'when multiple Delivered-To headers are present' do + let(:email_raw) do + <<~EMAIL + Delivered-To: other@example.com + Delivered-To: #{service_desk_address} + To: #{verification_address} + #{shared_email_raw} + EMAIL + end + + it_behaves_like 'a failing verification process', 'incorrect_forwarding_target' + end + + context 'when multiple To headers are present' do + # Microsoft Exchange forwards emails this way when forwarding + # to an external email address using a transport rule + let(:email_raw) do + <<~EMAIL + To: #{service_desk_address}, #{verification_address} + #{shared_email_raw} + EMAIL + end + + it_behaves_like 'a failing verification process', 'incorrect_forwarding_target' + end + end + context 'when already verified' do let(:expected_error_message) { error_already_finished } diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index d6fa1d60dad..fe84a80dae6 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -8148,7 +8148,6 @@ - './spec/requests/api/users_spec.rb' - './spec/requests/api/wikis_spec.rb' - './spec/requests/concerns/planning_hierarchy_spec.rb' -- './spec/requests/content_security_policy_spec.rb' - './spec/requests/dashboard_controller_spec.rb' - './spec/requests/dashboard/projects_controller_spec.rb' - './spec/requests/git_http_spec.rb' diff --git a/spec/support/shared_examples/controllers/base_action_controller_shared_examples.rb b/spec/support/shared_examples/controllers/base_action_controller_shared_examples.rb new file mode 100644 index 00000000000..2eab533ef7f --- /dev/null +++ b/spec/support/shared_examples/controllers/base_action_controller_shared_examples.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +# Requires `request` subject to be defined +# +# subject(:request) { get root_path } +RSpec.shared_examples 'Base action controller' do + describe 'security headers' do + describe 'Cross-Security-Policy' do + context 'when configuring snowplow' do + let(:snowplow_host) { 'snowplow.example.com' } + + shared_examples 'snowplow is not in the CSP' do + it 'does not add the snowplow collector hostname to the CSP' do + request + + expect(response.headers['Content-Security-Policy']).not_to include(snowplow_host) + end + end + + context 'when snowplow is enabled' do + before do + stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: snowplow_host) + end + + it 'adds snowplow to the csp' do + request + + expect(response.headers['Content-Security-Policy']).to include(snowplow_host) + end + end + + context 'when snowplow is enabled but host is not configured' do + before do + stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: nil) + end + + it_behaves_like 'snowplow is not in the CSP' + end + + context 'when snowplow is disabled' do + before do + stub_application_setting(snowplow_enabled: false, snowplow_collector_hostname: snowplow_host) + end + + it_behaves_like 'snowplow is not in the CSP' + end + end + + context 'when configuring vite' do + let(:vite_origin) { "#{ViteRuby.instance.config.host}:#{ViteRuby.instance.config.port}" } + + context 'when vite enabled during development', + skip: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424334' do + before do + stub_rails_env('development') + stub_feature_flags(vite: true) + end + + it 'adds vite csp' do + request + + expect(response.headers['Content-Security-Policy']).to include(vite_origin) + end + end + + context 'when vite disabled' do + before do + stub_feature_flags(vite: false) + end + + it "doesn't add vite csp" do + request + + expect(response.headers['Content-Security-Policy']).not_to include(vite_origin) + end + end + end + end + end +end |