diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-12 18:09:37 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-12 18:09:37 +0300 |
commit | 2c89e169769ead722394a79ed67fcd08e96863dd (patch) | |
tree | 0dadb576846c484475b895f75fab41f71cdb952e /spec | |
parent | bd497e352ebd279536ae11855871162e82a3f88c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
36 files changed, 714 insertions, 196 deletions
diff --git a/spec/controllers/projects/alerting/notifications_controller_spec.rb b/spec/controllers/projects/alerting/notifications_controller_spec.rb new file mode 100644 index 00000000000..a56ac59215f --- /dev/null +++ b/spec/controllers/projects/alerting/notifications_controller_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Alerting::NotificationsController do + let_it_be(:project) { create(:project) } + let_it_be(:environment) { create(:environment, project: project) } + + describe 'POST #create' do + let(:service_response) { ServiceResponse.success } + let(:notify_service) { instance_double(Projects::Alerting::NotifyService, execute: service_response) } + + around do |example| + ForgeryProtection.with_forgery_protection { example.run } + end + + before do + allow(Projects::Alerting::NotifyService).to receive(:new).and_return(notify_service) + end + + def make_request(body = {}) + post :create, params: project_params, body: body.to_json, as: :json + end + + context 'when notification service succeeds' do + let(:payload) do + { + title: 'Alert title', + hosts: 'https://gitlab.com' + } + end + + let(:permitted_params) { ActionController::Parameters.new(payload).permit! } + + it 'responds with ok' do + make_request + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'does not pass excluded parameters to the notify service' do + make_request(payload) + + expect(Projects::Alerting::NotifyService) + .to have_received(:new) + .with(project, nil, permitted_params) + end + end + + context 'when notification service fails' do + let(:service_response) { ServiceResponse.error(message: 'Unauthorized', http_status: 401) } + + it 'responds with the service response' do + make_request + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + + context 'bearer token' do + context 'when set' do + it 'extracts bearer token' do + request.headers['HTTP_AUTHORIZATION'] = 'Bearer some token' + + expect(notify_service).to receive(:execute).with('some token') + + make_request + end + + it 'pass nil if cannot extract a non-bearer token' do + request.headers['HTTP_AUTHORIZATION'] = 'some token' + + expect(notify_service).to receive(:execute).with(nil) + + make_request + end + end + + context 'when missing' do + it 'passes nil' do + expect(notify_service).to receive(:execute).with(nil) + + make_request + end + end + end + end + + def project_params(opts = {}) + opts.reverse_merge(namespace_id: project.namespace, project_id: project) + end +end diff --git a/spec/factories/services.rb b/spec/factories/services.rb index 5d62b3cb9c9..ffa51abf26f 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -44,6 +44,16 @@ FactoryBot.define do end end + factory :alerts_service do + project + type { 'AlertsService' } + active { true } + + trait :inactive do + active { false } + end + end + factory :drone_ci_service do project active { true } diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb index 562dbeb816a..6b2a9a6b852 100644 --- a/spec/features/projects/graph_spec.rb +++ b/spec/features/projects/graph_spec.rb @@ -22,20 +22,12 @@ describe 'Project Graph', :js do end end - shared_examples 'page should have languages graphs' do - it 'renders languages' do - expect(page).to have_content(/Ruby 66.* %/) - expect(page).to have_content(/JavaScript 22.* %/) - end - end - context 'commits graph' do before do visit commits_project_graph_path(project, 'master') end it_behaves_like 'page should have commits graphs' - it_behaves_like 'page should have languages graphs' end context 'languages graph' do @@ -44,7 +36,6 @@ describe 'Project Graph', :js do end it_behaves_like 'page should have commits graphs' - it_behaves_like 'page should have languages graphs' end context 'charts graph' do @@ -53,7 +44,6 @@ describe 'Project Graph', :js do end it_behaves_like 'page should have commits graphs' - it_behaves_like 'page should have languages graphs' end context 'chart graph with HTML escaped branch name' do diff --git a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap index b77ca28b9d8..2878ad492a4 100644 --- a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap +++ b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap @@ -12,13 +12,12 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = ` class="file-actions d-none d-sm-block" > <viewer-switcher-stub - activeviewer="rich" - blob="[object Object]" + value="simple" /> <default-actions-stub - activeviewer="rich" - blob="[object Object]" + activeviewer="simple" + rawpath="/flightjs/flight/snippets/51/raw" /> </div> </div> diff --git a/spec/frontend/blob/components/blob_header_default_actions_spec.js b/spec/frontend/blob/components/blob_header_default_actions_spec.js index fe0edffd12d..5da0d40ab14 100644 --- a/spec/frontend/blob/components/blob_header_default_actions_spec.js +++ b/spec/frontend/blob/components/blob_header_default_actions_spec.js @@ -8,7 +8,6 @@ import { } from '~/blob/components/constants'; import { GlButtonGroup, GlButton } from '@gitlab/ui'; import { Blob } from './mock_data'; -import eventHub from '~/blob/event_hub'; describe('Blob Header Default Actions', () => { let wrapper; @@ -16,10 +15,10 @@ describe('Blob Header Default Actions', () => { let buttons; const hrefPrefix = 'http://localhost'; - function createComponent(blobProps = {}, propsData = {}) { + function createComponent(propsData = {}) { wrapper = mount(BlobHeaderActions, { propsData: { - blob: Object.assign({}, Blob, blobProps), + rawPath: Blob.rawPath, ...propsData, }, }); @@ -60,12 +59,9 @@ describe('Blob Header Default Actions', () => { }); it('renders "Copy file contents" button as disables if the viewer is Rich', () => { - createComponent( - {}, - { - activeViewer: RICH_BLOB_VIEWER, - }, - ); + createComponent({ + activeViewer: RICH_BLOB_VIEWER, + }); buttons = wrapper.findAll(GlButton); expect(buttons.at(0).attributes('disabled')).toBeTruthy(); @@ -74,10 +70,10 @@ describe('Blob Header Default Actions', () => { describe('functionally', () => { it('emits an event when a Copy Contents button is clicked', () => { - jest.spyOn(eventHub, '$emit'); + jest.spyOn(wrapper.vm, '$emit'); buttons.at(0).vm.$emit('click'); - expect(eventHub.$emit).toHaveBeenCalledWith('copy'); + expect(wrapper.vm.$emit).toHaveBeenCalledWith('copy'); }); }); }); diff --git a/spec/frontend/blob/components/blob_header_spec.js b/spec/frontend/blob/components/blob_header_spec.js index 7d1443fb069..d410ef10fc9 100644 --- a/spec/frontend/blob/components/blob_header_spec.js +++ b/spec/frontend/blob/components/blob_header_spec.js @@ -3,7 +3,6 @@ import BlobHeader from '~/blob/components/blob_header.vue'; import ViewerSwitcher from '~/blob/components/blob_header_viewer_switcher.vue'; import DefaultActions from '~/blob/components/blob_header_default_actions.vue'; import BlobFilepath from '~/blob/components/blob_header_filepath.vue'; -import eventHub from '~/blob/event_hub'; import { Blob } from './mock_data'; @@ -21,10 +20,6 @@ describe('Blob Header Default Actions', () => { }); } - beforeEach(() => { - createComponent(); - }); - afterEach(() => { wrapper.destroy(); }); @@ -96,37 +91,48 @@ describe('Blob Header Default Actions', () => { describe('functionality', () => { const newViewer = 'Foo Bar'; + const activeViewerType = 'Alpha Beta'; - it('listens to "switch-view" event when viewer switcher is shown and updates activeViewer', () => { - expect(wrapper.vm.showViewerSwitcher).toBe(true); - eventHub.$emit('switch-viewer', newViewer); - - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.activeViewer).toBe(newViewer); - }); - }); - - it('does not update active viewer if the switcher is not shown', () => { - const activeViewer = 'Alpha Beta'; + const factory = (hideViewerSwitcher = false) => { createComponent( {}, + {}, { - data() { - return { - activeViewer, - }; - }, - }, - { - hideViewerSwitcher: true, + activeViewerType, + hideViewerSwitcher, }, ); + }; + + it('by default sets viewer data based on activeViewerType', () => { + factory(); + expect(wrapper.vm.viewer).toBe(activeViewerType); + }); + + it('sets viewer to null if the viewer switcher should be hidden', () => { + factory(true); + expect(wrapper.vm.viewer).toBe(null); + }); + + it('watches the changes in viewer data and emits event when the change is registered', () => { + factory(); + jest.spyOn(wrapper.vm, '$emit'); + wrapper.vm.viewer = newViewer; + + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.vm.$emit).toHaveBeenCalledWith('viewer-changed', newViewer); + }); + }); + + it('does not emit event if the switcher is not rendered', () => { + factory(true); expect(wrapper.vm.showViewerSwitcher).toBe(false); - eventHub.$emit('switch-viewer', newViewer); + jest.spyOn(wrapper.vm, '$emit'); + wrapper.vm.viewer = newViewer; return wrapper.vm.$nextTick().then(() => { - expect(wrapper.vm.activeViewer).toBe(activeViewer); + expect(wrapper.vm.$emit).not.toHaveBeenCalled(); }); }); }); diff --git a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js index 88e9eeea994..f1a7ac8b21a 100644 --- a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js +++ b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js @@ -7,18 +7,13 @@ import { SIMPLE_BLOB_VIEWER_TITLE, } from '~/blob/components/constants'; import { GlButtonGroup, GlButton } from '@gitlab/ui'; -import { Blob } from './mock_data'; -import eventHub from '~/blob/event_hub'; describe('Blob Header Viewer Switcher', () => { let wrapper; - function createComponent(blobProps = {}, propsData = {}) { + function createComponent(propsData = {}) { wrapper = mount(BlobHeaderViewerSwitcher, { - propsData: { - blob: Object.assign({}, Blob, blobProps), - ...propsData, - }, + propsData, }); } @@ -29,7 +24,7 @@ describe('Blob Header Viewer Switcher', () => { describe('intiialization', () => { it('is initialized with simple viewer as active', () => { createComponent(); - expect(wrapper.vm.activeViewer).toBe(SIMPLE_BLOB_VIEWER); + expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER); }); }); @@ -60,42 +55,42 @@ describe('Blob Header Viewer Switcher', () => { let simpleBtn; let richBtn; - function factory(propsOptions = {}) { - createComponent({}, propsOptions); + function factory(propsData = {}) { + createComponent(propsData); buttons = wrapper.findAll(GlButton); simpleBtn = buttons.at(0); richBtn = buttons.at(1); - jest.spyOn(eventHub, '$emit'); + jest.spyOn(wrapper.vm, '$emit'); } it('does not switch the viewer if the selected one is already active', () => { factory(); - expect(wrapper.vm.activeViewer).toBe(SIMPLE_BLOB_VIEWER); + expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER); simpleBtn.vm.$emit('click'); - expect(wrapper.vm.activeViewer).toBe(SIMPLE_BLOB_VIEWER); - expect(eventHub.$emit).not.toHaveBeenCalled(); + expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER); + expect(wrapper.vm.$emit).not.toHaveBeenCalled(); }); it('emits an event when a Rich Viewer button is clicked', () => { factory(); - expect(wrapper.vm.activeViewer).toBe(SIMPLE_BLOB_VIEWER); + expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER); richBtn.vm.$emit('click'); return wrapper.vm.$nextTick().then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('switch-viewer', RICH_BLOB_VIEWER); + expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', RICH_BLOB_VIEWER); }); }); it('emits an event when a Simple Viewer button is clicked', () => { factory({ - activeViewer: RICH_BLOB_VIEWER, + value: RICH_BLOB_VIEWER, }); simpleBtn.vm.$emit('click'); return wrapper.vm.$nextTick().then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('switch-viewer', SIMPLE_BLOB_VIEWER); + expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', SIMPLE_BLOB_VIEWER); }); }); }); diff --git a/spec/frontend/ide/ide_router_extension_spec.js b/spec/frontend/ide/ide_router_extension_spec.js new file mode 100644 index 00000000000..3e29ecc4a90 --- /dev/null +++ b/spec/frontend/ide/ide_router_extension_spec.js @@ -0,0 +1,48 @@ +import VueRouter from 'vue-router'; +import IdeRouter from '~/ide/ide_router_extension'; + +jest.mock('vue-router'); + +describe('IDE overrides of VueRouter', () => { + const paths = branch => [ + `${branch}`, + `/${branch}`, + `/${branch}/-/`, + `/edit/${branch}`, + `/edit/${branch}/-/`, + `/blob/${branch}`, + `/blob/${branch}/-/`, + `/blob/${branch}/-/src/merge_requests/2`, + `/blob/${branch}/-/src/blob/`, + `/tree/${branch}/-/src/blob/`, + `/tree/${branch}/-/src/tree/`, + ]; + let router; + + beforeEach(() => { + VueRouter.mockClear(); + router = new IdeRouter({ + mode: 'history', + }); + }); + + it.each` + path | expected + ${'#-test'} | ${'%23-test'} + ${'#test'} | ${'%23test'} + ${'test#'} | ${'test%23'} + ${'test-#'} | ${'test-%23'} + ${'test-#-hash'} | ${'test-%23-hash'} + ${'test/hash#123'} | ${'test/hash%23123'} + `('finds project path when route is $path', ({ path, expected }) => { + paths(path).forEach(route => { + const expectedPath = route.replace(path, expected); + + router.push(route); + expect(VueRouter.prototype.push).toHaveBeenCalledWith(expectedPath, undefined, undefined); + + router.resolve(route); + expect(VueRouter.prototype.resolve).toHaveBeenCalledWith(expectedPath, undefined, undefined); + }); + }); +}); diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js index 989de1a8337..d0abf2c03a9 100644 --- a/spec/frontend/lib/utils/url_utility_spec.js +++ b/spec/frontend/lib/utils/url_utility_spec.js @@ -28,6 +28,12 @@ describe('URL utility', () => { gon.relative_url_root = ''; }); + it('escapes special characters', () => { + expect(urlUtils.webIDEUrl('/gitlab-org/gitlab-#-foss/merge_requests/1')).toBe( + '/-/ide/project/gitlab-org/gitlab-%23-foss/merge_requests/1', + ); + }); + describe('without relative_url_root', () => { it('returns IDE path with route', () => { expect(urlUtils.webIDEUrl('/gitlab-org/gitlab-foss/merge_requests/1')).toBe( diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 2d0c4723648..a9f4b03eba5 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -244,8 +244,8 @@ describe BlobHelper do it 'escapes special characters' do Rails.application.routes.default_url_options[:script_name] = nil - expect(helper.ide_edit_path(project, "testing/#hashes", "readme.md#test")).to eq("/-/ide/project/#{project.namespace.path}/#{project.path}/edit/testing/#hashes/-/readme.md%23test") - expect(helper.ide_edit_path(project, "testing/#hashes", "src#/readme.md#test")).to eq("/-/ide/project/#{project.namespace.path}/#{project.path}/edit/testing/#hashes/-/src%23/readme.md%23test") + expect(helper.ide_edit_path(project, "testing/#hashes", "readme.md#test")).to eq("/-/ide/project/#{project.full_path}/edit/testing/%23hashes/-/readme.md%23test") + expect(helper.ide_edit_path(project, "testing/#hashes", "src#/readme.md#test")).to eq("/-/ide/project/#{project.full_path}/edit/testing/%23hashes/-/src%23/readme.md%23test") end it 'does not escape "/" character' do diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb index a493b96b1e4..5d2334a6d8f 100644 --- a/spec/lib/container_registry/client_spec.rb +++ b/spec/lib/container_registry/client_spec.rb @@ -146,4 +146,57 @@ describe ContainerRegistry::Client do expect(subject).to eq 'sha256:123' end end + + describe '#delete_repository_tag_by_name' do + subject { client.delete_repository_tag_by_name('group/test', 'a') } + + context 'when the tag exists' do + before do + stub_request(:delete, "http://container-registry/v2/group/test/tags/reference/a") + .to_return(status: 200, body: "") + end + + it { is_expected.to be_truthy } + end + + context 'when the tag does not exist' do + before do + stub_request(:delete, "http://container-registry/v2/group/test/tags/reference/a") + .to_return(status: 404, body: "") + end + + it { is_expected.to be_truthy } + end + + context 'when an error occurs' do + before do + stub_request(:delete, "http://container-registry/v2/group/test/tags/reference/a") + .to_return(status: 500, body: "") + end + + it { is_expected.to be_falsey } + end + end + + describe '#supports_tag_delete?' do + subject { client.supports_tag_delete? } + + context 'when the server supports tag deletion' do + before do + stub_request(:options, "http://container-registry/v2/name/tags/reference/tag") + .to_return(status: 200, body: "", headers: { 'Allow' => 'DELETE' }) + end + + it { is_expected.to be_truthy } + end + + context 'when the server does not support tag deletion' do + before do + stub_request(:options, "http://container-registry/v2/name/tags/reference/tag") + .to_return(status: 404, body: "") + end + + it { is_expected.to be_falsey } + end + end end diff --git a/spec/lib/gitlab/background_migration_spec.rb b/spec/lib/gitlab/background_migration_spec.rb index 9546550bfc5..71959f54b38 100644 --- a/spec/lib/gitlab/background_migration_spec.rb +++ b/spec/lib/gitlab/background_migration_spec.rb @@ -165,6 +165,32 @@ describe Gitlab::BackgroundMigration do end end + describe '.remaining', :redis do + context 'when there are jobs remaining' do + let(:queue) { Array.new(12) } + + before do + allow(Sidekiq::Queue).to receive(:new) + .with(described_class.queue) + .and_return(Array.new(12)) + + Sidekiq::Testing.disable! do + BackgroundMigrationWorker.perform_in(10.minutes, 'Foo') + end + end + + it 'returns the enqueued jobs plus the scheduled jobs' do + expect(described_class.remaining).to eq(13) + end + end + + context 'when there are no jobs remaining' do + it 'returns zero' do + expect(described_class.remaining).to be_zero + end + end + end + describe '.exists?' do context 'when there are enqueued jobs present' do let(:queue) do diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb index 82991937644..863c28a86fb 100644 --- a/spec/models/chat_name_spec.rb +++ b/spec/models/chat_name_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ChatName do - set(:chat_name) { create(:chat_name) } + let_it_be(:chat_name) { create(:chat_name) } subject { chat_name } it { is_expected.to belong_to(:service) } diff --git a/spec/models/chat_team_spec.rb b/spec/models/chat_team_spec.rb index 76beb3d506b..107fdaccc68 100644 --- a/spec/models/chat_team_spec.rb +++ b/spec/models/chat_team_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ChatTeam do - set(:chat_team) { create(:chat_team) } + let_it_be(:chat_team) { create(:chat_team) } subject { chat_team } # Associations diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index c9d6687f0ea..31e13122b95 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe Ci::Bridge do - set(:project) { create(:project) } - set(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) } - set(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:project) { create(:project) } + let_it_be(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } let(:bridge) do create(:ci_bridge, :variables, status: :created, diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index da95a2d30f5..588e5872cc8 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -3,11 +3,11 @@ require 'spec_helper' describe Ci::BuildMetadata do - set(:user) { create(:user) } - set(:group) { create(:group) } - set(:project) { create(:project, :repository, group: group, build_timeout: 2000) } + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, group: group, build_timeout: 2000) } - set(:pipeline) do + let_it_be(:pipeline) do create(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch, diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 91185446488..8f2626037a1 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -3,11 +3,11 @@ require 'spec_helper' describe Ci::Build do - set(:user) { create(:user) } - set(:group) { create(:group) } - set(:project) { create(:project, :repository, group: group) } + let_it_be(:user) { create(:user) } + let_it_be(:group, reload: true) { create(:group) } + let_it_be(:project, reload: true) { create(:project, :repository, group: group) } - set(:pipeline) do + let_it_be(:pipeline, reload: true) do create(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch, @@ -3612,7 +3612,7 @@ describe Ci::Build do end describe '.matches_tag_ids' do - set(:build) { create(:ci_build, project: project, user: user) } + let_it_be(:build, reload: true) { create(:ci_build, project: project, user: user) } let(:tag_ids) { ::ActsAsTaggableOn::Tag.named_any(tag_list).ids } subject { described_class.where(id: build).matches_tag_ids(tag_ids) } @@ -3659,7 +3659,7 @@ describe Ci::Build do end describe '.matches_tags' do - set(:build) { create(:ci_build, project: project, user: user) } + let_it_be(:build, reload: true) { create(:ci_build, project: project, user: user) } subject { described_class.where(id: build).with_any_tags } @@ -3685,7 +3685,7 @@ describe Ci::Build do end describe 'pages deployments' do - set(:build) { create(:ci_build, project: project, user: user) } + let_it_be(:build, reload: true) { create(:ci_build, project: project, user: user) } context 'when job is "pages"' do before do @@ -3852,7 +3852,7 @@ describe Ci::Build do end describe '#artifacts_metadata_entry' do - set(:build) { create(:ci_build, project: project) } + let_it_be(:build) { create(:ci_build, project: project) } let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' } around do |example| @@ -3952,7 +3952,7 @@ describe Ci::Build do end describe '#supported_runner?' do - set(:build) { create(:ci_build) } + let_it_be(:build) { create(:ci_build) } subject { build.supported_runner?(runner_features) } diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index ca49233dde1..f08f05a09bf 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do include ExclusiveLeaseHelpers - set(:build) { create(:ci_build, :running) } + let_it_be(:build) { create(:ci_build, :running) } let(:chunk_index) { 0 } let(:data_store) { :redis } let(:raw_data) { nil } @@ -24,7 +24,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do context 'FastDestroyAll' do let(:parent) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: parent) } - let(:build) { create(:ci_build, :running, :trace_live, pipeline: pipeline, project: parent) } + let!(:build) { create(:ci_build, :running, :trace_live, pipeline: pipeline, project: parent) } let(:subjects) { build.trace_chunks } describe 'Forbid #destroy and #destroy_all' do diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb index 370606a73bc..f3d743fc272 100644 --- a/spec/models/ci/processable_spec.rb +++ b/spec/models/ci/processable_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Ci::Processable do - set(:project) { create(:project) } - set(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:project) { create(:project) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } describe '#aggregated_needs_names' do let(:with_aggregated_needs) { pipeline.processables.select_with_aggregated_needs(project) } diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 3e494d19233..0192c8ed17d 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -38,8 +38,8 @@ describe Ci::Runner do end context 'runner_type validations' do - set(:group) { create(:group) } - set(:project) { create(:project) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project) } let(:group_runner) { create(:ci_runner, :group, groups: [group]) } let(:project_runner) { create(:ci_runner, :project, projects: [project]) } let(:instance_runner) { create(:ci_runner, :instance) } @@ -322,7 +322,7 @@ describe Ci::Runner do end describe '#can_pick?' do - set(:pipeline) { create(:ci_pipeline) } + let_it_be(:pipeline) { create(:ci_pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:runner_project) { build.project } let(:runner) { create(:ci_runner, :project, projects: [runner_project], tag_list: tag_list, run_untagged: run_untagged) } diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb index 0a3065140bf..5ed812652c5 100644 --- a/spec/models/container_repository_spec.rb +++ b/spec/models/container_repository_spec.rb @@ -85,7 +85,7 @@ describe ContainerRepository do context 'when action succeeds' do it 'returns status that indicates success' do expect(repository.client) - .to receive(:delete_repository_tag) + .to receive(:delete_repository_tag_by_digest) .twice .and_return(true) @@ -96,7 +96,7 @@ describe ContainerRepository do context 'when action fails' do it 'returns status that indicates failure' do expect(repository.client) - .to receive(:delete_repository_tag) + .to receive(:delete_repository_tag_by_digest) .twice .and_return(false) @@ -105,6 +105,36 @@ describe ContainerRepository do end end + describe '#delete_tag_by_name' do + let(:repository) do + create(:container_repository, name: 'my_image', + tags: { latest: '123', rc1: '234' }, + project: project) + end + + context 'when action succeeds' do + it 'returns status that indicates success' do + expect(repository.client) + .to receive(:delete_repository_tag_by_name) + .with(repository.path, "latest") + .and_return(true) + + expect(repository.delete_tag_by_name('latest')).to be_truthy + end + end + + context 'when action fails' do + it 'returns status that indicates failure' do + expect(repository.client) + .to receive(:delete_repository_tag_by_name) + .with(repository.path, "latest") + .and_return(false) + + expect(repository.delete_tag_by_name('latest')).to be_falsey + end + end + end + describe '#location' do context 'when registry is running on a custom port' do before do diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb index 27de0584b8a..0a14eae26f3 100644 --- a/spec/models/diff_viewer/server_side_spec.rb +++ b/spec/models/diff_viewer/server_side_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe DiffViewer::ServerSide do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } let(:commit) { project.commit_by(oid: '570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } let!(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } diff --git a/spec/models/event_collection_spec.rb b/spec/models/event_collection_spec.rb index c421ffa000d..e6f80a4c4d0 100644 --- a/spec/models/event_collection_spec.rb +++ b/spec/models/event_collection_spec.rb @@ -4,10 +4,10 @@ require 'spec_helper' describe EventCollection do describe '#to_a' do - set(:group) { create(:group) } - set(:project) { create(:project_empty_repo, group: group) } - set(:projects) { Project.where(id: project.id) } - set(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project_empty_repo, group: group) } + let_it_be(:projects) { Project.where(id: project.id) } + let_it_be(:user) { create(:user) } context 'with project events' do before do diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb index 93862e98172..57eb077031c 100644 --- a/spec/models/guest_spec.rb +++ b/spec/models/guest_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe Guest do - set(:public_project) { create(:project, :public) } - set(:private_project) { create(:project, :private) } - set(:internal_project) { create(:project, :internal) } + let_it_be(:public_project, reload: true) { create(:project, :public) } + let_it_be(:private_project) { create(:project, :private) } + let_it_be(:internal_project) { create(:project, :internal) } describe '.can_pull?' do context 'when project is private' do diff --git a/spec/models/list_user_preference_spec.rb b/spec/models/list_user_preference_spec.rb index 1335a3700dc..10a7bf41f4e 100644 --- a/spec/models/list_user_preference_spec.rb +++ b/spec/models/list_user_preference_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe ListUserPreference do - set(:user) { create(:user) } - set(:list) { create(:list) } + let_it_be(:user) { create(:user) } + let_it_be(:list) { create(:list) } before do list.update_preferences_for(user, { collapsed: true }) diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index a88db3b87af..99b7c4f148a 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -352,9 +352,9 @@ describe PagesDomain do end context 'configuration updates when attributes change' do - set(:project1) { create(:project) } - set(:project2) { create(:project) } - set(:domain) { create(:pages_domain) } + let_it_be(:project1) { create(:project) } + let_it_be(:project2) { create(:project) } + let_it_be(:domain) { create(:pages_domain) } where(:attribute, :old_value, :new_value, :update_expected) do now = Time.now @@ -402,8 +402,8 @@ describe PagesDomain do end context 'TLS configuration' do - set(:domain_without_tls) { create(:pages_domain, :without_certificate, :without_key) } - set(:domain) { create(:pages_domain) } + let_it_be(:domain_without_tls) { create(:pages_domain, :without_certificate, :without_key) } + let_it_be(:domain) { create(:pages_domain) } let(:cert1) { domain.certificate } let(:cert2) { cert1 + ' ' } diff --git a/spec/models/project_services/alerts_service_spec.rb b/spec/models/project_services/alerts_service_spec.rb new file mode 100644 index 00000000000..4e63ece26d8 --- /dev/null +++ b/spec/models/project_services/alerts_service_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe AlertsService do + let_it_be(:project) { create(:project) } + let(:service_params) { { project: project, active: active } } + let(:active) { true } + let(:service) { described_class.new(service_params) } + + shared_context 'when active' do + let(:active) { true } + end + + shared_context 'when inactive' do + let(:active) { false } + end + + shared_context 'when persisted' do + before do + service.save! + service.reload + end + end + + describe '#url' do + include Gitlab::Routing + + subject { service.url } + + it { is_expected.to eq(project_alerts_notify_url(project, format: :json)) } + end + + describe '#json_fields' do + subject { service.json_fields } + + it { is_expected.to eq(%w(active token)) } + end + + describe '#as_json' do + subject { service.as_json(only: service.json_fields) } + + it { is_expected.to eq('active' => true, 'token' => nil) } + end + + describe '#token' do + shared_context 'reset token' do + before do + service.token = '' + service.valid? + end + end + + shared_context 'assign token' do |token| + before do + service.token = token + service.valid? + end + end + + shared_examples 'valid token' do + it { is_expected.to match(/\A\h{32}\z/) } + end + + shared_examples 'no token' do + it { is_expected.to be_blank } + end + + subject { service.token } + + context 'when active' do + include_context 'when active' + + context 'when resetting' do + let!(:previous_token) { service.token } + + include_context 'reset token' + + it_behaves_like 'valid token' + + it { is_expected.not_to eq(previous_token) } + end + + context 'when assigning' do + include_context 'assign token', 'random token' + + it_behaves_like 'valid token' + end + end + + context 'when inactive' do + include_context 'when inactive' + + context 'when resetting' do + let!(:previous_token) { service.token } + + include_context 'reset token' + + it_behaves_like 'no token' + end + end + + context 'when persisted' do + include_context 'when persisted' + + it_behaves_like 'valid token' + end + end +end diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb index 83d3c8b3a70..d93b8a2cb40 100644 --- a/spec/models/project_services/microsoft_teams_service_spec.rb +++ b/spec/models/project_services/microsoft_teams_service_spec.rb @@ -37,9 +37,9 @@ describe MicrosoftTeamsService do end describe "#execute" do - let(:user) { create(:user) } + let(:user) { create(:user) } - set(:project) { create(:project, :repository, :wiki_repo) } + let_it_be(:project) { create(:project, :repository, :wiki_repo) } before do allow(chat_service).to receive_messages( diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index 49005d8c681..1922bb065cf 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -169,7 +169,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do end context 'cluster belongs to projects group' do - set(:group) { create(:group) } + let_it_be(:group) { create(:group) } let(:project) { create(:prometheus_project, group: group) } let(:cluster) { create(:cluster_for_group, :with_installed_helm, groups: [group]) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f847cb63ddc..9dc362594dd 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3901,7 +3901,7 @@ describe Project do end context 'legacy storage' do - set(:project) { create(:project, :repository, :legacy_storage) } + let_it_be(:project) { create(:project, :repository, :legacy_storage) } let(:gitlab_shell) { Gitlab::Shell.new } let(:project_storage) { project.send(:storage) } @@ -4000,7 +4000,7 @@ describe Project do end context 'hashed storage' do - set(:project) { create(:project, :repository, skip_disk_validation: true) } + let_it_be(:project) { create(:project, :repository, skip_disk_validation: true) } let(:gitlab_shell) { Gitlab::Shell.new } let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) } let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) } @@ -4090,7 +4090,7 @@ describe Project do end describe '#has_ci?' do - set(:project) { create(:project) } + let_it_be(:project, reload: true) { create(:project) } let(:repository) { double } before do @@ -4134,7 +4134,7 @@ describe Project do Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0) end - set(:project) { create(:project) } + let_it_be(:project, reload: true) { create(:project) } subject { project.auto_devops_enabled? } @@ -4269,7 +4269,7 @@ describe Project do end describe '#has_auto_devops_implicitly_enabled?' do - set(:project) { create(:project) } + let_it_be(:project, reload: true) { create(:project) } context 'when disabled in settings' do before do @@ -4330,7 +4330,7 @@ describe Project do end describe '#has_auto_devops_implicitly_disabled?' do - set(:project) { create(:project) } + let_it_be(:project, reload: true) { create(:project) } before do allow(Feature).to receive(:enabled?).and_call_original @@ -4408,7 +4408,7 @@ describe Project do end describe '#api_variables' do - set(:project) { create(:project) } + let_it_be(:project) { create(:project) } it 'exposes API v4 URL' do expect(project.api_variables.first[:key]).to eq 'CI_API_V4_URL' @@ -4605,7 +4605,7 @@ describe Project do end describe '#write_repository_config' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } it 'writes full path in .git/config when key is missing' do project.write_repository_config @@ -4696,7 +4696,7 @@ describe Project do end describe '#has_active_hooks?' do - set(:project) { create(:project) } + let_it_be(:project) { create(:project) } it { expect(project.has_active_hooks?).to be_falsey } @@ -4723,7 +4723,7 @@ describe Project do end describe '#has_active_services?' do - set(:project) { create(:project) } + let_it_be(:project) { create(:project) } it { expect(project.has_active_services?).to be_falsey } @@ -5009,8 +5009,8 @@ describe Project do describe '#members_among' do let(:users) { create_list(:user, 3) } - set(:group) { create(:group) } - set(:project) { create(:project, namespace: group) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, namespace: group) } before do project.add_guest(users.first) @@ -5584,6 +5584,14 @@ describe Project do end end + describe '#alerts_service_activated?' do + let!(:project) { create(:project) } + + subject { project.alerts_service_activated? } + + it { is_expected.to be_falsey } + end + def rugged_config rugged_repo(project.repository).config end diff --git a/spec/models/releases/source_spec.rb b/spec/models/releases/source_spec.rb index c8ac8e31c97..d7af6fd90a6 100644 --- a/spec/models/releases/source_spec.rb +++ b/spec/models/releases/source_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Releases::Source do - set(:project) { create(:project, :repository, name: 'finance-cal') } + let_it_be(:project) { create(:project, :repository, name: 'finance-cal') } let(:tag_name) { 'v1.0' } describe '.all' do diff --git a/spec/requests/api/lsif_data_spec.rb b/spec/requests/api/lsif_data_spec.rb index ca3a30bd1d0..2e0670ded95 100644 --- a/spec/requests/api/lsif_data_spec.rb +++ b/spec/requests/api/lsif_data_spec.rb @@ -60,7 +60,8 @@ describe API::LsifData do 'end_char' => 18, 'end_line' => 8, 'start_char' => 13, - 'start_line' => 8 + 'start_line' => 8, + 'definition_url' => project_blob_path(project, "#{commit.id}/morestrings/reverse.go", anchor: 'L5') }) end diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb new file mode 100644 index 00000000000..efd168a0a8a --- /dev/null +++ b/spec/services/projects/alerting/notify_service_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Alerting::NotifyService do + let_it_be(:project, reload: true) { create(:project) } + + shared_examples 'does not process incident issues' do |http_status:| + it 'does not process issues' do + expect(IncidentManagement::ProcessAlertWorker) + .not_to receive(:perform_async) + + expect(subject.status).to eq(:error) + expect(subject.http_status).to eq(http_status) + end + end + + describe '#execute' do + let(:token) { 'invalid-token' } + let(:starts_at) { Time.now.change(usec: 0) } + let(:service) { described_class.new(project, nil, payload) } + let(:payload_raw) do + { + 'title' => 'alert title', + 'start_time' => starts_at.rfc3339 + } + end + let(:payload) { ActionController::Parameters.new(payload_raw).permit! } + + subject { service.execute(token) } + + it_behaves_like 'does not process incident issues', http_status: 403 + end +end diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb index 78b969c8a0e..cd4d1e3fe67 100644 --- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb +++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb @@ -41,7 +41,7 @@ describe Projects::ContainerRepository::CleanupTagsService do let(:params) { {} } it 'does not remove anything' do - expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_digest) is_expected.to include(status: :success, deleted: []) end @@ -156,7 +156,7 @@ describe Projects::ContainerRepository::CleanupTagsService do def expect_delete(digest) expect_any_instance_of(ContainerRegistry::Client) - .to receive(:delete_repository_tag) + .to receive(:delete_repository_tag_by_digest) .with(repository.path, digest) { true } end end diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb index decbbb7597f..e17e4b6f7c9 100644 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -18,10 +18,6 @@ describe Projects::ContainerRepository::DeleteTagsService do stub_container_registry_tags( repository: repository.path, tags: %w(latest A Ba Bb C D E)) - - stub_tag_digest('latest', 'sha256:configA') - stub_tag_digest('A', 'sha256:configA') - stub_tag_digest('Ba', 'sha256:configB') end describe '#execute' do @@ -38,82 +34,178 @@ describe Projects::ContainerRepository::DeleteTagsService do project.add_developer(user) end - context 'when no params are specified' do - let(:params) { {} } + context 'when the registry supports fast delete' do + context 'and the feature is enabled' do + let_it_be(:project) { create(:project, :private) } + let_it_be(:repository) { create(:container_repository, :root, project: project) } - it 'does not remove anything' do - expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) + before do + allow(repository.client).to receive(:supports_tag_delete?).and_return(true) + end - is_expected.to include(status: :error) - end - end + context 'with tags to delete' do + let_it_be(:tags) { %w[A Ba] } - context 'with empty tags' do - let(:tags) { [] } + it 'deletes the tags by name' do + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/A") + .to_return(status: 200, body: "") - it 'does not remove anything' do - expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag) + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/Ba") + .to_return(status: 200, body: "") - is_expected.to include(status: :error) - end - end + expect_delete_tag_by_name('A') + expect_delete_tag_by_name('Ba') + + is_expected.to include(status: :success) + end + + it 'succeeds when tag delete returns 404' do + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/A") + .to_return(status: 200, body: "") + + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/Ba") + .to_return(status: 404, body: "") + + is_expected.to include(status: :success) + end + + context 'with failures' do + context 'when the delete request fails' do + before do + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/A") + .to_return(status: 500, body: "") + + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/tags/reference/Ba") + .to_return(status: 500, body: "") + end - context 'with tags to delete' do - let(:tags) { %w[A Ba] } + it { is_expected.to include(status: :error) } + end + end + end + + context 'when no params are specified' do + let_it_be(:params) { {} } + + it 'does not remove anything' do + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_name) + + is_expected.to include(status: :error) + end + end - it 'deletes the tags using a dummy image' do - stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + context 'with empty tags' do + let_it_be(:tags) { [] } - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + it 'does not remove anything' do + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_name) - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + is_expected.to include(status: :error) + end + end + end + context 'and the feature is disabled' do + before do + stub_feature_flags(container_registry_fast_tag_delete: false) + end - expect_delete_tag('sha256:dummy') + it 'fallbacks to slow delete' do + expect(service).not_to receive(:fast_delete) + expect(service).to receive(:slow_delete).with(repository, tags) - is_expected.to include(status: :success) + subject + end end + end + context 'when the registry does not support fast delete' do + let_it_be(:project) { create(:project, :private) } + let_it_be(:repository) { create(:container_repository, :root, project: project) } - it 'succedes when tag delete returns 404' do - stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + before do + stub_tag_digest('latest', 'sha256:configA') + stub_tag_digest('A', 'sha256:configA') + stub_tag_digest('Ba', 'sha256:configB') - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + allow(repository.client).to receive(:supports_tag_delete?).and_return(false) + end - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") - .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + context 'when no params are specified' do + let_it_be(:params) { {} } - stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:dummy") - .to_return(status: 404, body: "", headers: {}) + it 'does not remove anything' do + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_digest) - is_expected.to include(status: :success) + is_expected.to include(status: :error) + end end - context 'with failures' do - context 'when the dummy manifest generation fails' do - before do - stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3', success: false) - end + context 'with empty tags' do + let_it_be(:tags) { [] } + + it 'does not remove anything' do + expect_any_instance_of(ContainerRegistry::Client).not_to receive(:delete_repository_tag_by_digest) + + is_expected.to include(status: :error) + end + end + + context 'with tags to delete' do + let_it_be(:tags) { %w[A Ba] } - it { is_expected.to include(status: :error) } + it 'deletes the tags using a dummy image' do + stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + expect_delete_tag_by_digest('sha256:dummy') + + is_expected.to include(status: :success) end - context 'when updating the tags fails' do - before do - stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + it 'succeeds when tag delete returns 404' do + stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") - .to_return(status: 500, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) - stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") - .to_return(status: 500, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:dummy") + .to_return(status: 404, body: "", headers: {}) + + is_expected.to include(status: :success) + end - stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3") - .to_return(status: 200, body: "", headers: {}) + context 'with failures' do + context 'when the dummy manifest generation fails' do + before do + stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3', success: false) + end + + it { is_expected.to include(status: :error) } end - it { is_expected.to include(status: :error) } + context 'when updating the tags fails' do + before do + stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") + .to_return(status: 500, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") + .to_return(status: 500, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3") + .to_return(status: 200, body: "", headers: {}) + end + + it { is_expected.to include(status: :error) } + end end end end @@ -141,9 +233,21 @@ describe Projects::ContainerRepository::DeleteTagsService do .with(repository.path, content, digest) { double(success?: success ) } end - def expect_delete_tag(digest) + def expect_delete_tag_by_digest(digest) expect_any_instance_of(ContainerRegistry::Client) - .to receive(:delete_repository_tag) + .to receive(:delete_repository_tag_by_digest) .with(repository.path, digest) { true } + + expect_any_instance_of(ContainerRegistry::Client) + .not_to receive(:delete_repository_tag_by_name) + end + + def expect_delete_tag_by_name(name) + expect_any_instance_of(ContainerRegistry::Client) + .to receive(:delete_repository_tag_by_name) + .with(repository.path, name) { true } + + expect_any_instance_of(ContainerRegistry::Client) + .not_to receive(:delete_repository_tag_by_digest) end end diff --git a/spec/services/projects/lsif_data_service_spec.rb b/spec/services/projects/lsif_data_service_spec.rb index b3c37c01c4d..29a99a96c41 100644 --- a/spec/services/projects/lsif_data_service_spec.rb +++ b/spec/services/projects/lsif_data_service_spec.rb @@ -23,43 +23,51 @@ describe Projects::LsifDataService do end context 'for main.go' do + let(:path_prefix) { "/#{project.full_path}/-/blob/#{commit_id}" } + it 'returns lsif ranges for the file' do expect(service.execute).to eq([ { end_char: 9, end_line: 6, start_char: 5, - start_line: 6 + start_line: 6, + definition_url: "#{path_prefix}/main.go#L7" }, { end_char: 36, end_line: 3, start_char: 1, - start_line: 3 + start_line: 3, + definition_url: "#{path_prefix}/main.go#L4" }, { end_char: 12, end_line: 7, start_char: 1, - start_line: 7 + start_line: 7, + definition_url: "#{path_prefix}/main.go#L4" }, { end_char: 20, end_line: 7, start_char: 13, - start_line: 7 + start_line: 7, + definition_url: "#{path_prefix}/morestrings/reverse.go#L11" }, { end_char: 12, end_line: 8, start_char: 1, - start_line: 8 + start_line: 8, + definition_url: "#{path_prefix}/main.go#L4" }, { end_char: 18, end_line: 8, start_char: 13, - start_line: 8 + start_line: 8, + definition_url: "#{path_prefix}/morestrings/reverse.go#L5" } ]) end @@ -73,7 +81,8 @@ describe Projects::LsifDataService do end_char: 2, end_line: 11, start_char: 1, - start_line: 11 + start_line: 11, + definition_url: "/#{project.full_path}/-/blob/#{commit_id}/morestrings/reverse.go#L12" }) end end @@ -87,7 +96,7 @@ describe Projects::LsifDataService do end end - describe '#doc_id_from' do + describe '#doc_id' do context 'when the passed path matches multiple files' do let(:path) { 'check/main.go' } let(:docs) do @@ -100,7 +109,9 @@ describe Projects::LsifDataService do end it 'fetches the document with the shortest absolute path' do - expect(service.__send__(:doc_id_from, docs)).to eq(3) + service.instance_variable_set(:@docs, docs) + + expect(service.__send__(:doc_id)).to eq(3) end end end |