diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-14 18:08:43 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-14 18:08:43 +0300 |
commit | 9b8269e5708ba1c38610189f84cf7224b640c0ed (patch) | |
tree | 70916a0afcfd90ed5425a80bab7f6bedca13d622 /spec | |
parent | 7a124e225ea58c2a432dd29f82ba682963886383 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
23 files changed, 421 insertions, 198 deletions
diff --git a/spec/components/pajamas/alert_component_spec.rb b/spec/components/pajamas/alert_component_spec.rb index db425fb2dce..1e2845c44a8 100644 --- a/spec/components/pajamas/alert_component_spec.rb +++ b/spec/components/pajamas/alert_component_spec.rb @@ -50,10 +50,12 @@ RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do before do render_inline described_class.new( title: '_title_', - alert_class: '_alert_class_', - alert_data: { - feature_id: '_feature_id_', - dismiss_endpoint: '_dismiss_endpoint_' + alert_options: { + class: '_alert_class_', + data: { + feature_id: '_feature_id_', + dismiss_endpoint: '_dismiss_endpoint_' + } } ) end @@ -106,9 +108,11 @@ RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do context 'with dismissible content' do before do render_inline described_class.new( - close_button_class: '_close_button_class_', - close_button_data: { - testid: '_close_button_testid_' + close_button_options: { + class: '_close_button_class_', + data: { + testid: '_close_button_testid_' + } } ) end @@ -138,35 +142,5 @@ RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do end end end - - context 'with alert_options' do - let(:options) { { alert_options: { id: 'test_id', class: 'baz', data: { foo: 'bar' } } } } - - before do - render_inline described_class.new(**options) - end - - it 'renders the extra options' do - expect(rendered_component).to have_css "#test_id.gl-alert.baz[data-foo='bar']" - end - - context 'with custom classes or data' do - let(:options) do - { - variant: :danger, - alert_class: 'custom', - alert_data: { foo: 'bar' }, - alert_options: { - class: 'extra special', - data: { foo: 'conflict' } - } - } - end - - it 'doesn\'t conflict with internal alert_class or alert_data' do - expect(rendered_component).to have_css ".extra.special.custom.gl-alert.gl-alert-danger[data-foo='bar']" - end - end - end end end diff --git a/spec/contracts/consumer/.eslintrc.yml b/spec/contracts/consumer/.eslintrc.yml new file mode 100644 index 00000000000..e4b380714d3 --- /dev/null +++ b/spec/contracts/consumer/.eslintrc.yml @@ -0,0 +1,7 @@ +--- +extends: + - 'plugin:@gitlab/jest' +settings: + import/core-modules: + - '@pact-foundation/pact' + - jest-pact diff --git a/spec/contracts/consumer/babel.config.json b/spec/contracts/consumer/babel.config.json new file mode 100644 index 00000000000..1320b9a3272 --- /dev/null +++ b/spec/contracts/consumer/babel.config.json @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env"] +} diff --git a/spec/contracts/consumer/endpoints/merge_requests.js b/spec/contracts/consumer/endpoints/merge_requests.js index 04e66c57dc9..ae4d5544df6 100644 --- a/spec/contracts/consumer/endpoints/merge_requests.js +++ b/spec/contracts/consumer/endpoints/merge_requests.js @@ -1,42 +1,34 @@ -'use strict'; +import { request } from 'axios'; -const axios = require('axios'); +export function getMetadata(endpoint) { + const { url } = endpoint; -exports.getMetadata = (endpoint) => { - const url = endpoint.url; + return request({ + method: 'GET', + baseURL: url, + url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json', + headers: { Accept: '*/*' }, + }).then((response) => response.data); +} - return axios - .request({ - method: 'GET', - baseURL: url, - url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json', - headers: { Accept: '*/*' }, - }) - .then((response) => response.data); -}; +export function getDiscussions(endpoint) { + const { url } = endpoint; -exports.getDiscussions = (endpoint) => { - const url = endpoint.url; + return request({ + method: 'GET', + baseURL: url, + url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json', + headers: { Accept: '*/*' }, + }).then((response) => response.data); +} - return axios - .request({ - method: 'GET', - baseURL: url, - url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json', - headers: { Accept: '*/*' }, - }) - .then((response) => response.data); -}; +export function getDiffs(endpoint) { + const { url } = endpoint; -exports.getDiffs = (endpoint) => { - const url = endpoint.url; - - return axios - .request({ - method: 'GET', - baseURL: url, - url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0', - headers: { Accept: '*/*' }, - }) - .then((response) => response.data); -}; + return request({ + method: 'GET', + baseURL: url, + url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0', + headers: { Accept: '*/*' }, + }).then((response) => response.data); +} diff --git a/spec/contracts/consumer/fixtures/diffs.fixture.js b/spec/contracts/consumer/fixtures/diffs.fixture.js index 7dbe825b093..cc2c054b08f 100644 --- a/spec/contracts/consumer/fixtures/diffs.fixture.js +++ b/spec/contracts/consumer/fixtures/diffs.fixture.js @@ -1,6 +1,6 @@ -'use strict'; +/* eslint-disable @gitlab/require-i18n-strings */ -const { Matchers } = require('@pact-foundation/pact'); +import { Matchers } from '@pact-foundation/pact'; const body = { diff_files: Matchers.eachLike({ @@ -48,9 +48,9 @@ const body = { context_lines_path: Matchers.string('/gitlab-qa-bot/...'), highlighted_diff_lines: Matchers.eachLike({ // The following values can also be null which is not supported - //line_code: Matchers.string('de3150c01c3a946a6168173c4116741379fe3579_1_1'), - //old_line: Matchers.integer(1), - //new_line: Matchers.integer(1), + // line_code: Matchers.string('de3150c01c3a946a6168173c4116741379fe3579_1_1'), + // old_line: Matchers.integer(1), + // new_line: Matchers.integer(1), text: Matchers.string('source'), rich_text: Matchers.string('<span></span>'), can_receive_suggestion: Matchers.boolean(true), @@ -70,7 +70,7 @@ const Diffs = { headers: { 'Content-Type': 'application/json; charset=utf-8', }, - body: body, + body, }, request: { @@ -86,4 +86,5 @@ const Diffs = { }, }; -exports.Diffs = Diffs; +export { Diffs }; +/* eslint-enable @gitlab/require-i18n-strings */ diff --git a/spec/contracts/consumer/fixtures/discussions.fixture.js b/spec/contracts/consumer/fixtures/discussions.fixture.js index c28b9bdd505..26f1d65f663 100644 --- a/spec/contracts/consumer/fixtures/discussions.fixture.js +++ b/spec/contracts/consumer/fixtures/discussions.fixture.js @@ -1,6 +1,6 @@ -'use strict'; +/* eslint-disable @gitlab/require-i18n-strings */ -const { Matchers } = require('@pact-foundation/pact'); +import { Matchers } from '@pact-foundation/pact'; const body = Matchers.eachLike({ id: Matchers.string('fd73763cbcbf7b29eb8765d969a38f7d735e222a'), @@ -67,7 +67,7 @@ const Discussions = { headers: { 'Content-Type': 'application/json; charset=utf-8', }, - body: body, + body, }, request: { @@ -82,4 +82,5 @@ const Discussions = { }, }; -exports.Discussions = Discussions; +export { Discussions }; +/* eslint-enable @gitlab/require-i18n-strings */ diff --git a/spec/contracts/consumer/fixtures/metadata.fixture.js b/spec/contracts/consumer/fixtures/metadata.fixture.js index 3fca6cf9181..c19ca2175b3 100644 --- a/spec/contracts/consumer/fixtures/metadata.fixture.js +++ b/spec/contracts/consumer/fixtures/metadata.fixture.js @@ -1,6 +1,6 @@ -'use strict'; +/* eslint-disable @gitlab/require-i18n-strings */ -const { Matchers } = require('@pact-foundation/pact'); +import { Matchers } from '@pact-foundation/pact'; const body = { real_size: Matchers.string('1'), @@ -78,7 +78,7 @@ const Metadata = { headers: { 'Content-Type': 'application/json; charset=utf-8', }, - body: body, + body, }, request: { @@ -93,4 +93,5 @@ const Metadata = { }, }; -exports.Metadata = Metadata; +export { Metadata }; +/* eslint-enable @gitlab/require-i18n-strings */ diff --git a/spec/contracts/consumer/package.json b/spec/contracts/consumer/package.json index 6a427ff8a7b..6d3feaa6d4c 100644 --- a/spec/contracts/consumer/package.json +++ b/spec/contracts/consumer/package.json @@ -13,5 +13,14 @@ }, "scripts": { "test": "jest --runInBand" + }, + "jest": { + "transform": { + "^.+\\.[t|j]sx?$": "babel-jest" + } + }, + "devDependencies": { + "@babel/preset-env": "^7.18.2", + "babel-jest": "^28.1.1" } } diff --git a/spec/contracts/consumer/specs/diffs.spec.js b/spec/contracts/consumer/specs/diffs.spec.js index b812461cb69..6b1cefdbdbc 100644 --- a/spec/contracts/consumer/specs/diffs.spec.js +++ b/spec/contracts/consumer/specs/diffs.spec.js @@ -1,9 +1,9 @@ -'use strict'; +/* eslint-disable @gitlab/require-i18n-strings */ -const { pactWith } = require('jest-pact'); +import { pactWith } from 'jest-pact'; -const { Diffs } = require('../fixtures/diffs.fixture'); -const { getDiffs } = require('../endpoints/merge_requests'); +import { Diffs } from '../fixtures/diffs.fixture'; +import { getDiffs } from '../endpoints/merge_requests'; pactWith( { @@ -34,3 +34,4 @@ pactWith( }); }, ); +/* eslint-enable @gitlab/require-i18n-strings */ diff --git a/spec/contracts/consumer/specs/discussions.spec.js b/spec/contracts/consumer/specs/discussions.spec.js index 304c240f7d3..2a5d0ba6267 100644 --- a/spec/contracts/consumer/specs/discussions.spec.js +++ b/spec/contracts/consumer/specs/discussions.spec.js @@ -1,9 +1,9 @@ -'use strict'; +/* eslint-disable @gitlab/require-i18n-strings */ -const { pactWith } = require('jest-pact'); +import { pactWith } from 'jest-pact'; -const { Discussions } = require('../fixtures/discussions.fixture'); -const { getDiscussions } = require('../endpoints/merge_requests'); +import { Discussions } from '../fixtures/discussions.fixture'; +import { getDiscussions } from '../endpoints/merge_requests'; pactWith( { @@ -34,3 +34,4 @@ pactWith( }); }, ); +/* eslint-enable @gitlab/require-i18n-strings */ diff --git a/spec/contracts/consumer/specs/metadata.spec.js b/spec/contracts/consumer/specs/metadata.spec.js index c49d4c354b1..fc082cb6a46 100644 --- a/spec/contracts/consumer/specs/metadata.spec.js +++ b/spec/contracts/consumer/specs/metadata.spec.js @@ -1,9 +1,9 @@ -'use strict'; +/* eslint-disable @gitlab/require-i18n-strings */ -const { pactWith } = require('jest-pact'); +import { pactWith } from 'jest-pact'; -const { Metadata } = require('../fixtures/metadata.fixture'); -const { getMetadata } = require('../endpoints/merge_requests'); +import { Metadata } from '../fixtures/metadata.fixture'; +import { getMetadata } from '../endpoints/merge_requests'; pactWith( { @@ -34,3 +34,4 @@ pactWith( }); }, ); +/* eslint-enable @gitlab/require-i18n-strings */ diff --git a/spec/frontend/ide/components/commit_sidebar/editor_header_spec.js b/spec/frontend/ide/components/commit_sidebar/editor_header_spec.js index 6e4c66cb780..d77e8e3d04c 100644 --- a/spec/frontend/ide/components/commit_sidebar/editor_header_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/editor_header_spec.js @@ -68,7 +68,7 @@ describe('IDE commit editor header', () => { it('calls discardFileChanges if dialog result is confirmed', () => { expect(store.dispatch).not.toHaveBeenCalled(); - findDiscardModal().vm.$emit('ok'); + findDiscardModal().vm.$emit('primary'); expect(store.dispatch).toHaveBeenCalledWith('discardFileChanges', TEST_FILE_PATH); }); diff --git a/spec/frontend/security_configuration/components/app_spec.js b/spec/frontend/security_configuration/components/app_spec.js index d7d46d0d415..de91e51924d 100644 --- a/spec/frontend/security_configuration/components/app_spec.js +++ b/spec/frontend/security_configuration/components/app_spec.js @@ -2,7 +2,6 @@ import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import { GlTab, GlTabs, GlLink } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; - import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser'; import stubChildren from 'helpers/stub_children'; @@ -20,22 +19,14 @@ import { LICENSE_COMPLIANCE_DESCRIPTION, LICENSE_COMPLIANCE_HELP_PATH, AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY, - LICENSE_ULTIMATE, - LICENSE_PREMIUM, - LICENSE_FREE, } from '~/security_configuration/components/constants'; import FeatureCard from '~/security_configuration/components/feature_card.vue'; import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import currentLicenseQuery from '~/security_configuration/graphql/current_license.query.graphql'; -import waitForPromises from 'helpers/wait_for_promises'; - import UpgradeBanner from '~/security_configuration/components/upgrade_banner.vue'; import { REPORT_TYPE_LICENSE_COMPLIANCE, REPORT_TYPE_SAST, } from '~/vue_shared/security_reports/constants'; -import { getCurrentLicensePlanResponse } from '../mock_data'; const upgradePath = '/upgrade'; const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath'; @@ -50,31 +41,16 @@ Vue.use(VueApollo); describe('App component', () => { let wrapper; let userCalloutDismissSpy; - let mockApollo; - const createComponent = ({ - shouldShowCallout = true, - licenseQueryResponse = LICENSE_ULTIMATE, - ...propsData - }) => { + const createComponent = ({ shouldShowCallout = true, ...propsData }) => { userCalloutDismissSpy = jest.fn(); - mockApollo = createMockApollo([ - [ - currentLicenseQuery, - jest - .fn() - .mockResolvedValue( - licenseQueryResponse instanceof Error - ? licenseQueryResponse - : getCurrentLicensePlanResponse(licenseQueryResponse), - ), - ], - ]); - wrapper = extendedWrapper( mount(SecurityConfigurationApp, { - propsData, + propsData: { + securityTrainingEnabled: true, + ...propsData, + }, provide: { upgradePath, autoDevopsHelpPagePath, @@ -82,7 +58,6 @@ describe('App component', () => { projectFullPath, vulnerabilityTrainingDocsPath, }, - apolloProvider: mockApollo, stubs: { ...stubChildren(SecurityConfigurationApp), GlLink: false, @@ -157,7 +132,6 @@ describe('App component', () => { afterEach(() => { wrapper.destroy(); - mockApollo = null; }); describe('basic structure', () => { @@ -166,7 +140,6 @@ describe('App component', () => { augmentedSecurityFeatures: securityFeaturesMock, augmentedComplianceFeatures: complianceFeaturesMock, }); - await waitForPromises(); }); it('renders main-heading with correct text', () => { @@ -469,47 +442,42 @@ describe('App component', () => { }); describe('Vulnerability management', () => { - beforeEach(async () => { + it('does not show tab if security training is disabled', () => { createComponent({ augmentedSecurityFeatures: securityFeaturesMock, augmentedComplianceFeatures: complianceFeaturesMock, + securityTrainingEnabled: false, }); - await waitForPromises(); - }); - it('renders TrainingProviderList component', () => { - expect(findTrainingProviderList().exists()).toBe(true); + expect(findVulnerabilityManagementTab().exists()).toBe(false); }); - it('renders security training description', () => { - expect(findVulnerabilityManagementTab().text()).toContain(i18n.securityTrainingDescription); - }); - - it('renders link to help docs', () => { - const trainingLink = findVulnerabilityManagementTab().findComponent(GlLink); - - expect(trainingLink.text()).toBe('Learn more about vulnerability training'); - expect(trainingLink.attributes('href')).toBe(vulnerabilityTrainingDocsPath); - }); - - it.each` - licenseQueryResponse | display - ${LICENSE_ULTIMATE} | ${true} - ${LICENSE_PREMIUM} | ${false} - ${LICENSE_FREE} | ${false} - ${null} | ${true} - ${new Error()} | ${true} - `( - 'displays $display for license $licenseQueryResponse', - async ({ licenseQueryResponse, display }) => { + describe('security training enabled', () => { + beforeEach(async () => { createComponent({ - licenseQueryResponse, augmentedSecurityFeatures: securityFeaturesMock, augmentedComplianceFeatures: complianceFeaturesMock, }); - await waitForPromises(); - expect(findVulnerabilityManagementTab().exists()).toBe(display); - }, - ); + }); + + it('shows the tab if security training is enabled', () => { + expect(findVulnerabilityManagementTab().exists()).toBe(true); + }); + + it('renders TrainingProviderList component', () => { + expect(findTrainingProviderList().exists()).toBe(true); + }); + + it('renders security training description', () => { + expect(findVulnerabilityManagementTab().text()).toContain(i18n.securityTrainingDescription); + }); + + it('renders link to help docs', () => { + const trainingLink = findVulnerabilityManagementTab().findComponent(GlLink); + + expect(trainingLink.text()).toBe('Learn more about vulnerability training'); + expect(trainingLink.attributes('href')).toBe(vulnerabilityTrainingDocsPath); + }); + }); }); }); diff --git a/spec/frontend/security_configuration/mock_data.js b/spec/frontend/security_configuration/mock_data.js index 94a36472a1d..18a480bf082 100644 --- a/spec/frontend/security_configuration/mock_data.js +++ b/spec/frontend/security_configuration/mock_data.js @@ -111,12 +111,3 @@ export const tempProviderLogos = { svg: `<svg>${[testProviderName[1]]}</svg>`, }, }; - -export const getCurrentLicensePlanResponse = (plan) => ({ - data: { - currentLicense: { - id: 'gid://gitlab/License/1', - plan, - }, - }, -}); diff --git a/spec/frontend/sidebar/components/attention_requested_toggle_spec.js b/spec/frontend/sidebar/components/attention_requested_toggle_spec.js index 959fa799eb7..58fa878a189 100644 --- a/spec/frontend/sidebar/components/attention_requested_toggle_spec.js +++ b/spec/frontend/sidebar/components/attention_requested_toggle_spec.js @@ -41,18 +41,18 @@ describe('Attention require toggle', () => { ); it.each` - attentionRequested | variant - ${true} | ${'warning'} - ${false} | ${'default'} + attentionRequested | selected + ${true} | ${true} + ${false} | ${false} `( - 'renders button with variant $variant when attention_requested is $attentionRequested', - ({ attentionRequested, variant }) => { + 'renders button with as selected when $selected when attention_requested is $attentionRequested', + ({ attentionRequested, selected }) => { factory({ type: 'reviewer', user: { attention_requested: attentionRequested, can_update_merge_request: true }, }); - expect(findToggle().props('variant')).toBe(variant); + expect(findToggle().props('selected')).toBe(selected); }, ); diff --git a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js index 82526af7afa..01fbcb2154f 100644 --- a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js @@ -42,8 +42,8 @@ describe('Merge Request Collapsible Extension', () => { expect(wrapper.find('[data-testid="collapsed-header"]').text()).toBe('hello there'); }); - it('renders angle-right icon', () => { - expect(findIcon().props('name')).toBe('angle-right'); + it('renders chevron-lg-right icon', () => { + expect(findIcon().props('name')).toBe('chevron-lg-right'); }); describe('onClick', () => { @@ -60,8 +60,8 @@ describe('Merge Request Collapsible Extension', () => { expect(findTitle().text()).toBe('Collapse'); }); - it('renders angle-down icon', () => { - expect(findIcon().props('name')).toBe('angle-down'); + it('renders chevron-lg-down icon', () => { + expect(findIcon().props('name')).toBe('chevron-lg-down'); }); }); }); diff --git a/spec/helpers/form_helper_spec.rb b/spec/helpers/form_helper_spec.rb index 25dfa2251c3..c9c8c6b13b6 100644 --- a/spec/helpers/form_helper_spec.rb +++ b/spec/helpers/form_helper_spec.rb @@ -18,7 +18,7 @@ RSpec.describe FormHelper do expect(helper.form_errors(model, pajamas_alert: true)) .to include( - '<div class="gl-alert gl-alert-danger gl-alert-not-dismissible gl-mb-5" id="error_explanation" role="alert">' + '<div class="gl-alert gl-mb-5 gl-alert-danger gl-alert-not-dismissible" id="error_explanation" role="alert">' ) end diff --git a/spec/lib/gitlab/audit/unauthenticated_author_spec.rb b/spec/lib/gitlab/audit/unauthenticated_author_spec.rb index 4e5c477fc2a..70716ee7f4c 100644 --- a/spec/lib/gitlab/audit/unauthenticated_author_spec.rb +++ b/spec/lib/gitlab/audit/unauthenticated_author_spec.rb @@ -13,5 +13,11 @@ RSpec.describe Gitlab::Audit::UnauthenticatedAuthor do expect(described_class.new) .to have_attributes(id: -1, name: 'An unauthenticated user') end + + describe '#impersonated?' do + it 'returns false' do + expect(described_class.new.impersonated?).to be(false) + end + end end end diff --git a/spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb b/spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb new file mode 100644 index 00000000000..4810d55b541 --- /dev/null +++ b/spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rspec-parameterized' + +RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do + let(:dummy) { Class.new { include Gitlab::Diff::Rendered::Notebook::DiffFileHelper }.new } + + describe '#strip_diff_frontmatter' do + using RSpec::Parameterized::TableSyntax + + subject { dummy.strip_diff_frontmatter(diff) } + + where(:diff, :result) do + "FileLine1\nFileLine2\n@@ -1,76 +1,74 @@\nhello\n" | "@@ -1,76 +1,74 @@\nhello\n" + "" | nil + nil | nil + end + + with_them do + it { is_expected.to eq(result) } + end + end + + describe '#map_transformed_line_to_source' do + using RSpec::Parameterized::TableSyntax + + subject { dummy.map_transformed_line_to_source(1, transformed_blocks) } + + where(:case, :transformed_blocks, :result) do + 'if transformed diff is empty' | [] | 0 + 'if the transformed line does not map to any in the original file' | [{ source_line: nil }] | 0 + 'if the transformed line maps to a line in the source file' | [{ source_line: 2 }] | 3 + end + + with_them do + it { is_expected.to eq(result) } + end + end + + describe '#map_diff_block_to_source_line' do + let(:file_added) { false } + let(:file_deleted) { false } + let(:old_positions) { [1] } + let(:new_positions) { [1] } + let(:lines) { old_positions.zip(new_positions).map { |old, new| Gitlab::Diff::Line.new("", "", 0, old, new) } } + + subject { dummy.map_diff_block_to_source_line(lines, file_added, file_deleted)} + + context 'only additions' do + let(:old_positions) { [1, 2, 2, 2] } + let(:new_positions) { [1, 2, 3, 4] } + + it 'computes the removals correctly' do + expect(subject[0]).to eq({ 1 => 1, 2 => 4 }) + end + + it 'computes the additions correctly' do + expect(subject[1]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 }) + end + end + + context 'only additions' do + let(:old_positions) { [1, 2, 3, 4] } + let(:new_positions) { [1, 2, 2, 2] } + + it 'computes the removals correctly' do + expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 }) + end + + it 'computes the additions correctly' do + expect(subject[1]).to eq({ 1 => 1, 2 => 4 }) + end + end + + context 'with additions and removals' do + let(:old_positions) { [1, 2, 3, 4, 4, 4] } + let(:new_positions) { [1, 2, 2, 2, 3, 4] } + + it 'computes the removals correctly' do + expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 4 }) + end + + it 'computes the additions correctly' do + expect(subject[1]).to eq({ 1 => 1, 2 => 4, 3 => 4, 4 => 4 }) + end + end + + context 'is new file' do + let(:file_added) { true } + + it 'removals is empty' do + expect(subject[0]).to be_empty + end + end + + context 'is deleted file' do + let(:file_deleted) { true } + + it 'additions is empty' do + expect(subject[1]).to be_empty + end + end + end + + describe '#image_as_rich_text' do + let(:img) { 'data:image/png;base64,some_image_here' } + let(:line_text) { " ![](#{img})"} + + subject { dummy.image_as_rich_text(line_text) } + + context 'text does not contain image' do + let(:img) { "not an image" } + + it { is_expected.to be_nil } + end + + context 'text contains image' do + it { is_expected.to eq("<img src=\"#{img}\">") } + end + + context 'text contains image that has malicious html' do + let(:img) { 'data:image/png;base64,some_image_here"<div>Hello</div>' } + + it 'sanitizes the html' do + expect(subject).not_to include('<div>Hello') + end + + it 'adds image to src' do + expect(subject).to end_with('/div>">') + end + end + end +end diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb index ac1d1f5d74d..b7da9b27e19 100644 --- a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb +++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb @@ -4,15 +4,30 @@ require 'spec_helper' RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountImportedProjectsMetric do let_it_be(:user) { create(:user) } - let_it_be(:gitea_imports) do - create_list(:project, 3, import_type: 'gitea', creator_id: user.id, created_at: 3.weeks.ago) + + # Project records have to be created chronologically, because of + # metric SQL query optimizations that rely on the fact that `id`s + # increment monotonically over time. + # + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89701 + let_it_be(:old_import) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 2.months.ago) } + let_it_be(:gitea_import_1) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 21.days.ago) } + + let_it_be(:gitea_import_2) do + create(:project, import_type: 'gitea', creator_id: user.id, created_at: 20.days.ago) end - let_it_be(:bitbucket_imports) do - create_list(:project, 2, import_type: 'bitbucket', creator_id: user.id, created_at: 3.weeks.ago) + let_it_be(:gitea_import_3) do + create(:project, import_type: 'gitea', creator_id: user.id, created_at: 19.days.ago) end - let_it_be(:old_import) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 2.months.ago) } + let_it_be(:bitbucket_import_1) do + create(:project, import_type: 'bitbucket', creator_id: user.id, created_at: 2.weeks.ago) + end + + let_it_be(:bitbucket_import_2) do + create(:project, import_type: 'bitbucket', creator_id: user.id, created_at: 1.week.ago) + end context 'with import_type gitea' do context 'with all time frame' do @@ -53,7 +68,7 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountImportedProjectsMe options: { import_type: 'bitbucket' } end - context 'for 28d time frame', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/362591' do + context 'for 28d time frame' do let(:expected_value) { 2 } let(:start) { 30.days.ago.to_s(:db) } let(:finish) { 2.days.ago.to_s(:db) } diff --git a/spec/models/concerns/as_cte_spec.rb b/spec/models/concerns/as_cte_spec.rb index c92d46ef25f..06d9650ec46 100644 --- a/spec/models/concerns/as_cte_spec.rb +++ b/spec/models/concerns/as_cte_spec.rb @@ -21,7 +21,7 @@ RSpec.describe AsCte do it { expect(subject.query).to eq(query) } it { expect(subject.table.name).to eq(name.to_s) } - context 'with materialized parameter' do + context 'with materialized parameter', if: Gitlab::Database::AsWithMaterialized.materialized_supported? do subject { query.as_cte(name, materialized: materialized).to_arel.to_sql } context 'as true' do diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 91008dd57e7..44646b02df0 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -407,17 +407,32 @@ RSpec.describe 'Query.runner(id)' do <<~SINGLE runner(id: "#{runner.to_global_id}") { #{all_graphql_fields_for('CiRunner', excluded: excluded_fields)} + groups { + nodes { + id + } + } + projects { + nodes { + id + } + } } SINGLE end + let(:active_project_runner2) { create(:ci_runner, :project) } + let(:active_group_runner2) { create(:ci_runner, :group) } + # Currently excluding a known N+1 issue, see https://gitlab.com/gitlab-org/gitlab/-/issues/334759 - let(:excluded_fields) { %w[jobCount] } + let(:excluded_fields) { %w[jobCount groups projects] } let(:single_query) do <<~QUERY { - active: #{runner_query(active_instance_runner)} + instance_runner1: #{runner_query(active_instance_runner)} + project_runner1: #{runner_query(active_project_runner)} + group_runner1: #{runner_query(active_group_runner)} } QUERY end @@ -425,22 +440,49 @@ RSpec.describe 'Query.runner(id)' do let(:double_query) do <<~QUERY { - active: #{runner_query(active_instance_runner)} - inactive: #{runner_query(inactive_instance_runner)} + instance_runner1: #{runner_query(active_instance_runner)} + instance_runner2: #{runner_query(inactive_instance_runner)} + group_runner1: #{runner_query(active_group_runner)} + group_runner2: #{runner_query(active_group_runner2)} + project_runner1: #{runner_query(active_project_runner)} + project_runner2: #{runner_query(active_project_runner2)} } QUERY end it 'does not execute more queries per runner', :aggregate_failures do # warm-up license cache and so on: - post_graphql(single_query, current_user: user) + post_graphql(double_query, current_user: user) control = ActiveRecord::QueryRecorder.new { post_graphql(single_query, current_user: user) } expect { post_graphql(double_query, current_user: user) } .not_to exceed_query_limit(control) - expect(graphql_data_at(:active)).not_to be_nil - expect(graphql_data_at(:inactive)).not_to be_nil + + expect(graphql_data.count).to eq 6 + expect(graphql_data).to match( + a_hash_including( + 'instance_runner1' => a_hash_including('id' => active_instance_runner.to_global_id.to_s), + 'instance_runner2' => a_hash_including('id' => inactive_instance_runner.to_global_id.to_s), + 'group_runner1' => a_hash_including( + 'id' => active_group_runner.to_global_id.to_s, + 'groups' => { 'nodes' => [a_hash_including('id' => group.to_global_id.to_s)] } + ), + 'group_runner2' => a_hash_including( + 'id' => active_group_runner2.to_global_id.to_s, + 'groups' => { 'nodes' => [a_hash_including('id' => active_group_runner2.groups[0].to_global_id.to_s)] } + ), + 'project_runner1' => a_hash_including( + 'id' => active_project_runner.to_global_id.to_s, + 'projects' => { 'nodes' => [a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)] } + ), + 'project_runner2' => a_hash_including( + 'id' => active_project_runner2.to_global_id.to_s, + 'projects' => { + 'nodes' => [a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)] + } + ) + )) end end end diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb index 00326426af5..2345c0063dd 100644 --- a/spec/requests/api/release/links_spec.rb +++ b/spec/requests/api/release/links_spec.rb @@ -49,6 +49,20 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/links') end + + context 'when using JOB-TOKEN auth' do + let(:job) { create(:ci_build, :running, user: maintainer) } + + it 'returns releases links' do + get api("/projects/#{project.id}/releases/v0.1/assets/links", job_token: job.token) + + aggregate_failures "testing response" do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('release/links') + expect(json_response.count).to eq(2) + end + end + end end context 'when release does not exist' do @@ -116,6 +130,20 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'when using JOB-TOKEN auth' do + let(:job) { create(:ci_build, :running, user: maintainer) } + + it 'returns releases link' do + get api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", job_token: job.token) + + aggregate_failures "testing response" do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('release/link') + expect(json_response['name']).to eq(release_link.name) + end + end + end + context 'when specified tag is not found in the project' do it_behaves_like '404 response' do let(:request) { get api("/projects/#{project.id}/releases/non_existing_tag/assets/links/#{release_link.id}", maintainer) } @@ -198,6 +226,25 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'when using JOB-TOKEN auth' do + let(:job) { create(:ci_build, :running, user: maintainer) } + + it 'creates a new release link' do + expect do + post api("/projects/#{project.id}/releases/v0.1/assets/links"), params: params.merge(job_token: job.token) + end.to change { Releases::Link.count }.by(1) + + release.reload + + aggregate_failures "testing response" do + expect(response).to have_gitlab_http_status(:created) + expect(last_release_link.name).to eq('awesome-app.dmg') + expect(last_release_link.filepath).to eq('/binaries/awesome-app.dmg') + expect(last_release_link.url).to eq('https://example.com/download/awesome-app.dmg') + end + end + end + context 'with protected tag' do context 'when user has access to the protected tag' do let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } @@ -314,6 +361,20 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'when using JOB-TOKEN auth' do + let(:job) { create(:ci_build, :running, user: maintainer) } + + it 'updates the release link' do + put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}"), params: params.merge(job_token: job.token) + + aggregate_failures "testing response" do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('release/link') + expect(json_response['name']).to eq('awesome-app.msi') + end + end + end + context 'with protected tag' do context 'when user has access to the protected tag' do let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } @@ -411,6 +472,21 @@ RSpec.describe API::Release::Links do expect(response).to match_response_schema('release/link') end + context 'when using JOB-TOKEN auth' do + let(:job) { create(:ci_build, :running, user: maintainer) } + + it 'deletes the release link' do + expect do + delete api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", job_token: job.token) + end.to change { Releases::Link.count }.by(-1) + + aggregate_failures "testing response" do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('release/link') + end + end + end + context 'with protected tag' do context 'when user has access to the protected tag' do let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } |