diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-28 21:09:07 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-28 21:09:07 +0300 |
commit | 1c8fa70f9d0818e2a82089c8643a6e455bca47fd (patch) | |
tree | f339f97de0425270bdd909e2f4d378927b6e0a18 /spec | |
parent | 736d36d8597d0d1ec1b47644e6d091c3f4a78f45 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
24 files changed, 539 insertions, 174 deletions
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb index 4e8cb3f94fb..fbf88a01eb3 100644 --- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb @@ -210,4 +210,16 @@ describe Groups::Settings::CiCdController do end end end + + describe 'POST create_deploy_token' do + it_behaves_like 'a created deploy token' do + let(:entity) { group } + let(:create_entity_params) { { group_id: group } } + let(:deploy_token_type) { DeployToken.deploy_token_types[:group_type] } + + before do + entity.add_owner(user) + end + end + end end diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 68260e4e101..a8631389e17 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -247,4 +247,12 @@ describe Projects::Settings::CiCdController do end end end + + describe 'POST create_deploy_token' do + it_behaves_like 'a created deploy token' do + let(:entity) { project } + let(:create_entity_params) { { namespace_id: project.namespace, project_id: project } } + let(:deploy_token_type) { DeployToken.deploy_token_types[:project_type] } + end + end end diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb index 64f5b8e34ae..67ae9ebda38 100644 --- a/spec/controllers/projects/settings/repository_controller_spec.rb +++ b/spec/controllers/projects/settings/repository_controller_spec.rb @@ -32,24 +32,4 @@ describe Projects::Settings::RepositoryController do expect(RepositoryCleanupWorker).to have_received(:perform_async).once end end - - describe 'POST create_deploy_token' do - let(:deploy_token_params) do - { - name: 'deployer_token', - expires_at: 1.month.from_now.to_date.to_s, - username: 'deployer', - read_repository: '1' - } - end - - subject(:create_deploy_token) { post :create_deploy_token, params: { namespace_id: project.namespace, project_id: project, deploy_token: deploy_token_params } } - - it 'creates deploy token' do - expect { create_deploy_token }.to change { DeployToken.active.count }.by(1) - - expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template(:show) - end - end end diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb index 5b1a9512c55..3fbc7c7a695 100644 --- a/spec/features/groups/settings/ci_cd_spec.rb +++ b/spec/features/groups/settings/ci_cd_spec.rb @@ -37,6 +37,19 @@ describe 'Group CI/CD settings' do end end + context 'Deploy tokens' do + let!(:deploy_token) { create(:deploy_token, :group, groups: [group]) } + + before do + stub_container_registry_config(enabled: true) + visit group_settings_ci_cd_path(group) + end + + it_behaves_like 'a deploy token in ci/cd settings' do + let(:entity_type) { 'group' } + end + end + describe 'Auto DevOps form' do before do stub_application_setting(auto_devops_enabled: true) diff --git a/spec/features/projects/settings/ci_cd_settings_spec.rb b/spec/features/projects/settings/ci_cd_settings_spec.rb new file mode 100644 index 00000000000..e69ee31e582 --- /dev/null +++ b/spec/features/projects/settings/ci_cd_settings_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Projects > Settings > CI/CD settings' do + let(:project) { create(:project_empty_repo) } + let(:user) { create(:user) } + let(:role) { :maintainer } + + context 'Deploy tokens' do + let!(:deploy_token) { create(:deploy_token, projects: [project]) } + + before do + project.add_role(user, role) + sign_in(user) + stub_container_registry_config(enabled: true) + visit project_settings_ci_cd_path(project) + end + + it_behaves_like 'a deploy token in ci/cd settings' do + let(:entity_type) { 'project' } + end + end +end diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index 18031a40f15..d750234d9ad 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -108,39 +108,6 @@ describe 'Projects > Settings > Repository settings' do end end - context 'Deploy tokens' do - let!(:deploy_token) { create(:deploy_token, projects: [project]) } - - before do - stub_container_registry_config(enabled: true) - visit project_settings_repository_path(project) - end - - it 'view deploy tokens' do - within('.deploy-tokens') do - expect(page).to have_content(deploy_token.name) - expect(page).to have_content('read_repository') - expect(page).to have_content('read_registry') - end - end - - it 'add a new deploy token' do - fill_in 'deploy_token_name', with: 'new_deploy_key' - fill_in 'deploy_token_expires_at', with: (Date.today + 1.month).to_s - fill_in 'deploy_token_username', with: 'deployer' - check 'deploy_token_read_repository' - check 'deploy_token_read_registry' - click_button 'Create deploy token' - - expect(page).to have_content('Your new project deploy token has been created') - - within('.created-deploy-token-container') do - expect(page).to have_selector("input[name='deploy-token-user'][value='deployer']") - expect(page).to have_selector("input[name='deploy-token'][readonly='readonly']") - end - end - end - context 'remote mirror settings' do let(:user2) { create(:user) } diff --git a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb index 3e9bfed1e47..a9253c20896 100644 --- a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb +++ b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb @@ -11,7 +11,7 @@ describe 'Repository Settings > User sees revoke deploy token modal', :js do before do project.add_role(user, role) sign_in(user) - visit(project_settings_repository_path(project)) + visit(project_settings_ci_cd_path(project)) click_link('Revoke') end diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/frontend/behaviors/quick_submit_spec.js index 7af8c984841..2dc2bb198e8 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/frontend/behaviors/quick_submit_spec.js @@ -1,70 +1,72 @@ import $ from 'jquery'; import '~/behaviors/quick_submit'; -describe('Quick Submit behavior', function() { +describe('Quick Submit behavior', () => { + let testContext; + const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options); preloadFixtures('snippets/show.html'); beforeEach(() => { loadFixtures('snippets/show.html'); + + testContext = {}; + + testContext.spies = { + submit: jest.fn(), + }; + $('form').submit(e => { // Prevent a form submit from moving us off the testing page e.preventDefault(); + // Explicitly call the spie to know this function get's not called + testContext.spies.submit(); }); - this.spies = { - submit: spyOnEvent('form', 'submit'), - }; - - this.textarea = $('.js-quick-submit textarea').first(); - }); - - afterEach(() => { - // Undo what we did to the shared <body> - $('body').removeAttr('data-page'); + testContext.textarea = $('.js-quick-submit textarea').first(); }); it('does not respond to other keyCodes', () => { - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ keyCode: 32, }), ); - expect(this.spies.submit).not.toHaveBeenTriggered(); + expect(testContext.spies.submit).not.toHaveBeenCalled(); }); it('does not respond to Enter alone', () => { - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ ctrlKey: false, metaKey: false, }), ); - expect(this.spies.submit).not.toHaveBeenTriggered(); + expect(testContext.spies.submit).not.toHaveBeenCalled(); }); it('does not respond to repeated events', () => { - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ repeat: true, }), ); - expect(this.spies.submit).not.toHaveBeenTriggered(); + expect(testContext.spies.submit).not.toHaveBeenCalled(); }); it('disables input of type submit', () => { const submitButton = $('.js-quick-submit input[type=submit]'); - this.textarea.trigger(keydownEvent()); + testContext.textarea.trigger(keydownEvent()); expect(submitButton).toBeDisabled(); }); it('disables button of type submit', () => { const submitButton = $('.js-quick-submit input[type=submit]'); - this.textarea.trigger(keydownEvent()); + testContext.textarea.trigger(keydownEvent()); expect(submitButton).toBeDisabled(); }); @@ -73,71 +75,79 @@ describe('Quick Submit behavior', function() { const existingSubmit = $('.js-quick-submit input[type=submit]'); // Add an extra submit button const newSubmit = $('<button type="submit">Submit it</button>'); - newSubmit.insertAfter(this.textarea); + newSubmit.insertAfter(testContext.textarea); - const oldClick = spyOnEvent(existingSubmit, 'click'); - const newClick = spyOnEvent(newSubmit, 'click'); + const spies = { + oldClickSpy: jest.fn(), + newClickSpy: jest.fn(), + }; + existingSubmit.on('click', () => { + spies.oldClickSpy(); + }); + newSubmit.on('click', () => { + spies.newClickSpy(); + }); - this.textarea.trigger(keydownEvent()); + testContext.textarea.trigger(keydownEvent()); - expect(oldClick).not.toHaveBeenTriggered(); - expect(newClick).toHaveBeenTriggered(); + expect(spies.oldClickSpy).not.toHaveBeenCalled(); + expect(spies.newClickSpy).toHaveBeenCalled(); }); // We cannot stub `navigator.userAgent` for CI's `rake karma` task, so we'll // only run the tests that apply to the current platform if (navigator.userAgent.match(/Macintosh/)) { describe('In Macintosh', () => { it('responds to Meta+Enter', () => { - this.textarea.trigger(keydownEvent()); + testContext.textarea.trigger(keydownEvent()); - expect(this.spies.submit).toHaveBeenTriggered(); + expect(testContext.spies.submit).toHaveBeenCalled(); }); it('excludes other modifier keys', () => { - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ altKey: true, }), ); - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ ctrlKey: true, }), ); - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ shiftKey: true, }), ); - expect(this.spies.submit).not.toHaveBeenTriggered(); + expect(testContext.spies.submit).not.toHaveBeenCalled(); }); }); } else { it('responds to Ctrl+Enter', () => { - this.textarea.trigger(keydownEvent()); + testContext.textarea.trigger(keydownEvent()); - expect(this.spies.submit).toHaveBeenTriggered(); + expect(testContext.spies.submit).toHaveBeenCalled(); }); it('excludes other modifier keys', () => { - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ altKey: true, }), ); - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ metaKey: true, }), ); - this.textarea.trigger( + testContext.textarea.trigger( keydownEvent({ shiftKey: true, }), ); - expect(this.spies.submit).not.toHaveBeenTriggered(); + expect(testContext.spies.submit).not.toHaveBeenCalled(); }); } }); diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index 48f3b0f9b65..2b83f7e7351 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -1,5 +1,5 @@ import { mount } from '@vue/test-utils'; -import { GlTable, GlPagination, GlLoadingIcon } from '@gitlab/ui'; +import { GlTable, GlPagination, GlSkeletonLoader } from '@gitlab/ui'; import Tracking from '~/tracking'; import stubChildren from 'helpers/stub_children'; import component from '~/registry/explorer/pages/details.vue'; @@ -14,8 +14,7 @@ describe('Details Page', () => { const findDeleteModal = () => wrapper.find(GlModal); const findPagination = () => wrapper.find(GlPagination); - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); - const findTagsTable = () => wrapper.find(GlTable); + const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader); const findMainCheckbox = () => wrapper.find({ ref: 'mainCheckbox' }); const findFirstRowItem = ref => wrapper.find({ ref }); const findBulkDeleteButton = () => wrapper.find({ ref: 'bulkDeleteButton' }); @@ -33,7 +32,7 @@ describe('Details Page', () => { ...stubChildren(component), GlModal, GlSprintf: false, - GlTable: false, + GlTable, }, mocks: { $route: { @@ -53,18 +52,19 @@ describe('Details Page', () => { }); describe('when isLoading is true', () => { - beforeAll(() => store.commit(SET_MAIN_LOADING, true)); + beforeEach(() => { + store.dispatch('receiveTagsListSuccess', { ...tagsListResponse, data: [] }); + store.commit(SET_MAIN_LOADING, true); + }); afterAll(() => store.commit(SET_MAIN_LOADING, false)); - it('has a loading icon', () => { - expect(findLoadingIcon().exists()).toBe(true); + it('has a skeleton loader', () => { + expect(findSkeletonLoader().exists()).toBe(true); }); - it('does not have a main content', () => { - expect(findTagsTable().exists()).toBe(false); - expect(findPagination().exists()).toBe(false); - expect(findDeleteModal().exists()).toBe(false); + it('does not have list items', () => { + expect(findFirstRowItem('rowCheckbox').exists()).toBe(false); }); }); diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js index f463dc49035..91c3c242ed4 100644 --- a/spec/frontend/registry/explorer/pages/list_spec.js +++ b/spec/frontend/registry/explorer/pages/list_spec.js @@ -1,6 +1,6 @@ import VueRouter from 'vue-router'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlPagination, GlLoadingIcon, GlSprintf } from '@gitlab/ui'; +import { GlPagination, GlSkeletonLoader, GlSprintf } from '@gitlab/ui'; import Tracking from '~/tracking'; import component from '~/registry/explorer/pages/list.vue'; import store from '~/registry/explorer/stores/'; @@ -17,7 +17,7 @@ describe('List Page', () => { const findDeleteBtn = () => wrapper.find({ ref: 'deleteImageButton' }); const findDeleteModal = () => wrapper.find(GlModal); - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); + const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader); const findImagesList = () => wrapper.find({ ref: 'imagesList' }); const findRowItems = () => wrapper.findAll({ ref: 'rowItem' }); const findEmptyState = () => wrapper.find(GlEmptyState); @@ -71,7 +71,7 @@ describe('List Page', () => { }); it('should not show the loading or default state', () => { - expect(findLoadingIcon().exists()).toBe(false); + expect(findSkeletonLoader().exists()).toBe(false); expect(findImagesList().exists()).toBe(false); }); }); @@ -81,8 +81,8 @@ describe('List Page', () => { afterAll(() => store.commit(SET_MAIN_LOADING, false)); - it('shows the loading icon', () => { - expect(findLoadingIcon().exists()).toBe(true); + it('shows the skeleton loader', () => { + expect(findSkeletonLoader().exists()).toBe(true); }); it('imagesList is not visible', () => { diff --git a/spec/frontend/registry/explorer/stores/actions_spec.js b/spec/frontend/registry/explorer/stores/actions_spec.js index a3fb29c0eb9..3e22621058e 100644 --- a/spec/frontend/registry/explorer/stores/actions_spec.js +++ b/spec/frontend/registry/explorer/stores/actions_spec.js @@ -180,10 +180,7 @@ describe('Actions RegistryExplorer Store', () => { { tagsPagination: {}, }, - [ - { type: types.SET_MAIN_LOADING, payload: true }, - { type: types.SET_MAIN_LOADING, payload: false }, - ], + [{ type: types.SET_MAIN_LOADING, payload: true }], [ { type: 'requestTagsList', @@ -220,13 +217,11 @@ describe('Actions RegistryExplorer Store', () => { }); describe('request delete multiple tags', () => { - const id = 1; - const params = window.btoa(JSON.stringify({ id })); - const projectPath = 'project-path'; - const url = `${projectPath}/registry/repository/${id}/tags/bulk_destroy`; + const url = `project-path/registry/repository/foo/tags`; + const params = window.btoa(JSON.stringify({ tags_path: `${url}?format=json` })); it('successfully performs the delete request', done => { - mock.onDelete(url).replyOnce(200); + mock.onDelete(`${url}/bulk_destroy`).replyOnce(200); testAction( actions.requestDeleteTags, @@ -235,15 +230,9 @@ describe('Actions RegistryExplorer Store', () => { params, }, { - config: { - projectPath, - }, tagsPagination: {}, }, - [ - { type: types.SET_MAIN_LOADING, payload: true }, - { type: types.SET_MAIN_LOADING, payload: false }, - ], + [{ type: types.SET_MAIN_LOADING, payload: true }], [ { type: 'requestTagsList', @@ -267,9 +256,6 @@ describe('Actions RegistryExplorer Store', () => { params, }, { - config: { - projectPath, - }, tagsPagination: {}, }, [ diff --git a/spec/frontend/registry/explorer/stores/getters_spec.js b/spec/frontend/registry/explorer/stores/getters_spec.js new file mode 100644 index 00000000000..c224f076d30 --- /dev/null +++ b/spec/frontend/registry/explorer/stores/getters_spec.js @@ -0,0 +1,34 @@ +import * as getters from '~/registry/explorer/stores/getters'; + +describe('Getters RegistryExplorer store', () => { + let state; + const tags = ['foo', 'bar']; + + describe('tags', () => { + describe('when isLoading is false', () => { + beforeEach(() => { + state = { + tags, + isLoading: false, + }; + }); + + it('returns tags', () => { + expect(getters.tags(state)).toEqual(state.tags); + }); + }); + + describe('when isLoading is true', () => { + beforeEach(() => { + state = { + tags, + isLoading: true, + }; + }); + + it('returns empty array', () => { + expect(getters.tags(state)).toEqual([]); + }); + }); + }); +}); diff --git a/spec/lib/gitlab/config_checker/puma_rugged_checker_spec.rb b/spec/lib/gitlab/config_checker/puma_rugged_checker_spec.rb index 070887c83c3..badfd56d571 100644 --- a/spec/lib/gitlab/config_checker/puma_rugged_checker_spec.rb +++ b/spec/lib/gitlab/config_checker/puma_rugged_checker_spec.rb @@ -15,26 +15,10 @@ describe Gitlab::ConfigChecker::PumaRuggedChecker do end context 'application is puma' do - let(:notice_running_puma) do - { - type: 'info', - message: 'You are running Puma, which is currently experimental. '\ - 'More information is available in our '\ - '<a href="https://docs.gitlab.com/ee/administration/operations/puma.html">documentation</a>.' - } - end - let(:notice_multi_threaded_puma) do - { - type: 'info', - message: 'Puma is running with a thread count above 1. '\ - 'Information on deprecated GitLab features in this configuration is available in the '\ - '<a href="https://docs.gitlab.com/ee/administration/operations/puma.html">documentation</a>.'\ - } - end let(:notice_multi_threaded_puma_with_rugged) do { type: 'warning', - message: 'Puma is running with a thread count above 1 and the rugged '\ + message: 'Puma is running with a thread count above 1 and the Rugged '\ 'service is enabled. This may decrease performance in some environments. '\ 'See our <a href="https://docs.gitlab.com/ee/administration/operations/puma.html#performance-caveat-when-using-puma-with-rugged">documentation</a> '\ 'for details of this issue.' @@ -51,35 +35,29 @@ describe Gitlab::ConfigChecker::PumaRuggedChecker do let(:multithreaded_puma) { false } let(:rugged_enabled) { true } - it 'report running puma notice' do - is_expected.to contain_exactly(notice_running_puma) - end + it { is_expected.to be_empty } end context 'not multithreaded_puma and rugged API is not enabled' do let(:multithreaded_puma) { false } let(:rugged_enabled) { false } - it 'report running puma notice' do - is_expected.to contain_exactly(notice_running_puma) - end + it { is_expected.to be_empty } end context 'multithreaded_puma and rugged API is not enabled' do let(:multithreaded_puma) { true } let(:rugged_enabled) { false } - it 'report running puma notice and multi-thread puma notice' do - is_expected.to contain_exactly(notice_running_puma, notice_multi_threaded_puma) - end + it { is_expected.to be_empty } end context 'multithreaded_puma and rugged API is enabled' do let(:multithreaded_puma) { true } let(:rugged_enabled) { true } - it 'report puma/multi_threaded_puma/multi_threaded_puma_with_rugged notices' do - is_expected.to contain_exactly(notice_running_puma, notice_multi_threaded_puma, notice_multi_threaded_puma_with_rugged) + it 'report multi_threaded_puma_with_rugged notices' do + is_expected.to contain_exactly(notice_multi_threaded_puma_with_rugged) end end end diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb index 9befbb02b17..5b16b945efe 100644 --- a/spec/models/snippet_repository_spec.rb +++ b/spec/models/snippet_repository_spec.rb @@ -3,6 +3,11 @@ require 'spec_helper' describe SnippetRepository do + let_it_be(:user) { create(:user) } + let(:snippet) { create(:personal_snippet, :repository, author: user) } + let(:snippet_repository) { snippet.snippet_repository } + let(:commit_opts) { { branch_name: 'master', message: 'whatever' } } + describe 'associations' do it { is_expected.to belong_to(:shard) } it { is_expected.to belong_to(:snippet) } @@ -10,7 +15,7 @@ describe SnippetRepository do describe '.find_snippet' do it 'finds snippet by disk path' do - snippet = create(:snippet) + snippet = create(:snippet, author: user) snippet.track_snippet_repository expect(described_class.find_snippet(snippet.disk_path)).to eq(snippet) @@ -20,4 +25,147 @@ describe SnippetRepository do expect(described_class.find_snippet('@@unexisting/path/to/snippet')).to be_nil end end + + describe '#multi_files_action' do + let(:new_file) { { file_path: 'new_file_test', content: 'bar' } } + let(:move_file) { { previous_path: 'CHANGELOG', file_path: 'CHANGELOG_new', content: 'bar' } } + let(:update_file) { { previous_path: 'README', file_path: 'README', content: 'bar' } } + let(:data) { [new_file, move_file, update_file] } + + it 'returns nil when files argument is empty' do + expect(snippet.repository).not_to receive(:multi_action) + + operation = snippet_repository.multi_files_action(user, [], commit_opts) + + expect(operation).to be_nil + end + + it 'returns nil when files argument is nil' do + expect(snippet.repository).not_to receive(:multi_action) + + operation = snippet_repository.multi_files_action(user, nil, commit_opts) + + expect(operation).to be_nil + end + + it 'performs the operation accordingly to the files data' do + new_file_blob = blob_at(snippet, new_file[:file_path]) + move_file_blob = blob_at(snippet, move_file[:previous_path]) + update_file_blob = blob_at(snippet, update_file[:previous_path]) + + aggregate_failures do + expect(new_file_blob).to be_nil + expect(move_file_blob).not_to be_nil + expect(update_file_blob).not_to be_nil + end + + expect do + snippet_repository.multi_files_action(user, data, commit_opts) + end.not_to raise_error + + aggregate_failures do + data.each do |entry| + blob = blob_at(snippet, entry[:file_path]) + + expect(blob).not_to be_nil + expect(blob.path).to eq entry[:file_path] + expect(blob.data).to eq entry[:content] + end + end + end + + it 'tries to obtain an exclusive lease' do + expect(Gitlab::ExclusiveLease).to receive(:new).with("multi_files_action:#{snippet.id}", anything).and_call_original + + snippet_repository.multi_files_action(user, data, commit_opts) + end + + it 'cancels the lease when the method has finished' do + expect(Gitlab::ExclusiveLease).to receive(:cancel).with("multi_files_action:#{snippet.id}", anything).and_call_original + + snippet_repository.multi_files_action(user, data, commit_opts) + end + + it 'raises an error if the lease cannot be obtained' do + allow_next_instance_of(Gitlab::ExclusiveLease) do |instance| + allow(instance).to receive(:try_obtain).and_return false + end + + expect do + snippet_repository.multi_files_action(user, data, commit_opts) + end.to raise_error(described_class::CommitError) + end + + context 'with commit actions' do + let(:result) do + [{ action: :create }.merge(new_file), + { action: :move }.merge(move_file), + { action: :update }.merge(update_file)] + end + let(:repo) { double } + + before do + allow(snippet).to receive(:repository).and_return(repo) + allow(repo).to receive(:ls_files).and_return([]) + end + + it 'infers the commit action based on the parameters if not present' do + expect(repo).to receive(:multi_action).with(user, hash_including(actions: result)) + + snippet_repository.multi_files_action(user, data, commit_opts) + end + + context 'when commit actions are present' do + let(:file_action) { { file_path: 'foo.txt', content: 'foo', action: :foobar } } + let(:data) { [file_action] } + + it 'does not change commit action' do + expect(repo).to( + receive(:multi_action).with( + user, + hash_including(actions: array_including(hash_including(action: :foobar))))) + + snippet_repository.multi_files_action(user, data, commit_opts) + end + end + end + + context 'when files are not named' do + let(:data) do + [ + { + file_path: '', + content: 'foo', + action: :create + }, + { + file_path: '', + content: 'bar', + action: :create + }, + { + file_path: 'foo.txt', + content: 'bar', + action: :create + } + ] + end + + it 'sets a name for non named files' do + expect do + snippet_repository.multi_files_action(user, data, commit_opts) + end.not_to raise_error + + expect(snippet.repository.ls_files(nil)).to include('snippetfile1.txt', 'snippetfile2.txt', 'foo.txt') + end + end + end + + def blob_at(snippet, path) + snippet.repository.blob_at('master', path) + end + + def first_blob(snippet) + snippet.repository.blob_at('master', snippet.repository.ls_files(nil).first) + end end diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 7efc426c81d..16903d9d6d0 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -98,6 +98,36 @@ describe API::ProjectSnippets do } end + shared_examples 'project snippet repository actions' do + let(:snippet) { ProjectSnippet.find(json_response['id']) } + + it 'creates repository' do + subject + + expect(snippet.repository.exists?).to be_truthy + end + + it 'commit the files to the repository' do + subject + + blob = snippet.repository.blob_at('master', params[:file_name]) + + expect(blob.data).to eq params[:code] + end + + context 'when feature flag :version_snippets is disabled' do + it 'does not create snippet repository' do + stub_feature_flags(version_snippets: false) + + expect do + subject + end.to change { ProjectSnippet.count }.by(1) + + expect(snippet.repository_exists?).to be_falsey + end + end + end + context 'with a regular user' do let(:user) { create(:user) } @@ -118,6 +148,10 @@ describe API::ProjectSnippets do expect(snippet.file_name).to eq(params[:file_name]) expect(snippet.visibility_level).to eq(Snippet::INTERNAL) end + + it_behaves_like 'project snippet repository actions' do + subject { post api("/projects/#{project.id}/snippets/", user), params: params } + end end it 'creates a new snippet' do @@ -132,6 +166,10 @@ describe API::ProjectSnippets do expect(snippet.visibility_level).to eq(Snippet::PUBLIC) end + it_behaves_like 'project snippet repository actions' do + subject { post api("/projects/#{project.id}/snippets/", admin), params: params } + end + it 'creates a new snippet with content parameter' do params[:content] = params.delete(:code) diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb index 21565265b99..cb2a0adc092 100644 --- a/spec/requests/api/snippets_spec.rb +++ b/spec/requests/api/snippets_spec.rb @@ -199,9 +199,13 @@ describe API::Snippets do end shared_examples 'snippet creation' do + let(:snippet) { Snippet.find(json_response["id"]) } + + subject { post api("/snippets/", user), params: params } + it 'creates a new snippet' do expect do - post api("/snippets/", user), params: params + subject end.to change { PersonalSnippet.count }.by(1) expect(response).to have_gitlab_http_status(201) @@ -210,6 +214,32 @@ describe API::Snippets do expect(json_response['file_name']).to eq(params[:file_name]) expect(json_response['visibility']).to eq(params[:visibility]) end + + it 'creates repository' do + subject + + expect(snippet.repository.exists?).to be_truthy + end + + it 'commit the files to the repository' do + subject + + blob = snippet.repository.blob_at('master', params[:file_name]) + + expect(blob.data).to eq params[:content] + end + + context 'when feature flag :version_snippets is disabled' do + it 'does not create snippet repository' do + stub_feature_flags(version_snippets: false) + + expect do + subject + end.to change { PersonalSnippet.count }.by(1) + + expect(snippet.repository_exists?).to be_falsey + end + end end context 'with restricted visibility settings' do diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index e503f1a4231..449c4c07b08 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -798,6 +798,11 @@ describe 'project routing' do end it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/settings/repository", "/gitlab/gitlabhq/-/settings/repository" + + # TODO: remove this test as part of https://gitlab.com/gitlab-org/gitlab/issues/207079 (12.9) + it 'to ci_cd#create_deploy_token' do + expect(post('gitlab/gitlabhq/-/settings/repository/deploy_token/create')).to route_to('projects/settings/ci_cd#create_deploy_token', namespace_id: 'gitlab', project_id: 'gitlabhq') + end end describe Projects::TemplatesController, 'routing' do diff --git a/spec/services/groups/deploy_tokens/create_service_spec.rb b/spec/services/groups/deploy_tokens/create_service_spec.rb new file mode 100644 index 00000000000..20c609bc828 --- /dev/null +++ b/spec/services/groups/deploy_tokens/create_service_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Groups::DeployTokens::CreateService do + it_behaves_like 'a deploy token creation service' do + let(:entity) { create(:group) } + let(:deploy_token_class) { GroupDeployToken } + end +end diff --git a/spec/services/projects/deploy_tokens/create_service_spec.rb b/spec/services/projects/deploy_tokens/create_service_spec.rb new file mode 100644 index 00000000000..5c3ada8af4e --- /dev/null +++ b/spec/services/projects/deploy_tokens/create_service_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::DeployTokens::CreateService do + it_behaves_like 'a deploy token creation service' do + let(:entity) { create(:project) } + let(:deploy_token_class) { ProjectDeployToken } + end +end diff --git a/spec/services/snippets/create_service_spec.rb b/spec/services/snippets/create_service_spec.rb index 37b203c2341..5c853f8b7d7 100644 --- a/spec/services/snippets/create_service_spec.rb +++ b/spec/services/snippets/create_service_spec.rb @@ -143,37 +143,102 @@ describe Snippets::CreateService do end end - shared_examples 'creates repository' do - it do + shared_examples 'creates repository and files' do + it 'creates repository' do subject - expect(snippet.repository_exists?).to be_truthy + expect(snippet.repository.exists?).to be_truthy + end + + it 'commit the files to the repository' do + subject + + blob = snippet.repository.blob_at('master', base_opts[:file_name]) + + expect(blob.data).to eq base_opts[:content] + end + + context 'when repository creation action fails' do + before do + allow_next_instance_of(Snippet) do |instance| + allow(instance).to receive(:create_repository).and_return(nil) + end + end + + it 'does not create the snippet' do + expect { subject }.not_to change { Snippet.count } + end + + it 'returns the error' do + expect(snippet.errors.full_messages).to include('Repository could not be created') + end + end + + context 'when the commit action fails' do + before do + allow_next_instance_of(SnippetRepository) do |instance| + allow(instance).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError.new('foobar')) + end + end + + it 'does not create the snippet' do + expect { subject }.not_to change { Snippet.count } + end + + it 'does not create the repository' do + expect(snippet.repository_exists?).to be_falsey + end + + it 'destroys the existing repository' do + expect(Repositories::DestroyService).to receive(:new).and_call_original + + subject + end + + it 'returns the error' do + response = subject + + expect(response).to be_error + expect(response.payload[:snippet].errors.full_messages).to eq ['foobar'] + end end context 'when snippet creation fails' do let(:extra_opts) { { content: nil } } it 'does not create repository' do - subject + expect do + subject + end.not_to change(Snippet, :count) expect(snippet.repository_exists?).to be_falsey end end context 'when feature flag :version_snippets is disabled' do - it 'does not create snippet repository' do + before do stub_feature_flags(version_snippets: false) + end + it 'does not create snippet repository' do expect do subject end.to change(Snippet, :count).by(1) expect(snippet.repository_exists?).to be_falsey end + + it 'does not try to commit files' do + expect_next_instance_of(described_class) do |instance| + expect(instance).not_to receive(:create_commit) + end + + subject + end end end - context 'when Project Snippet' do + context 'when ProjectSnippet' do let_it_be(:project) { create(:project) } before do @@ -185,7 +250,7 @@ describe Snippets::CreateService do it_behaves_like 'spam check is performed' it_behaves_like 'snippet create data is tracked' it_behaves_like 'an error service response when save fails' - it_behaves_like 'creates repository' + it_behaves_like 'creates repository and files' end context 'when PersonalSnippet' do @@ -196,7 +261,7 @@ describe Snippets::CreateService do it_behaves_like 'spam check is performed' it_behaves_like 'snippet create data is tracked' it_behaves_like 'an error service response when save fails' - it_behaves_like 'creates repository' + it_behaves_like 'creates repository and files' end end end diff --git a/spec/services/deploy_tokens/create_service_spec.rb b/spec/support/services/deploy_token_shared_examples.rb index fbb66fe4cb7..b49f4743f7d 100644 --- a/spec/services/deploy_tokens/create_service_spec.rb +++ b/spec/support/services/deploy_token_shared_examples.rb @@ -1,14 +1,11 @@ # frozen_string_literal: true -require 'spec_helper' - -describe DeployTokens::CreateService do - let(:project) { create(:project) } +RSpec.shared_examples 'a deploy token creation service' do let(:user) { create(:user) } let(:deploy_token_params) { attributes_for(:deploy_token) } describe '#execute' do - subject { described_class.new(project, user, deploy_token_params).execute } + subject { described_class.new(entity, user, deploy_token_params).execute } context 'when the deploy token is valid' do it 'creates a new DeployToken' do @@ -16,7 +13,7 @@ describe DeployTokens::CreateService do end it 'creates a new ProjectDeployToken' do - expect { subject }.to change { ProjectDeployToken.count }.by(1) + expect { subject }.to change { deploy_token_class.count }.by(1) end it 'returns a DeployToken' do @@ -56,7 +53,7 @@ describe DeployTokens::CreateService do end it 'does not create a new ProjectDeployToken' do - expect { subject }.not_to change { ProjectDeployToken.count } + expect { subject }.not_to change { deploy_token_class.count } end end end diff --git a/spec/support/shared_examples/controllers/deploy_token_shared_examples.rb b/spec/support/shared_examples/controllers/deploy_token_shared_examples.rb new file mode 100644 index 00000000000..791eb0b68e0 --- /dev/null +++ b/spec/support/shared_examples/controllers/deploy_token_shared_examples.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'a created deploy token' do + let(:deploy_token_params) do + { + name: 'deployer_token', + expires_at: 1.month.from_now.to_date.to_s, + username: 'deployer', + read_repository: '1', + deploy_token_type: deploy_token_type + } + end + + subject(:create_deploy_token) { post :create_deploy_token, params: create_entity_params.merge({ deploy_token: deploy_token_params }) } + + it 'creates deploy token' do + expect { create_deploy_token }.to change { DeployToken.active.count }.by(1) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:show) + end +end diff --git a/spec/support/shared_examples/features/deploy_token_shared_examples.rb b/spec/support/shared_examples/features/deploy_token_shared_examples.rb new file mode 100644 index 00000000000..f358615ee9e --- /dev/null +++ b/spec/support/shared_examples/features/deploy_token_shared_examples.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'a deploy token in ci/cd settings' do + it 'view deploy tokens' do + within('.deploy-tokens') do + expect(page).to have_content(deploy_token.name) + expect(page).to have_content('read_repository') + expect(page).to have_content('read_registry') + end + end + + it 'add a new deploy token' do + fill_in 'deploy_token_name', with: 'new_deploy_key' + fill_in 'deploy_token_expires_at', with: (Date.today + 1.month).to_s + fill_in 'deploy_token_username', with: 'deployer' + check 'deploy_token_read_repository' + check 'deploy_token_read_registry' + click_button 'Create deploy token' + + expect(page).to have_content("Your new #{entity_type} deploy token has been created") + + within('.created-deploy-token-container') do + expect(page).to have_selector("input[name='deploy-token-user'][value='deployer']") + expect(page).to have_selector("input[name='deploy-token'][readonly='readonly']") + end + end +end diff --git a/spec/views/import/gitlab_projects/new.html.haml_spec.rb b/spec/views/import/gitlab_projects/new.html.haml_spec.rb index 953fcc6dc51..17636c99cbc 100644 --- a/spec/views/import/gitlab_projects/new.html.haml_spec.rb +++ b/spec/views/import/gitlab_projects/new.html.haml_spec.rb @@ -5,17 +5,18 @@ require 'spec_helper' describe 'import/gitlab_projects/new.html.haml' do include Devise::Test::ControllerHelpers - let(:user) { build_stubbed(:user, namespace: build_stubbed(:namespace)) } + let(:namespace) { build_stubbed(:namespace) } + let(:user) { build_stubbed(:user, namespace: namespace) } before do allow(view).to receive(:current_user).and_return(user) end context 'when the user has no other namespaces' do - it 'shows a namespace_id hidden field tag' do + it 'adds a namespace_id hidden field tag with the namespace id as value' do render - expect(rendered).to have_css('input[name="namespace_id"]', count: 1, visible: false) + expect(rendered).to have_css("input[name='namespace_id'][value='#{namespace.id}']", count: 1, visible: false) end end |