diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-25 12:08:19 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-25 12:08:19 +0000 |
commit | e6baeabaa9651d90b03bb64ffce75a2c3cb89aab (patch) | |
tree | 85f3cbd6e437b17be59505cf3ac4794c1838609e /spec | |
parent | 5064bf8c5647d4c4430cbb4d097cf1592416de29 (diff) | |
download | gitlab-ce-e6baeabaa9651d90b03bb64ffce75a2c3cb89aab.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
30 files changed, 494 insertions, 182 deletions
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 305419efe96..a280d829d83 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -31,6 +31,8 @@ describe DashboardController do before do create(:event, :created, project: project, target: create(:issue)) + create(:wiki_page_event, :created, project: project) + create(:wiki_page_event, :updated, project: project) sign_in(user) @@ -45,7 +47,7 @@ describe DashboardController do it 'returns count' do get :activity, params: { format: :json } - expect(json_response['count']).to eq(1) + expect(json_response['count']).to eq(3) end end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 446c1c59030..5bb7853a154 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -16,7 +16,7 @@ FactoryBot.define do options do { - image: 'ruby:2.1', + image: 'ruby:2.7', services: ['postgres'], script: ['ls -a'] } @@ -336,7 +336,7 @@ FactoryBot.define do trait :extended_options do options do { - image: { name: 'ruby:2.1', entrypoint: '/bin/sh' }, + image: { name: 'ruby:2.7', entrypoint: '/bin/sh' }, services: ['postgres', { name: 'docker:stable-dind', entrypoint: '/bin/sh', command: 'sleep 30', alias: 'docker' }], script: %w(echo), after_script: %w(ls date), diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb index 3d99a04ea1a..21e1d911f96 100644 --- a/spec/factories/snippets.rb +++ b/spec/factories/snippets.rb @@ -28,7 +28,7 @@ FactoryBot.define do bare_repo: TestEnv.factory_repo_path_bare, refs: TestEnv::BRANCH_SHA) - snippet.track_snippet_repository + snippet.track_snippet_repository(snippet.repository.storage) end end diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index 660004e5eea..60e509e4a88 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -5,8 +5,15 @@ import stubChildren from 'helpers/stub_children'; import component from '~/registry/explorer/pages/details.vue'; import store from '~/registry/explorer/stores/'; import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/'; +import { + DELETE_TAG_SUCCESS_MESSAGE, + DELETE_TAG_ERROR_MESSAGE, + DELETE_TAGS_SUCCESS_MESSAGE, + DELETE_TAGS_ERROR_MESSAGE, +} from '~/registry/explorer/constants'; import { tagsListResponse } from '../mock_data'; import { GlModal } from '../stubs'; +import { $toast } from '../../shared/mocks'; describe('Details Page', () => { let wrapper; @@ -40,6 +47,7 @@ describe('Details Page', () => { id: routeId, }, }, + $toast, }, }); dispatchSpy = jest.spyOn(store, 'dispatch'); @@ -249,13 +257,11 @@ describe('Details Page', () => { }); }); - it('when only one element is selected', () => { - const deleteModal = findDeleteModal(); - - wrapper.setData({ itemsToBeDeleted: [0] }); - deleteModal.vm.$emit('ok'); + describe('when only one element is selected', () => { + it('execute the delete and remove selection', () => { + wrapper.setData({ itemsToBeDeleted: [0] }); + findDeleteModal().vm.$emit('ok'); - return wrapper.vm.$nextTick().then(() => { expect(store.dispatch).toHaveBeenCalledWith('requestDeleteTag', { tag: store.state.tags[0], params: wrapper.vm.$route.params.id, @@ -264,15 +270,33 @@ describe('Details Page', () => { expect(wrapper.vm.itemsToBeDeleted).toEqual([]); expect(findCheckedCheckboxes()).toHaveLength(0); }); + + it('show success toast on successful delete', () => { + return wrapper.vm.handleSingleDelete(0).then(() => { + expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAG_SUCCESS_MESSAGE, { + type: 'success', + }); + }); + }); + + it('show error toast on erred delete', () => { + dispatchSpy.mockRejectedValue(); + return wrapper.vm.handleSingleDelete(0).then(() => { + expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAG_ERROR_MESSAGE, { + type: 'error', + }); + }); + }); }); - it('when multiple elements are selected', () => { - const deleteModal = findDeleteModal(); + describe('when multiple elements are selected', () => { + beforeEach(() => { + wrapper.setData({ itemsToBeDeleted: [0, 1] }); + }); - wrapper.setData({ itemsToBeDeleted: [0, 1] }); - deleteModal.vm.$emit('ok'); + it('execute the delete and remove selection', () => { + findDeleteModal().vm.$emit('ok'); - return wrapper.vm.$nextTick().then(() => { expect(store.dispatch).toHaveBeenCalledWith('requestDeleteTags', { ids: store.state.tags.map(t => t.name), params: wrapper.vm.$route.params.id, @@ -281,6 +305,23 @@ describe('Details Page', () => { expect(wrapper.vm.itemsToBeDeleted).toEqual([]); expect(findCheckedCheckboxes()).toHaveLength(0); }); + + it('show success toast on successful delete', () => { + return wrapper.vm.handleMultipleDelete(0).then(() => { + expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAGS_SUCCESS_MESSAGE, { + type: 'success', + }); + }); + }); + + it('show error toast on erred delete', () => { + dispatchSpy.mockRejectedValue(); + return wrapper.vm.handleMultipleDelete(0).then(() => { + expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAGS_ERROR_MESSAGE, { + type: 'error', + }); + }); + }); }); }); diff --git a/spec/frontend/registry/explorer/pages/index_spec.js b/spec/frontend/registry/explorer/pages/index_spec.js new file mode 100644 index 00000000000..f52e7d67866 --- /dev/null +++ b/spec/frontend/registry/explorer/pages/index_spec.js @@ -0,0 +1,62 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; +import component from '~/registry/explorer/pages/index.vue'; +import store from '~/registry/explorer/stores/'; + +describe('List Page', () => { + let wrapper; + let dispatchSpy; + + const findRouterView = () => wrapper.find({ ref: 'router-view' }); + const findAlert = () => wrapper.find(GlAlert); + const findLink = () => wrapper.find(GlLink); + + const mountComponent = () => { + wrapper = shallowMount(component, { + store, + stubs: { + RouterView: true, + GlSprintf, + }, + }); + }; + + beforeEach(() => { + dispatchSpy = jest.spyOn(store, 'dispatch'); + mountComponent(); + }); + + it('has a router view', () => { + expect(findRouterView().exists()).toBe(true); + }); + + describe('garbageCollectionTip alert', () => { + beforeEach(() => { + store.dispatch('setInitialState', { isAdmin: true, garbageCollectionHelpPagePath: 'foo' }); + store.dispatch('setShowGarbageCollectionTip', true); + }); + + afterEach(() => { + store.dispatch('setInitialState', {}); + store.dispatch('setShowGarbageCollectionTip', false); + }); + + it('is visible when the user is an admin and the user performed a delete action', () => { + expect(findAlert().exists()).toBe(true); + }); + + it('on dismiss disappears ', () => { + findAlert().vm.$emit('dismiss'); + expect(dispatchSpy).toHaveBeenCalledWith('setShowGarbageCollectionTip', false); + return wrapper.vm.$nextTick().then(() => { + expect(findAlert().exists()).toBe(false); + }); + }); + + it('contains a link to the docs', () => { + const link = findLink(); + expect(link.exists()).toBe(true); + expect(link.attributes('href')).toBe(store.state.config.garbageCollectionHelpPagePath); + }); + }); +}); diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js index 5b713778495..3e46a29f776 100644 --- a/spec/frontend/registry/explorer/pages/list_spec.js +++ b/spec/frontend/registry/explorer/pages/list_spec.js @@ -8,8 +8,13 @@ import GroupEmptyState from '~/registry/explorer/components/group_empty_state.vu import ProjectEmptyState from '~/registry/explorer/components/project_empty_state.vue'; import store from '~/registry/explorer/stores/'; import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/'; +import { + DELETE_IMAGE_SUCCESS_MESSAGE, + DELETE_IMAGE_ERROR_MESSAGE, +} from '~/registry/explorer/constants'; import { imagesListResponse } from '../mock_data'; import { GlModal, GlEmptyState } from '../stubs'; +import { $toast } from '../../shared/mocks'; const localVue = createLocalVue(); localVue.use(VueRouter); @@ -40,6 +45,9 @@ describe('List Page', () => { GlEmptyState, GlSprintf, }, + mocks: { + $toast, + }, }); dispatchSpy = jest.spyOn(store, 'dispatch'); store.dispatch('receiveImagesListSuccess', imagesListResponse); @@ -174,11 +182,29 @@ describe('List Page', () => { const itemToDelete = wrapper.vm.images[0]; wrapper.setData({ itemToDelete }); findDeleteModal().vm.$emit('ok'); - return wrapper.vm.$nextTick().then(() => { - expect(store.dispatch).toHaveBeenCalledWith( - 'requestDeleteImage', - itemToDelete.destroy_path, - ); + expect(store.dispatch).toHaveBeenCalledWith( + 'requestDeleteImage', + itemToDelete.destroy_path, + ); + }); + + it('should show a success toast when delete request is successful', () => { + dispatchSpy.mockResolvedValue(); + return wrapper.vm.handleDeleteImage().then(() => { + expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_IMAGE_SUCCESS_MESSAGE, { + type: 'success', + }); + expect(wrapper.vm.itemToDelete).toEqual({}); + }); + }); + + it('should show a error toast when delete request fails', () => { + dispatchSpy.mockRejectedValue(); + return wrapper.vm.handleDeleteImage().then(() => { + expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_IMAGE_ERROR_MESSAGE, { + type: 'error', + }); + expect(wrapper.vm.itemToDelete).toEqual({}); }); }); }); @@ -227,7 +253,7 @@ describe('List Page', () => { beforeEach(() => { jest.spyOn(Tracking, 'event'); - dispatchSpy.mockReturnValue(); + dispatchSpy.mockResolvedValue(); }); it('send an event when delete button is clicked', () => { @@ -235,13 +261,14 @@ describe('List Page', () => { deleteBtn.vm.$emit('click'); testTrackingCall('click_button'); }); + it('send an event when cancel is pressed on modal', () => { const deleteModal = findDeleteModal(); deleteModal.vm.$emit('cancel'); testTrackingCall('cancel_delete'); }); + it('send an event when confirm is clicked on modal', () => { - dispatchSpy.mockReturnValue(); const deleteModal = findDeleteModal(); deleteModal.vm.$emit('ok'); testTrackingCall('confirm_delete'); diff --git a/spec/frontend/registry/explorer/stores/actions_spec.js b/spec/frontend/registry/explorer/stores/actions_spec.js index 3e22621058e..b39c79dd1ab 100644 --- a/spec/frontend/registry/explorer/stores/actions_spec.js +++ b/spec/frontend/registry/explorer/stores/actions_spec.js @@ -38,6 +38,17 @@ describe('Actions RegistryExplorer Store', () => { ); }); + it('setShowGarbageCollectionTip', done => { + testAction( + actions.setShowGarbageCollectionTip, + true, + null, + [{ type: types.SET_SHOW_GARBAGE_COLLECTION_TIP, payload: true }], + [], + done, + ); + }); + describe('receives api responses', () => { const response = { data: [1, 2, 3], @@ -183,18 +194,19 @@ describe('Actions RegistryExplorer Store', () => { [{ type: types.SET_MAIN_LOADING, payload: true }], [ { + type: 'setShowGarbageCollectionTip', + payload: true, + }, + { type: 'requestTagsList', payload: { pagination: {}, params }, }, ], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, + done, ); }); - it('should show flash message on error', done => { + it('should turn off loading on error', done => { testAction( actions.requestDeleteTag, { @@ -208,10 +220,7 @@ describe('Actions RegistryExplorer Store', () => { { type: types.SET_MAIN_LOADING, payload: false }, ], [], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, + done, ); }); }); @@ -235,18 +244,19 @@ describe('Actions RegistryExplorer Store', () => { [{ type: types.SET_MAIN_LOADING, payload: true }], [ { + type: 'setShowGarbageCollectionTip', + payload: true, + }, + { type: 'requestTagsList', payload: { pagination: {}, params }, }, ], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, + done, ); }); - it('should show flash message on error', done => { + it('should turn off loading on error', done => { mock.onDelete(url).replyOnce(500); testAction( @@ -263,17 +273,14 @@ describe('Actions RegistryExplorer Store', () => { { type: types.SET_MAIN_LOADING, payload: false }, ], [], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, + done, ); }); }); describe('request delete single image', () => { + const deletePath = 'delete/path'; it('successfully performs the delete request', done => { - const deletePath = 'delete/path'; mock.onDelete(deletePath).replyOnce(200); testAction( @@ -288,32 +295,32 @@ describe('Actions RegistryExplorer Store', () => { ], [ { + type: 'setShowGarbageCollectionTip', + payload: true, + }, + { type: 'requestImagesList', payload: { pagination: {} }, }, ], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, + done, ); }); - it('should show flash message on error', done => { + it('should turn off loading on error', done => { + mock.onDelete(deletePath).replyOnce(400); testAction( actions.requestDeleteImage, - null, + deletePath, {}, [ { type: types.SET_MAIN_LOADING, payload: true }, { type: types.SET_MAIN_LOADING, payload: false }, ], [], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, - ); + ).catch(() => { + done(); + }); }); }); }); diff --git a/spec/frontend/registry/explorer/stores/getters_spec.js b/spec/frontend/registry/explorer/stores/getters_spec.js index 211b8169d82..cd053ea8edc 100644 --- a/spec/frontend/registry/explorer/stores/getters_spec.js +++ b/spec/frontend/registry/explorer/stores/getters_spec.js @@ -49,4 +49,22 @@ describe('Getters RegistryExplorer store', () => { expect(getters[getter](state)).toBe(expectedPieces.join(' ')); }); }); + + describe('showGarbageCollection', () => { + it.each` + result | showGarbageCollectionTip | isAdmin + ${true} | ${true} | ${true} + ${false} | ${true} | ${false} + ${false} | ${false} | ${true} + `( + 'return $result when showGarbageCollectionTip $showGarbageCollectionTip and isAdmin is $isAdmin', + ({ result, showGarbageCollectionTip, isAdmin }) => { + state = { + config: { isAdmin }, + showGarbageCollectionTip, + }; + expect(getters.showGarbageCollection(state)).toBe(result); + }, + ); + }); }); diff --git a/spec/frontend/registry/explorer/stores/mutations_spec.js b/spec/frontend/registry/explorer/stores/mutations_spec.js index 1d5055c02d2..029fd23f7ce 100644 --- a/spec/frontend/registry/explorer/stores/mutations_spec.js +++ b/spec/frontend/registry/explorer/stores/mutations_spec.js @@ -10,7 +10,12 @@ describe('Mutations Registry Explorer Store', () => { describe('SET_INITIAL_STATE', () => { it('should set the initial state', () => { - const payload = { endpoint: 'foo', isGroupPage: true, expirationPolicy: { foo: 'bar' } }; + const payload = { + endpoint: 'foo', + isGroupPage: true, + expirationPolicy: { foo: 'bar' }, + isAdmin: true, + }; const expectedState = { ...mockState, config: payload }; mutations[types.SET_INITIAL_STATE](mockState, { ...payload, @@ -50,6 +55,15 @@ describe('Mutations Registry Explorer Store', () => { }); }); + describe('SET_SHOW_GARBAGE_COLLECTION_TIP', () => { + it('should set the showGarbageCollectionTip', () => { + const expectedState = { ...mockState, showGarbageCollectionTip: true }; + mutations[types.SET_SHOW_GARBAGE_COLLECTION_TIP](mockState, true); + + expect(mockState).toEqual(expectedState); + }); + }); + describe('SET_PAGINATION', () => { const generatePagination = () => [ { diff --git a/spec/frontend/registry/shared/mocks.js b/spec/frontend/registry/shared/mocks.js new file mode 100644 index 00000000000..e33d06e7499 --- /dev/null +++ b/spec/frontend/registry/shared/mocks.js @@ -0,0 +1,4 @@ +// eslint-disable-next-line import/prefer-default-export +export const $toast = { + show: jest.fn(), +}; diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 61229127770..0109525bcac 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -88,6 +88,85 @@ describe EventsHelper do end end + describe '#event_preposition' do + context 'for wiki page events' do + let(:event) { create(:wiki_page_event) } + + it 'returns a suitable phrase' do + expect(helper.event_preposition(event)).to eq('in the wiki for') + end + end + + context 'for push action events' do + let(:event) { create(:push_event) } + + it 'returns a suitable phrase' do + expect(helper.event_preposition(event)).to eq('at') + end + end + + context 'for commented actions' do + let(:event) { create(:event, :commented) } + + it 'returns a suitable phrase' do + expect(helper.event_preposition(event)).to eq('at') + end + end + + context 'for any event with a target' do + let(:event) { create(:event, target: create(:issue)) } + + it 'returns a suitable phrase' do + expect(helper.event_preposition(event)).to eq('at') + end + end + + context 'for milestone events' do + let(:event) { create(:event, target: create(:milestone)) } + + it 'returns a suitable phrase' do + expect(helper.event_preposition(event)).to eq('in') + end + end + + context 'for non-matching events' do + let(:event) { create(:event, :created) } + + it 'returns no preposition' do + expect(helper.event_preposition(event)).to be_nil + end + end + end + + describe 'event_wiki_page_target_url' do + let(:project) { create(:project) } + let(:wiki_page) { create(:wiki_page, wiki: create(:project_wiki, project: project)) } + let(:event) { create(:wiki_page_event, project: project, wiki_page: wiki_page) } + + it 'links to the wiki page' do + url = helper.project_wiki_url(project, wiki_page.slug) + + expect(helper.event_wiki_page_target_url(event)).to eq(url) + end + end + + describe '#event_wiki_title_html' do + let(:event) { create(:wiki_page_event) } + + it 'produces a suitable title chunk' do + url = helper.event_wiki_page_target_url(event) + title = event.target_title + html = [ + "<span class=\"event-target-type append-right-4\">wiki page</span>", + "<a title=\"#{title}\" class=\"has-tooltip event-target-link append-right-4\" href=\"#{url}\">", + title, + "</a>" + ].join + + expect(helper.event_wiki_title_html(event)).to eq(html) + end + end + describe '#event_note_target_url' do let(:project) { create(:project, :public, :repository) } let(:event) { create(:event, project: project) } diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb index 04bab9c58b8..4f7cfc9783a 100644 --- a/spec/lib/gitlab/ci/build/image_spec.rb +++ b/spec/lib/gitlab/ci/build/image_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Ci::Build::Image do subject { described_class.from_image(job) } context 'when image is defined in job' do - let(:image_name) { 'ruby:2.1' } + let(:image_name) { 'ruby:2.7' } let(:job) { create(:ci_build, options: { image: image_name } ) } context 'when image is defined as string' do diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index 8de2e5de724..de3e887a6ed 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -6,11 +6,11 @@ describe Gitlab::Ci::Config::Entry::Image do let(:entry) { described_class.new(config) } context 'when configuration is a string' do - let(:config) { 'ruby:2.2' } + let(:config) { 'ruby:2.7' } describe '#value' do it 'returns image hash' do - expect(entry.value).to eq({ name: 'ruby:2.2' }) + expect(entry.value).to eq({ name: 'ruby:2.7' }) end end @@ -28,7 +28,7 @@ describe Gitlab::Ci::Config::Entry::Image do describe '#image' do it "returns image's name" do - expect(entry.name).to eq 'ruby:2.2' + expect(entry.name).to eq 'ruby:2.7' end end @@ -46,7 +46,7 @@ describe Gitlab::Ci::Config::Entry::Image do end context 'when configuration is a hash' do - let(:config) { { name: 'ruby:2.2', entrypoint: %w(/bin/sh run) } } + let(:config) { { name: 'ruby:2.7', entrypoint: %w(/bin/sh run) } } describe '#value' do it 'returns image hash' do @@ -68,7 +68,7 @@ describe Gitlab::Ci::Config::Entry::Image do describe '#image' do it "returns image's name" do - expect(entry.name).to eq 'ruby:2.2' + expect(entry.name).to eq 'ruby:2.7' end end @@ -80,7 +80,7 @@ describe Gitlab::Ci::Config::Entry::Image do context 'when configuration has ports' do let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] } - let(:config) { { name: 'ruby:2.2', entrypoint: %w(/bin/sh run), ports: ports } } + let(:config) { { name: 'ruby:2.7', entrypoint: %w(/bin/sh run), ports: ports } } let(:entry) { described_class.new(config, { with_image_ports: image_ports }) } let(:image_ports) { false } @@ -112,7 +112,7 @@ describe Gitlab::Ci::Config::Entry::Image do end context 'when entry value is not correct' do - let(:config) { ['ruby:2.2'] } + let(:config) { ['ruby:2.7'] } describe '#errors' do it 'saves errors' do @@ -129,7 +129,7 @@ describe Gitlab::Ci::Config::Entry::Image do end context 'when unexpected key is specified' do - let(:config) { { name: 'ruby:2.2', non_existing: 'test' } } + let(:config) { { name: 'ruby:2.7', non_existing: 'test' } } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index c3871b6b3cf..ba2dbf72fba 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::Ci::Config::Entry::Root do let(:hash) do { before_script: %w(ls pwd), - image: 'ruby:2.2', + image: 'ruby:2.7', default: {}, services: ['postgres:9.1', 'mysql:5.5'], variables: { VAR: 'root' }, @@ -124,7 +124,7 @@ describe Gitlab::Ci::Config::Entry::Root do { name: :rspec, script: %w[rspec ls], before_script: %w(ls pwd), - image: { name: 'ruby:2.2' }, + image: { name: 'ruby:2.7' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push' }, @@ -138,7 +138,7 @@ describe Gitlab::Ci::Config::Entry::Root do { name: :spinach, before_script: [], script: %w[spinach], - image: { name: 'ruby:2.2' }, + image: { name: 'ruby:2.7' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push' }, @@ -154,7 +154,7 @@ describe Gitlab::Ci::Config::Entry::Root do before_script: [], script: ["make changelog | tee release_changelog.txt"], release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" }, - image: { name: "ruby:2.2" }, + image: { name: "ruby:2.7" }, services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }], cache: { key: "k", untracked: true, paths: ["public/"], policy: "pull-push" }, only: { refs: %w(branches tags) }, @@ -173,7 +173,7 @@ describe Gitlab::Ci::Config::Entry::Root do { before_script: %w(ls pwd), after_script: ['make clean'], default: { - image: 'ruby:2.1', + image: 'ruby:2.7', services: ['postgres:9.1', 'mysql:5.5'] }, variables: { VAR: 'root' }, @@ -200,7 +200,7 @@ describe Gitlab::Ci::Config::Entry::Root do rspec: { name: :rspec, script: %w[rspec ls], before_script: %w(ls pwd), - image: { name: 'ruby:2.1' }, + image: { name: 'ruby:2.7' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: "pull-push" }, @@ -212,7 +212,7 @@ describe Gitlab::Ci::Config::Entry::Root do spinach: { name: :spinach, before_script: [], script: %w[spinach], - image: { name: 'ruby:2.1' }, + image: { name: 'ruby:2.7' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: "pull-push" }, diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb index b2924ae9d91..4d8f0dbc861 100644 --- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb @@ -71,7 +71,7 @@ describe Gitlab::Ci::Config::External::File::Project do let(:root_ref_sha) { project.repository.root_ref_sha } before do - stub_project_blob(root_ref_sha, '/file.yml') { 'image: ruby:2.1' } + stub_project_blob(root_ref_sha, '/file.yml') { 'image: ruby:2.7' } end it 'returns true' do @@ -96,7 +96,7 @@ describe Gitlab::Ci::Config::External::File::Project do let(:ref_sha) { project.commit('master').sha } before do - stub_project_blob(ref_sha, '/file.yml') { 'image: ruby:2.1' } + stub_project_blob(ref_sha, '/file.yml') { 'image: ruby:2.7' } end it 'returns true' do diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index 6839002c3ab..fa358f36527 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::Ci::Config::External::Mapper do let(:file_content) do <<~HEREDOC - image: 'ruby:2.2' + image: 'ruby:2.7' HEREDOC end @@ -34,7 +34,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when the string is a local file' do let(:values) do { include: local_file, - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns File instances' do @@ -46,7 +46,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a local file hash' do let(:values) do { include: { 'local' => local_file }, - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns File instances' do @@ -57,7 +57,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when the string is a remote file' do let(:values) do - { include: remote_url, image: 'ruby:2.2' } + { include: remote_url, image: 'ruby:2.7' } end it 'returns File instances' do @@ -69,7 +69,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a remote file hash' do let(:values) do { include: { 'remote' => remote_url }, - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns File instances' do @@ -81,7 +81,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a template file hash' do let(:values) do { include: { 'template' => template_file }, - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns File instances' do @@ -93,7 +93,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a hash of file and remote' do let(:values) do { include: { 'local' => local_file, 'remote' => remote_url }, - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns ambigious specification error' do @@ -105,7 +105,7 @@ describe Gitlab::Ci::Config::External::Mapper do context "when 'include' is defined as an array" do let(:values) do { include: [remote_url, local_file], - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns Files instances' do @@ -117,7 +117,7 @@ describe Gitlab::Ci::Config::External::Mapper do context "when 'include' is defined as an array of hashes" do let(:values) do { include: [{ remote: remote_url }, { local: local_file }], - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns Files instances' do @@ -128,7 +128,7 @@ describe Gitlab::Ci::Config::External::Mapper do context 'when it has ambigious match' do let(:values) do { include: [{ remote: remote_url, local: local_file }], - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'returns ambigious specification error' do @@ -140,7 +140,7 @@ describe Gitlab::Ci::Config::External::Mapper do context "when 'include' is not defined" do let(:values) do { - image: 'ruby:2.2' + image: 'ruby:2.7' } end @@ -155,7 +155,7 @@ describe Gitlab::Ci::Config::External::Mapper do { 'local' => local_file }, { 'local' => local_file } ], - image: 'ruby:2.2' } + image: 'ruby:2.7' } end it 'raises an exception' do @@ -169,7 +169,7 @@ describe Gitlab::Ci::Config::External::Mapper do { 'local' => local_file }, { 'remote' => remote_url } ], - image: 'ruby:2.2' } + image: 'ruby:2.7' } end before do diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb index ff08d4703fe..45f646660a7 100644 --- a/spec/lib/gitlab/ci/config/external/processor_spec.rb +++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb @@ -24,7 +24,7 @@ describe Gitlab::Ci::Config::External::Processor do subject { processor.perform } context 'when no external files defined' do - let(:values) { { image: 'ruby:2.2' } } + let(:values) { { image: 'ruby:2.7' } } it 'returns the same values' do expect(processor.perform).to eq(values) @@ -32,7 +32,7 @@ describe Gitlab::Ci::Config::External::Processor do end context 'when an invalid local file is defined' do - let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'ruby:2.2' } } + let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'ruby:2.7' } } it 'raises an error' do expect { processor.perform }.to raise_error( @@ -44,7 +44,7 @@ describe Gitlab::Ci::Config::External::Processor do context 'when an invalid remote file is defined' do let(:remote_file) { 'http://doesntexist.com/.gitlab-ci-1.yml' } - let(:values) { { include: remote_file, image: 'ruby:2.2' } } + let(:values) { { include: remote_file, image: 'ruby:2.7' } } before do stub_full_request(remote_file).and_raise(SocketError.new('Some HTTP error')) @@ -60,7 +60,7 @@ describe Gitlab::Ci::Config::External::Processor do context 'with a valid remote external file is defined' do let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } - let(:values) { { include: remote_file, image: 'ruby:2.2' } } + let(:values) { { include: remote_file, image: 'ruby:2.7' } } let(:external_file_content) do <<-HEREDOC before_script: @@ -94,7 +94,7 @@ describe Gitlab::Ci::Config::External::Processor do end context 'with a valid local external file is defined' do - let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } } + let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.7' } } let(:local_file_content) do <<-HEREDOC before_script: @@ -131,7 +131,7 @@ describe Gitlab::Ci::Config::External::Processor do let(:values) do { include: external_files, - image: 'ruby:2.2' + image: 'ruby:2.7' } end @@ -163,7 +163,7 @@ describe Gitlab::Ci::Config::External::Processor do end context 'when external files are defined but not valid' do - let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } } + let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.7' } } let(:local_file_content) { 'invalid content file ////' } @@ -185,7 +185,7 @@ describe Gitlab::Ci::Config::External::Processor do let(:values) do { include: remote_file, - image: 'ruby:2.2' + image: 'ruby:2.7' } end @@ -198,7 +198,7 @@ describe Gitlab::Ci::Config::External::Processor do it 'takes precedence' do stub_full_request(remote_file).to_return(body: remote_file_content) - expect(processor.perform[:image]).to eq('ruby:2.2') + expect(processor.perform[:image]).to eq('ruby:2.7') end end @@ -208,7 +208,7 @@ describe Gitlab::Ci::Config::External::Processor do include: [ { local: '/local/file.yml' } ], - image: 'ruby:2.2' + image: 'ruby:2.7' } end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index d8101e8a621..3b65dbe11ec 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Ci::Config do context 'when config is valid' do let(:yml) do <<-EOS - image: ruby:2.2 + image: ruby:2.7 rspec: script: @@ -32,7 +32,7 @@ describe Gitlab::Ci::Config do describe '#to_hash' do it 'returns hash created from string' do hash = { - image: 'ruby:2.2', + image: 'ruby:2.7', rspec: { script: ['gem install rspec', 'rspec'] @@ -85,7 +85,7 @@ describe Gitlab::Ci::Config do context 'when using extendable hash' do let(:yml) do <<-EOS - image: ruby:2.2 + image: ruby:2.7 rspec: script: rspec @@ -98,7 +98,7 @@ describe Gitlab::Ci::Config do it 'correctly extends the hash' do hash = { - image: 'ruby:2.2', + image: 'ruby:2.7', rspec: { script: 'rspec' }, test: { extends: 'rspec', @@ -188,7 +188,7 @@ describe Gitlab::Ci::Config do let(:yml) do <<-EOS image: - name: ruby:2.2 + name: ruby:2.7 ports: - 80 EOS @@ -202,12 +202,12 @@ describe Gitlab::Ci::Config do context 'in the job image' do let(:yml) do <<-EOS - image: ruby:2.2 + image: ruby:2.7 test: script: rspec image: - name: ruby:2.2 + name: ruby:2.7 ports: - 80 EOS @@ -221,11 +221,11 @@ describe Gitlab::Ci::Config do context 'in the services' do let(:yml) do <<-EOS - image: ruby:2.2 + image: ruby:2.7 test: script: rspec - image: ruby:2.2 + image: ruby:2.7 services: - name: test alias: test @@ -266,7 +266,7 @@ describe Gitlab::Ci::Config do - #{local_location} - #{remote_location} - image: ruby:2.2 + image: ruby:2.7 HEREDOC end @@ -296,7 +296,7 @@ describe Gitlab::Ci::Config do } composed_hash = { before_script: before_script_values, - image: "ruby:2.2", + image: "ruby:2.7", rspec: { script: ["bundle exec rspec"] }, variables: variables } @@ -381,7 +381,7 @@ describe Gitlab::Ci::Config do include: - #{remote_location} - image: ruby:2.2 + image: ruby:2.7 HEREDOC end @@ -392,7 +392,7 @@ describe Gitlab::Ci::Config do end it 'takes precedence' do - expect(config.to_hash).to eq({ image: 'ruby:2.2' }) + expect(config.to_hash).to eq({ image: 'ruby:2.7' }) end end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index af0a85f6c4e..62adba4319e 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -665,7 +665,7 @@ module Gitlab describe "Image and service handling" do context "when extended docker configuration is used" do it "returns image and service when defined" do - config = YAML.dump({ image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] }, + config = YAML.dump({ image: { name: "ruby:2.7", entrypoint: ["/usr/local/bin/init", "run"] }, services: ["mysql", { name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"], command: ["/usr/local/bin/init", "run"] }], @@ -683,7 +683,7 @@ module Gitlab options: { before_script: ["pwd"], script: ["rspec"], - image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] }, + image: { name: "ruby:2.7", entrypoint: ["/usr/local/bin/init", "run"] }, services: [{ name: "mysql" }, { name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"], command: ["/usr/local/bin/init", "run"] }] @@ -696,7 +696,7 @@ module Gitlab end it "returns image and service when overridden for job" do - config = YAML.dump({ image: "ruby:2.1", + config = YAML.dump({ image: "ruby:2.7", services: ["mysql"], before_script: ["pwd"], rspec: { image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, @@ -731,7 +731,7 @@ module Gitlab context "when etended docker configuration is not used" do it "returns image and service when defined" do - config = YAML.dump({ image: "ruby:2.1", + config = YAML.dump({ image: "ruby:2.7", services: ["mysql", "docker:dind"], before_script: ["pwd"], rspec: { script: "rspec" } }) @@ -747,7 +747,7 @@ module Gitlab options: { before_script: ["pwd"], script: ["rspec"], - image: { name: "ruby:2.1" }, + image: { name: "ruby:2.7" }, services: [{ name: "mysql" }, { name: "docker:dind" }] }, allow_failure: false, @@ -758,7 +758,7 @@ module Gitlab end it "returns image and service when overridden for job" do - config = YAML.dump({ image: "ruby:2.1", + config = YAML.dump({ image: "ruby:2.7", services: ["mysql"], before_script: ["pwd"], rspec: { image: "ruby:2.5", services: ["postgresql", "docker:dind"], script: "rspec" } }) @@ -1292,7 +1292,7 @@ module Gitlab describe "Artifacts" do it "returns artifacts when defined" do config = YAML.dump({ - image: "ruby:2.1", + image: "ruby:2.7", services: ["mysql"], before_script: ["pwd"], rspec: { @@ -1318,7 +1318,7 @@ module Gitlab options: { before_script: ["pwd"], script: ["rspec"], - image: { name: "ruby:2.1" }, + image: { name: "ruby:2.7" }, services: [{ name: "mysql" }], artifacts: { name: "custom_name", @@ -1945,7 +1945,7 @@ module Gitlab context 'when hidden job have a script definition' do let(:config) do YAML.dump({ - '.hidden_job' => { image: 'ruby:2.1', script: 'test' }, + '.hidden_job' => { image: 'ruby:2.7', script: 'test' }, 'normal_job' => { script: 'test' } }) end @@ -1956,7 +1956,7 @@ module Gitlab context "when hidden job doesn't have a script definition" do let(:config) do YAML.dump({ - '.hidden_job' => { image: 'ruby:2.1' }, + '.hidden_job' => { image: 'ruby:2.7' }, 'normal_job' => { script: 'test' } }) end diff --git a/spec/lib/gitlab/config/loader/yaml_spec.rb b/spec/lib/gitlab/config/loader/yaml_spec.rb index 28039e99916..a52c1c362e1 100644 --- a/spec/lib/gitlab/config/loader/yaml_spec.rb +++ b/spec/lib/gitlab/config/loader/yaml_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::Config::Loader::Yaml do let(:loader) { described_class.new(yml) } context 'when yaml syntax is correct' do - let(:yml) { 'image: ruby:2.2' } + let(:yml) { 'image: ruby:2.7' } describe '#valid?' do it 'returns true' do @@ -16,7 +16,7 @@ describe Gitlab::Config::Loader::Yaml do describe '#load!' do it 'returns a valid hash' do - expect(loader.load!).to eq(image: 'ruby:2.2') + expect(loader.load!).to eq(image: 'ruby:2.7') end end end diff --git a/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb b/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb index d72d41ddf38..3ce950d6a64 100644 --- a/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb @@ -55,9 +55,11 @@ describe Gitlab::ImportExport::SnippetRepoRestorer do let(:snippet_bundle_path) { File.join(bundle_path, "#{snippet_with_repo.hexdigest}.bundle") } let(:result) { exporter.save } - it 'creates the repository from the bundle' do + before do expect(exporter.save).to be_truthy + end + it 'creates the repository from the bundle' do expect(snippet.repository_exists?).to be_falsey expect(snippet.snippet_repository).to be_nil expect(snippet.repository).to receive(:create_from_bundle).and_call_original @@ -66,5 +68,14 @@ describe Gitlab::ImportExport::SnippetRepoRestorer do expect(snippet.repository_exists?).to be_truthy expect(snippet.snippet_repository).not_to be_nil end + + it 'sets same shard in snippet repository as in the repository storage' do + expect(snippet).to receive(:repository_storage).and_return('picked') + expect(snippet.repository).to receive(:create_from_bundle) + + restorer.restore + + expect(snippet.snippet_repository.shard_name).to eq 'picked' + end end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 8c7969af177..88c26c92417 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1882,7 +1882,7 @@ describe Ci::Build do describe '#options' do let(:options) do { - image: "ruby:2.1", + image: "ruby:2.7", services: ["postgres"], script: ["ls -a"] } @@ -1893,11 +1893,11 @@ describe Ci::Build do end it 'allows to access with keys' do - expect(build.options[:image]).to eq('ruby:2.1') + expect(build.options[:image]).to eq('ruby:2.7') end it 'allows to access with strings' do - expect(build.options['image']).to eq('ruby:2.1') + expect(build.options['image']).to eq('ruby:2.7') end context 'when ci_build_metadata_config is set' do diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index c622b7706c6..8b6fa36eaa5 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -84,6 +84,10 @@ describe BuildkiteService, :use_clean_rails_memory_store_caching do describe '#calculate_reactive_cache' do describe '#commit_status' do + let(:buildkite_full_url) do + 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=123' + end + subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] } it 'sets commit status to :error when status is 500' do @@ -103,13 +107,25 @@ describe BuildkiteService, :use_clean_rails_memory_store_caching do is_expected.to eq('Great Success') end + + Gitlab::HTTP::HTTP_ERRORS.each do |http_error| + it "sets commit status to :error with a #{http_error.name} error" do + WebMock.stub_request(:get, buildkite_full_url) + .to_raise(http_error) + + expect(Gitlab::ErrorTracking) + .to receive(:log_exception) + .with(instance_of(http_error), project_id: project.id) + + is_expected.to eq(:error) + end + end end end end def stub_request(status: 200, body: nil) body ||= %q({"status":"success"}) - buildkite_full_url = 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=123' stub_full_request(buildkite_full_url) .to_return(status: status, diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index 0639a4c1f23..1ee9c5f90c6 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -106,6 +106,10 @@ describe DroneCiService, :use_clean_rails_memory_store_caching do WebMock.stub_request(:get, commit_status_path) .to_raise(http_error) + expect(Gitlab::ErrorTracking) + .to receive(:log_exception) + .with(instance_of(http_error), project_id: project.id) + is_expected.to eq(:error) end end diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb index c31fe192367..dc9f9a95d24 100644 --- a/spec/models/snippet_repository_spec.rb +++ b/spec/models/snippet_repository_spec.rb @@ -16,7 +16,7 @@ describe SnippetRepository do describe '.find_snippet' do it 'finds snippet by disk path' do snippet = create(:snippet, author: user) - snippet.track_snippet_repository + snippet.track_snippet_repository(snippet.repository.storage) expect(described_class.find_snippet(snippet.disk_path)).to eq(snippet) end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 0e19dfc147b..8ce3718e3c1 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -567,18 +567,21 @@ describe Snippet do describe '#track_snippet_repository' do let(:snippet) { create(:snippet) } + let(:shard_name) { 'foo' } + + subject { snippet.track_snippet_repository(shard_name) } context 'when a snippet repository entry does not exist' do it 'creates a new entry' do - expect { snippet.track_snippet_repository }.to change(snippet, :snippet_repository) + expect { subject }.to change(snippet, :snippet_repository) end it 'tracks the snippet storage location' do - snippet.track_snippet_repository + subject expect(snippet.snippet_repository).to have_attributes( disk_path: snippet.disk_path, - shard_name: snippet.repository_storage + shard_name: shard_name ) end end @@ -586,21 +589,20 @@ describe Snippet do context 'when a tracking entry exists' do let!(:snippet) { create(:snippet, :repository) } let(:snippet_repository) { snippet.snippet_repository } - let!(:shard) { create(:shard, name: 'foo') } + let(:shard_name) { 'bar' } it 'does not create a new entry in the database' do - expect { snippet.track_snippet_repository }.not_to change(snippet, :snippet_repository) + expect { subject }.not_to change(snippet, :snippet_repository) end it 'updates the snippet storage location' do allow(snippet).to receive(:disk_path).and_return('fancy/new/path') - allow(snippet).to receive(:repository_storage).and_return('foo') - snippet.track_snippet_repository + subject expect(snippet.snippet_repository).to have_attributes( disk_path: 'fancy/new/path', - shard_name: 'foo' + shard_name: shard_name ) end end @@ -609,19 +611,31 @@ describe Snippet do describe '#create_repository' do let(:snippet) { create(:snippet) } + subject { snippet.create_repository } + it 'creates the repository' do expect(snippet.repository).to receive(:after_create).and_call_original - expect(snippet.create_repository).to be_truthy + expect(subject).to be_truthy expect(snippet.repository.exists?).to be_truthy end it 'tracks snippet repository' do expect do - snippet.create_repository + subject end.to change(SnippetRepository, :count).by(1) end + it 'sets same shard in snippet repository as in the repository storage' do + expect(snippet).to receive(:repository_storage).and_return('picked') + expect(snippet).to receive(:repository_exists?).and_return(false) + expect(snippet.repository).to receive(:create_if_not_exists) + + subject + + expect(snippet.snippet_repository.shard_name).to eq 'picked' + end + context 'when repository exists' do let!(:snippet) { create(:snippet, :repository) } diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb index 5815ce07125..71c2619d898 100644 --- a/spec/requests/api/lint_spec.rb +++ b/spec/requests/api/lint_spec.rb @@ -29,7 +29,7 @@ describe API::Lint do end it "responds with errors about invalid configuration" do - post api('/ci/lint'), params: { content: '{ image: "ruby:2.1", services: ["postgres"] }' } + post api('/ci/lint'), params: { content: '{ image: "ruby:2.7", services: ["postgres"] }' } expect(response).to have_gitlab_http_status(:ok) expect(json_response['status']).to eq('invalid') diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 5a8add1e9db..03560cae775 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -523,7 +523,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(json_response['token']).to eq(job.token) expect(json_response['job_info']).to eq(expected_job_info) expect(json_response['git_info']).to eq(expected_git_info) - expect(json_response['image']).to eq({ 'name' => 'ruby:2.1', 'entrypoint' => '/bin/sh', 'ports' => [] }) + expect(json_response['image']).to eq({ 'name' => 'ruby:2.7', 'entrypoint' => '/bin/sh', 'ports' => [] }) expect(json_response['services']).to eq([{ 'name' => 'postgres', 'entrypoint' => nil, 'alias' => nil, 'command' => nil, 'ports' => [] }, { 'name' => 'docker:stable-dind', 'entrypoint' => '/bin/sh', diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 3b819c795b2..681ce9669e2 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -555,7 +555,7 @@ describe Ci::CreatePipelineService do let(:ci_yaml) do <<-EOS image: - name: ruby:2.2 + name: ruby:2.7 ports: - 80 EOS @@ -567,12 +567,12 @@ describe Ci::CreatePipelineService do context 'in the job image' do let(:ci_yaml) do <<-EOS - image: ruby:2.2 + image: ruby:2.7 test: script: rspec image: - name: ruby:2.2 + name: ruby:2.7 ports: - 80 EOS @@ -584,11 +584,11 @@ describe Ci::CreatePipelineService do context 'in the service' do let(:ci_yaml) do <<-EOS - image: ruby:2.2 + image: ruby:2.7 test: script: rspec - image: ruby:2.2 + image: ruby:2.7 services: - name: test ports: 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 96cddef4628..01f09f208fd 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,8 @@ 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_by_digest) + expect_any_instance_of(Projects::ContainerRepository::DeleteTagsService) + .not_to receive(:execute) is_expected.to include(status: :success, deleted: []) end @@ -49,15 +50,10 @@ describe Projects::ContainerRepository::CleanupTagsService do context 'when regex matching everything is specified' do shared_examples 'removes all matches' do - it 'does remove B* and C' do - # The :A cannot be removed as config is shared with :latest - # The :E cannot be removed as it does not have valid manifest + it 'does remove all tags except latest' do + expect_delete(%w(A Ba Bb C D E)) - expect_delete('sha256:configB').twice - expect_delete('sha256:configC') - expect_delete('sha256:configD') - - is_expected.to include(status: :success, deleted: %w(D Bb Ba C)) + is_expected.to include(status: :success, deleted: %w(A Ba Bb C D E)) end end @@ -82,10 +78,9 @@ describe Projects::ContainerRepository::CleanupTagsService do end it 'does remove C and D' do - expect_delete('sha256:configC') - expect_delete('sha256:configD') + expect_delete(%w(C D)) - is_expected.to include(status: :success, deleted: %w(D C)) + is_expected.to include(status: :success, deleted: %w(C D)) end context 'with overriding allow regex' do @@ -95,7 +90,7 @@ describe Projects::ContainerRepository::CleanupTagsService do end it 'does not remove C' do - expect_delete('sha256:configD') + expect_delete(%w(D)) is_expected.to include(status: :success, deleted: %w(D)) end @@ -108,36 +103,52 @@ describe Projects::ContainerRepository::CleanupTagsService do end it 'does not remove C' do - expect_delete('sha256:configD') + expect_delete(%w(D)) is_expected.to include(status: :success, deleted: %w(D)) end end end - context 'when removing a tagged image that is used by another tag' do + context 'with allow regex value' do let(:params) do - { 'name_regex_delete' => 'Ba' } + { 'name_regex_delete' => '.*', + 'name_regex_keep' => 'B.*' } end - it 'does not remove the tag' do - # Issue: https://gitlab.com/gitlab-org/gitlab-foss/issues/21405 + it 'does not remove B*' do + expect_delete(%w(A C D E)) - is_expected.to include(status: :success, deleted: []) + is_expected.to include(status: :success, deleted: %w(A C D E)) end end - context 'with allow regex value' do + context 'when keeping only N tags' do let(:params) do - { 'name_regex_delete' => '.*', - 'name_regex_keep' => 'B.*' } + { 'name_regex' => 'A|B.*|C', + 'keep_n' => 1 } end - it 'does not remove B*' do - expect_delete('sha256:configC') - expect_delete('sha256:configD') + it 'sorts tags by date' do + expect_delete(%w(Bb Ba C)) + + expect(service).to receive(:order_by_date).and_call_original - is_expected.to include(status: :success, deleted: %w(D C)) + is_expected.to include(status: :success, deleted: %w(Bb Ba C)) + end + end + + context 'when not keeping N tags' do + let(:params) do + { 'name_regex' => 'A|B.*|C' } + end + + it 'does not sort tags by date' do + expect_delete(%w(A Ba Bb C)) + + expect(service).not_to receive(:order_by_date) + + is_expected.to include(status: :success, deleted: %w(A Ba Bb C)) end end @@ -147,10 +158,10 @@ describe Projects::ContainerRepository::CleanupTagsService do 'keep_n' => 3 } end - it 'does remove C as it is oldest' do - expect_delete('sha256:configC') + it 'does remove B* and C as they are the oldest' do + expect_delete(%w(Bb Ba C)) - is_expected.to include(status: :success, deleted: %w(C)) + is_expected.to include(status: :success, deleted: %w(Bb Ba C)) end end @@ -161,10 +172,9 @@ describe Projects::ContainerRepository::CleanupTagsService do end it 'does remove B* and C as they are older than 1 day' do - expect_delete('sha256:configB').twice - expect_delete('sha256:configC') + expect_delete(%w(Ba Bb C)) - is_expected.to include(status: :success, deleted: %w(Bb Ba C)) + is_expected.to include(status: :success, deleted: %w(Ba Bb C)) end end @@ -176,8 +186,7 @@ describe Projects::ContainerRepository::CleanupTagsService do end it 'does remove B* and C' do - expect_delete('sha256:configB').twice - expect_delete('sha256:configC') + expect_delete(%w(Bb Ba C)) is_expected.to include(status: :success, deleted: %w(Bb Ba C)) end @@ -195,8 +204,7 @@ describe Projects::ContainerRepository::CleanupTagsService do end it 'succeeds without a user' do - expect_delete('sha256:configB').twice - expect_delete('sha256:configC') + expect_delete(%w(Bb Ba C)) is_expected.to include(status: :success, deleted: %w(Bb Ba C)) end @@ -238,9 +246,14 @@ describe Projects::ContainerRepository::CleanupTagsService do end end - def expect_delete(digest) - expect_any_instance_of(ContainerRegistry::Client) - .to receive(:delete_repository_tag_by_digest) - .with(repository.path, digest) { true } + def expect_delete(tags) + expect(Projects::ContainerRepository::DeleteTagsService) + .to receive(:new) + .with(repository.project, user, tags: tags) + .and_call_original + + expect_any_instance_of(Projects::ContainerRepository::DeleteTagsService) + .to receive(:execute) + .with(repository) { { status: :success, deleted: tags } } end end |