From 7f5f940041c8d1ee074be96cc64bdccdd95649b1 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 13 Nov 2020 19:27:03 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-5-stable-ee --- CHANGELOG.md | 10 ++ GITALY_SERVER_VERSION | 2 +- .../sidebar/labels_select_vue/constants.js | 2 - .../dropdown_contents_labels_view.vue | 173 ++++++++++----------- .../sidebar/labels_select_vue/label_item.vue | 106 +++++++------ .../labels_select_vue/labels_select_root.vue | 2 +- app/assets/stylesheets/framework/dropdowns.scss | 24 ++- app/controllers/admin/groups_controller.rb | 1 + .../hashed_storage/migrate_repository_service.rb | 28 +++- .../hashed_storage/rollback_repository_service.rb | 28 +++- ..._framework_enum_to_database_framework_record.rb | 2 - ...104124300_ensure_namespace_settings_creation.rb | 40 +++++ db/schema_migrations/20201104124300 | 1 + doc/user/admin_area/merge_requests_approvals.md | 42 ++--- locale/gitlab.pot | 9 -- spec/controllers/admin/groups_controller_spec.rb | 14 ++ .../dropdown_contents_labels_view_spec.js | 75 ++++++--- .../sidebar/labels_select_vue/label_item_spec.js | 98 ++++-------- .../ensure_namespace_settings_creation_spec.rb | 44 ++++++ ...ework_enum_to_database_framework_record_spec.rb | 42 ++--- .../migrate_repository_service_spec.rb | 36 +++++ .../rollback_repository_service_spec.rb | 36 +++++ 22 files changed, 493 insertions(+), 322 deletions(-) create mode 100644 db/post_migrate/20201104124300_ensure_namespace_settings_creation.rb create mode 100644 db/schema_migrations/20201104124300 create mode 100644 spec/migrations/ensure_namespace_settings_creation_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 807db37bd47..4b6c4582b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 13.5.4 (2020-11-13) + +### Fixed (4 changes) + +- Fix Vue Labels Select dropdown keyboard scroll. !43874 +- Hashed Storage: make migration and rollback resilient to exceptions. !46178 +- Fix compliance framework database migration on CE instances. !46761 +- Resolve problem when namespace_settings were not created for groups created via admin panel. !46875 + + ## 13.5.3 (2020-11-03) ### Fixed (3 changes) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index cd2c0e088c7..3330217f208 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -13.5.3 \ No newline at end of file +13.5.4 \ No newline at end of file diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js index 746e38e98e8..00c54313292 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js @@ -3,5 +3,3 @@ export const DropdownVariant = { Standalone: 'standalone', Embedded: 'embedded', }; - -export const LIST_BUFFER_SIZE = 5; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue index c8dee81d746..353dee862d0 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue @@ -1,23 +1,25 @@ diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue index 002e741ab96..e431fd000a6 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue @@ -1,11 +1,8 @@ - - diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue index c651013c5f5..2f71907f772 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue @@ -266,7 +266,7 @@ export default { diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index e8d37fcf40b..ca20b18f851 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -1016,6 +1016,23 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { } } + li { + &:hover, + &.is-focused { + .label-item { + @include dropdown-item-hover; + + text-decoration: none; + } + } + } + + .labels-select-dropdown-button { + .gl-button-text { + width: 100%; + } + } + .labels-select-dropdown-contents { min-height: $dropdown-min-height; max-height: 330px; @@ -1049,13 +1066,6 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { .label-item { padding: 8px 20px; - - &:hover, - &.is-focused { - @include dropdown-item-hover; - - text-decoration: none; - } } .color-input-container { diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 032e449f995..0a1c85eef3f 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -41,6 +41,7 @@ class Admin::GroupsController < Admin::ApplicationController if @group.save @group.add_owner(current_user) + @group.create_namespace_settings redirect_to [:admin, @group], notice: _('Group %{group_name} was successfully created.') % { group_name: @group.name } else render "new" diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb index fd62ac37d27..adc7e38e4d5 100644 --- a/app/services/projects/hashed_storage/migrate_repository_service.rb +++ b/app/services/projects/hashed_storage/migrate_repository_service.rb @@ -21,14 +21,32 @@ module Projects project.storage_version = nil end - project.repository_read_only = false - project.save!(validate: false) - - if result && block_given? - yield + project.transaction do + project.save!(validate: false) + project.set_repository_writable! end result + rescue Gitlab::Git::CommandError => e + logger.error("Repository #{project.full_path} failed to upgrade (PROJECT_ID=#{project.id}). Git operation failed: #{e.inspect}") + + rollback_migration! + + false + rescue OpenSSL::Cipher::CipherError => e + logger.error("Repository #{project.full_path} failed to upgrade (PROJECT_ID=#{project.id}). There is a problem with encrypted attributes: #{e.inspect}") + + rollback_migration! + + false + end + + private + + def rollback_migration! + rollback_folder_move + project.storage_version = nil + project.set_repository_writable! end end end diff --git a/app/services/projects/hashed_storage/rollback_repository_service.rb b/app/services/projects/hashed_storage/rollback_repository_service.rb index d6646e3765e..6ab49630603 100644 --- a/app/services/projects/hashed_storage/rollback_repository_service.rb +++ b/app/services/projects/hashed_storage/rollback_repository_service.rb @@ -21,14 +21,32 @@ module Projects project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository] end - project.repository_read_only = false - project.save!(validate: false) - - if result && block_given? - yield + project.transaction do + project.save!(validate: false) + project.set_repository_writable! end result + rescue Gitlab::Git::CommandError => e + logger.error("Repository #{project.full_path} failed to rollback (PROJECT_ID=#{project.id}). Git operation failed: #{e.inspect}") + + rollback_migration! + + false + rescue OpenSSL::Cipher::CipherError => e + logger.error("Repository #{project.full_path} failed to rollback (PROJECT_ID=#{project.id}). There is a problem with encrypted attributes: #{e.inspect}") + + rollback_migration! + + false + end + + private + + def rollback_migration! + rollback_folder_move + project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository] + project.set_repository_writable! end end end diff --git a/db/post_migrate/20201005094331_migrate_compliance_framework_enum_to_database_framework_record.rb b/db/post_migrate/20201005094331_migrate_compliance_framework_enum_to_database_framework_record.rb index 5e261637d46..a5fab8576e4 100644 --- a/db/post_migrate/20201005094331_migrate_compliance_framework_enum_to_database_framework_record.rb +++ b/db/post_migrate/20201005094331_migrate_compliance_framework_enum_to_database_framework_record.rb @@ -52,8 +52,6 @@ class MigrateComplianceFrameworkEnumToDatabaseFrameworkRecord < ActiveRecord::Mi end def up - return unless Gitlab.ee? - TmpComplianceFramework.reset_column_information TmpProjectSettings.reset_column_information diff --git a/db/post_migrate/20201104124300_ensure_namespace_settings_creation.rb b/db/post_migrate/20201104124300_ensure_namespace_settings_creation.rb new file mode 100644 index 00000000000..08f92d21f44 --- /dev/null +++ b/db/post_migrate/20201104124300_ensure_namespace_settings_creation.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class EnsureNamespaceSettingsCreation < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + BATCH_SIZE = 10000 + MIGRATION = 'BackfillNamespaceSettings' + DELAY_INTERVAL = 2.minutes.to_i + + disable_ddl_transaction! + + class Namespace < ActiveRecord::Base + include EachBatch + + self.table_name = 'namespaces' + end + + def up + ensure_data_migration + end + + def down + # no-op + end + + private + + def ensure_data_migration + Namespace.each_batch(of: BATCH_SIZE) do |query, index| + missing_count = query.where("NOT EXISTS (SELECT 1 FROM namespace_settings WHERE namespace_settings.namespace_id=namespaces.id)").limit(1).size + + if missing_count > 0 + ids_range = query.pluck("MIN(id), MAX(id)").flatten + + migrate_in(index * DELAY_INTERVAL, MIGRATION, ids_range) + end + end + end +end diff --git a/db/schema_migrations/20201104124300 b/db/schema_migrations/20201104124300 new file mode 100644 index 00000000000..6c54da0da54 --- /dev/null +++ b/db/schema_migrations/20201104124300 @@ -0,0 +1 @@ +e17da7eebb6d054a711368369d2b4fa684e96344f845bb7c6b3c89a9b4c4e067 \ No newline at end of file diff --git a/doc/user/admin_area/merge_requests_approvals.md b/doc/user/admin_area/merge_requests_approvals.md index 8f51c03e105..fb9ca21a214 100644 --- a/doc/user/admin_area/merge_requests_approvals.md +++ b/doc/user/admin_area/merge_requests_approvals.md @@ -5,51 +5,29 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference, concepts --- -# Instance-level merge request approval rules **(PREMIUM ONLY)** +# Merge request approval rules **(PREMIUM ONLY)** > Introduced in [GitLab Premium](https://gitlab.com/gitlab-org/gitlab/-/issues/39060) 12.8. -Merge request approvals rules prevent users overriding certain settings on a project -level. When configured, only administrators can change these settings on a project level -if they are enabled at an instance level. +Merge request approval rules prevent users from overriding certain settings on the project +level. When enabled at the instance level, these settings are no longer editable on the +project level. To enable merge request approval rules for an instance: 1. Navigate to **Admin Area >** **{push-rules}** **Push Rules** and expand **Merge - requests approvals**. +requests approvals**. 1. Set the required rule. 1. Click **Save changes**. -GitLab administrators can later override these settings in a project’s settings. - ## Available rules Merge request approval rules that can be set at an instance level are: - **Prevent approval of merge requests by merge request author**. Prevents project - maintainers from allowing request authors to merge their own merge requests. +maintainers from allowing request authors to merge their own merge requests. - **Prevent approval of merge requests by merge request committers**. Prevents project - maintainers from allowing users to approve merge requests if they have submitted - any commits to the source branch. -- **Prevent users from modifying merge request approvers list**. Prevents project - maintainers from allowing users to modify the approvers list in project settings - or in individual merge requests. - -## Scope rules to compliance-labeled projects - -> Introduced in [GitLab Premium](https://gitlab.com/groups/gitlab-org/-/epics/3432) 13.2. - -Merge request approval rules can be further scoped to specific compliance frameworks. - -When the compliance framework label is selected and the project is assigned the compliance -label, the instance-level MR approval settings will take effect and the -[project-level settings](../project/merge_requests/merge_request_approvals.md#adding--editing-a-default-approval-rule) -is locked for modification. - -When the compliance framework label is not selected or the project is not assigned the -compliance label, the project-level MR approval settings will take effect and the users with -Maintainer role and above can modify these. - -| Instance-level | Project-level | -| -------------- | ------------- | -| ![Scope MR approval settings to compliance frameworks](img/scope_mr_approval_settings_v13_1.png) | ![MR approval settings on compliance projects](img/mr_approval_settings_compliance_project_v13_1.png) | +maintainers from allowing users to approve merge requests if they have submitted +any commits to the source branch. +- **Prevent users from modifying merge request approvers list**. Prevents users from +modifying the approvers list in project settings or in individual merge requests. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2eff8d1365f..624e0aa9d57 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6817,9 +6817,6 @@ msgstr "" msgid "Compliance framework (optional)" msgstr "" -msgid "Compliance frameworks" -msgstr "" - msgid "ComplianceDashboard|created by:" msgstr "" @@ -21738,9 +21735,6 @@ msgstr "" msgid "Registry setup" msgstr "" -msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level." -msgstr "" - msgid "Reindexing status" msgstr "" @@ -26064,9 +26058,6 @@ msgstr "" msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS." msgstr "" -msgid "The above settings apply to all projects with the selected compliance framework(s)." -msgstr "" - msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential." msgstr "" diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 3f32209543f..38f4ce54e5c 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -25,6 +25,20 @@ RSpec.describe Admin::GroupsController do end end + describe 'POST #create' do + it 'creates group' do + expect do + post :create, params: { group: { path: 'test', name: 'test' } } + end.to change { Group.count }.by(1) + end + + it 'creates namespace_settings for group' do + expect do + post :create, params: { group: { path: 'test', name: 'test' } } + end.to change { NamespaceSetting.count }.by(1) + end + end + describe 'PUT #members_update' do let(:group_user) { create(:user) } diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js index a9350bc059d..e8a126d8774 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js @@ -1,9 +1,14 @@ import Vuex from 'vuex'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlButton, GlLoadingIcon, GlSearchBoxByType, GlLink } from '@gitlab/ui'; +import { + GlIntersectionObserver, + GlButton, + GlLoadingIcon, + GlSearchBoxByType, + GlLink, +} from '@gitlab/ui'; import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes'; -import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue'; import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue'; import LabelItem from '~/vue_shared/components/sidebar/labels_select_vue/label_item.vue'; @@ -88,20 +93,25 @@ describe('DropdownContentsLabelsView', () => { }); }); - describe('showListContainer', () => { + describe('showNoMatchingResultsMessage', () => { it.each` - variant | loading | showList - ${'sidebar'} | ${false} | ${true} - ${'sidebar'} | ${true} | ${false} - ${'not-sidebar'} | ${true} | ${true} - ${'not-sidebar'} | ${false} | ${true} + searchKey | labels | labelsDescription | returnValue + ${''} | ${[]} | ${'empty'} | ${false} + ${'bug'} | ${[]} | ${'empty'} | ${true} + ${''} | ${mockLabels} | ${'not empty'} | ${false} + ${'bug'} | ${mockLabels} | ${'not empty'} | ${false} `( - 'returns $showList if `state.variant` is "$variant" and `labelsFetchInProgress` is $loading', - ({ variant, loading, showList }) => { - createComponent({ ...mockConfig, variant }); - wrapper.vm.$store.state.labelsFetchInProgress = loading; + 'returns $returnValue when searchKey is "$searchKey" and visibleLabels is $labelsDescription', + async ({ searchKey, labels, returnValue }) => { + wrapper.setData({ + searchKey, + }); - expect(wrapper.vm.showListContainer).toBe(showList); + wrapper.vm.$store.dispatch('receiveLabelsSuccess', labels); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.showNoMatchingResultsMessage).toBe(returnValue); }, ); }); @@ -118,6 +128,28 @@ describe('DropdownContentsLabelsView', () => { }); }); + describe('handleComponentDisappear', () => { + it('calls action `receiveLabelsSuccess` with empty array', () => { + jest.spyOn(wrapper.vm, 'receiveLabelsSuccess'); + + wrapper.vm.handleComponentDisappear(); + + expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]); + }); + }); + + describe('handleCreateLabelClick', () => { + it('calls actions `receiveLabelsSuccess` with empty array and `toggleDropdownContentsCreateView`', () => { + jest.spyOn(wrapper.vm, 'receiveLabelsSuccess'); + jest.spyOn(wrapper.vm, 'toggleDropdownContentsCreateView'); + + wrapper.vm.handleCreateLabelClick(); + + expect(wrapper.vm.receiveLabelsSuccess).toHaveBeenCalledWith([]); + expect(wrapper.vm.toggleDropdownContentsCreateView).toHaveBeenCalled(); + }); + }); + describe('handleKeyDown', () => { it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', () => { wrapper.setData({ @@ -226,8 +258,8 @@ describe('DropdownContentsLabelsView', () => { }); describe('template', () => { - it('renders component container element with class `labels-select-contents-list`', () => { - expect(wrapper.attributes('class')).toContain('labels-select-contents-list'); + it('renders gl-intersection-observer as component root', () => { + expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true); }); it('renders gl-loading-icon component when `labelsFetchInProgress` prop is true', () => { @@ -272,15 +304,11 @@ describe('DropdownContentsLabelsView', () => { expect(searchInputEl.attributes('autofocus')).toBe('true'); }); - it('renders smart-virtual-list element', () => { - expect(wrapper.find(SmartVirtualList).exists()).toBe(true); - }); - it('renders label elements for all labels', () => { expect(wrapper.findAll(LabelItem)).toHaveLength(mockLabels.length); }); - it('renders label element with "is-focused" when value of `currentHighlightItem` is more than -1', () => { + it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', () => { wrapper.setData({ currentHighlightItem: 0, }); @@ -288,7 +316,7 @@ describe('DropdownContentsLabelsView', () => { return wrapper.vm.$nextTick(() => { const labelItemEl = findDropdownContent().find(LabelItem); - expect(labelItemEl.props('highlight')).toBe(true); + expect(labelItemEl.attributes('highlight')).toBe('true'); }); }); @@ -310,9 +338,12 @@ describe('DropdownContentsLabelsView', () => { return wrapper.vm.$nextTick(() => { const dropdownContent = findDropdownContent(); + const loadingIcon = findLoadingIcon(); expect(dropdownContent.exists()).toBe(true); - expect(dropdownContent.isVisible()).toBe(false); + expect(dropdownContent.isVisible()).toBe(true); + expect(loadingIcon.exists()).toBe(true); + expect(loadingIcon.isVisible()).toBe(true); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js index ad3f073fdf9..a6ec01ad7e1 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js @@ -6,11 +6,15 @@ import { mockRegularLabel } from './mock_data'; const mockLabel = { ...mockRegularLabel, set: true }; -const createComponent = ({ label = mockLabel, highlight = true } = {}) => +const createComponent = ({ + label = mockLabel, + isLabelSet = mockLabel.set, + highlight = true, +} = {}) => shallowMount(LabelItem, { propsData: { label, - isLabelSet: label.set, + isLabelSet, highlight, }, }); @@ -26,94 +30,44 @@ describe('LabelItem', () => { wrapper.destroy(); }); - describe('computed', () => { - describe('labelBoxStyle', () => { - it('returns an object containing `backgroundColor` based on `label` prop', () => { - expect(wrapper.vm.labelBoxStyle).toEqual( - expect.objectContaining({ - backgroundColor: mockLabel.color, - }), - ); - }); - }); - }); - - describe('watchers', () => { - describe('isLabelSet', () => { - it('sets value of `isLabelSet` to `isSet` data prop', () => { - expect(wrapper.vm.isSet).toBe(true); - - wrapper.setProps({ - isLabelSet: false, - }); - - return wrapper.vm.$nextTick(() => { - expect(wrapper.vm.isSet).toBe(false); - }); - }); - }); - }); - - describe('methods', () => { - describe('handleClick', () => { - it('sets value of `isSet` data prop to opposite of its current value', () => { - wrapper.setData({ - isSet: true, - }); - - wrapper.vm.handleClick(); - expect(wrapper.vm.isSet).toBe(false); - wrapper.vm.handleClick(); - expect(wrapper.vm.isSet).toBe(true); - }); - - it('emits event `clickLabel` on component with `label` prop as param', () => { - wrapper.vm.handleClick(); - - expect(wrapper.emitted('clickLabel')).toBeTruthy(); - expect(wrapper.emitted('clickLabel')[0]).toEqual([mockLabel]); - }); - }); - }); - describe('template', () => { it('renders gl-link component', () => { expect(wrapper.find(GlLink).exists()).toBe(true); }); - it('renders gl-link component with class `is-focused` when `highlight` prop is true', () => { - wrapper.setProps({ + it('renders component root with class `is-focused` when `highlight` prop is true', () => { + const wrapperTemp = createComponent({ highlight: true, }); - return wrapper.vm.$nextTick(() => { - expect(wrapper.find(GlLink).classes()).toContain('is-focused'); - }); + expect(wrapperTemp.classes()).toContain('is-focused'); + + wrapperTemp.destroy(); }); - it('renders visible gl-icon component when `isSet` prop is true', () => { - wrapper.setData({ - isSet: true, + it('renders visible gl-icon component when `isLabelSet` prop is true', () => { + const wrapperTemp = createComponent({ + isLabelSet: true, }); - return wrapper.vm.$nextTick(() => { - const iconEl = wrapper.find(GlIcon); + const iconEl = wrapperTemp.find(GlIcon); - expect(iconEl.isVisible()).toBe(true); - expect(iconEl.props('name')).toBe('mobile-issue-close'); - }); + expect(iconEl.isVisible()).toBe(true); + expect(iconEl.props('name')).toBe('mobile-issue-close'); + + wrapperTemp.destroy(); }); - it('renders visible span element as placeholder instead of gl-icon when `isSet` prop is false', () => { - wrapper.setData({ - isSet: false, + it('renders visible span element as placeholder instead of gl-icon when `isLabelSet` prop is false', () => { + const wrapperTemp = createComponent({ + isLabelSet: false, }); - return wrapper.vm.$nextTick(() => { - const placeholderEl = wrapper.find('[data-testid="no-icon"]'); + const placeholderEl = wrapperTemp.find('[data-testid="no-icon"]'); - expect(placeholderEl.isVisible()).toBe(true); - }); + expect(placeholderEl.isVisible()).toBe(true); + + wrapperTemp.destroy(); }); it('renders label color element', () => { diff --git a/spec/migrations/ensure_namespace_settings_creation_spec.rb b/spec/migrations/ensure_namespace_settings_creation_spec.rb new file mode 100644 index 00000000000..8574063f7fe --- /dev/null +++ b/spec/migrations/ensure_namespace_settings_creation_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20201104124300_ensure_namespace_settings_creation.rb') + +RSpec.describe EnsureNamespaceSettingsCreation do + context 'when there are namespaces without namespace settings' do + let(:namespaces) { table(:namespaces) } + let(:namespace_settings) { table(:namespace_settings) } + let!(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') } + let!(:namespace_2) { namespaces.create!(name: 'gitlab', path: 'gitlab-org2') } + + it 'migrates namespaces without namespace_settings' do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + expect(described_class::MIGRATION) + .to be_scheduled_delayed_migration(2.minutes.to_i, namespace.id, namespace_2.id) + end + end + end + + it 'schedules migrations in batches ' do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + + namespace_3 = namespaces.create!(name: 'gitlab', path: 'gitlab-org3') + namespace_4 = namespaces.create!(name: 'gitlab', path: 'gitlab-org4') + + Sidekiq::Testing.fake! do + freeze_time do + migrate! + + expect(described_class::MIGRATION) + .to be_scheduled_delayed_migration(2.minutes.to_i, namespace.id, namespace_2.id) + expect(described_class::MIGRATION) + .to be_scheduled_delayed_migration(4.minutes.to_i, namespace_3.id, namespace_4.id) + end + end + end + end +end diff --git a/spec/migrations/migrate_compliance_framework_enum_to_database_framework_record_spec.rb b/spec/migrations/migrate_compliance_framework_enum_to_database_framework_record_spec.rb index cd2ec81abb7..5c3ca281d49 100644 --- a/spec/migrations/migrate_compliance_framework_enum_to_database_framework_record_spec.rb +++ b/spec/migrations/migrate_compliance_framework_enum_to_database_framework_record_spec.rb @@ -30,41 +30,23 @@ RSpec.describe MigrateComplianceFrameworkEnumToDatabaseFrameworkRecord, schema: subject { described_class.new.up } - context 'when Gitlab.ee? is true' do - before do - expect(Gitlab).to receive(:ee?).and_return(true) - end + it 'updates the project settings' do + subject - it 'updates the project settings' do - subject + gdpr_framework = compliance_management_frameworks.find_by(namespace_id: root_group.id, name: 'GDPR') + expect(project_on_root_level_compliance_setting.reload.framework_id).to eq(gdpr_framework.id) + expect(project_on_sub_sub_level_compliance_setting_2.reload.framework_id).to eq(gdpr_framework.id) - gdpr_framework = compliance_management_frameworks.find_by(namespace_id: root_group.id, name: 'GDPR') - expect(project_on_root_level_compliance_setting.reload.framework_id).to eq(gdpr_framework.id) - expect(project_on_sub_sub_level_compliance_setting_2.reload.framework_id).to eq(gdpr_framework.id) + sox_framework = compliance_management_frameworks.find_by(namespace_id: root_group.id, name: 'SOX') + expect(project_on_sub_sub_level_compliance_setting_1.reload.framework_id).to eq(sox_framework.id) - sox_framework = compliance_management_frameworks.find_by(namespace_id: root_group.id, name: 'SOX') - expect(project_on_sub_sub_level_compliance_setting_1.reload.framework_id).to eq(sox_framework.id) - - gdpr_framework = compliance_management_frameworks.find_by(namespace_id: namespace.id, name: 'GDPR') - expect(project_on_namespace_level_compliance_setting.reload.framework_id).to eq(gdpr_framework.id) - end - - it 'adds two framework records' do - subject - - expect(compliance_management_frameworks.count).to eq(3) - end + gdpr_framework = compliance_management_frameworks.find_by(namespace_id: namespace.id, name: 'GDPR') + expect(project_on_namespace_level_compliance_setting.reload.framework_id).to eq(gdpr_framework.id) end - context 'when Gitlab.ee? is false' do - before do - expect(Gitlab).to receive(:ee?).and_return(false) - end - - it 'does nothing' do - subject + it 'adds two framework records' do + subject - expect(compliance_management_frameworks.count).to eq(0) - end + expect(compliance_management_frameworks.count).to eq(3) end end diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb index e03e75653ff..f0fd243f0ca 100644 --- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb +++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb @@ -77,6 +77,42 @@ RSpec.describe Projects::HashedStorage::MigrateRepositoryService do end end + context 'when exception happens' do + it 'handles OpenSSL::Cipher::CipherError' do + expect(project).to receive(:ensure_runners_token).and_raise(OpenSSL::Cipher::CipherError) + + expect { service.execute }.not_to raise_exception + end + + it 'ensures rollback when OpenSSL::Cipher::CipherError' do + expect(project).to receive(:ensure_runners_token).and_raise(OpenSSL::Cipher::CipherError) + expect(service).to receive(:rollback_folder_move).and_call_original + + service.execute + project.reload + + expect(project.legacy_storage?).to be_truthy + expect(project.repository_read_only?).to be_falsey + end + + it 'handles Gitlab::Git::CommandError' do + expect(project).to receive(:write_repository_config).and_raise(Gitlab::Git::CommandError) + + expect { service.execute }.not_to raise_exception + end + + it 'ensures rollback when Gitlab::Git::CommandError' do + expect(project).to receive(:write_repository_config).and_raise(Gitlab::Git::CommandError) + expect(service).to receive(:rollback_folder_move).and_call_original + + service.execute + project.reload + + expect(project.legacy_storage?).to be_truthy + expect(project.repository_read_only?).to be_falsey + end + end + context 'when one move fails' do it 'rollsback repositories to original name' do allow(service).to receive(:move_repository).and_call_original diff --git a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb index f2b1ce30a54..492eb0956aa 100644 --- a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb +++ b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb @@ -77,6 +77,42 @@ RSpec.describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab end end + context 'when exception happens' do + it 'handles OpenSSL::Cipher::CipherError' do + expect(project).to receive(:ensure_runners_token).and_raise(OpenSSL::Cipher::CipherError) + + expect { service.execute }.not_to raise_exception + end + + it 'ensures rollback when OpenSSL::Cipher::CipherError' do + expect(project).to receive(:ensure_runners_token).and_raise(OpenSSL::Cipher::CipherError) + expect(service).to receive(:rollback_folder_move).and_call_original + + service.execute + project.reload + + expect(project.hashed_storage?(:repository)).to be_truthy + expect(project.repository_read_only?).to be_falsey + end + + it 'handles Gitlab::Git::CommandError' do + expect(project).to receive(:write_repository_config).and_raise(Gitlab::Git::CommandError) + + expect { service.execute }.not_to raise_exception + end + + it 'ensures rollback when Gitlab::Git::CommandError' do + expect(project).to receive(:write_repository_config).and_raise(Gitlab::Git::CommandError) + expect(service).to receive(:rollback_folder_move).and_call_original + + service.execute + project.reload + + expect(project.hashed_storage?(:repository)).to be_truthy + expect(project.repository_read_only?).to be_falsey + end + end + context 'when one move fails' do it 'rolls repositories back to original name' do allow(service).to receive(:move_repository).and_call_original -- cgit v1.2.1