Commit fa29f13c authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents ad1824fc 2b719bdf
...@@ -41,6 +41,11 @@ export default { ...@@ -41,6 +41,11 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
hideLineNumbers: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
viewer() { viewer() {
...@@ -80,6 +85,7 @@ export default { ...@@ -80,6 +85,7 @@ export default {
:is-raw-content="isRawContent" :is-raw-content="isRawContent"
:file-name="blob.name" :file-name="blob.name"
:type="activeViewer.fileType" :type="activeViewer.fileType"
:hide-line-numbers="hideLineNumbers"
data-qa-selector="file_content" data-qa-selector="file_content"
/> />
</template> </template>
......
...@@ -42,9 +42,6 @@ export default { ...@@ -42,9 +42,6 @@ export default {
this.switchViewer( this.switchViewer(
this.hasRichViewer && !window.location.hash ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER, this.hasRichViewer && !window.location.hash ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER,
); );
if (this.hasRichViewer && !this.blobViewer) {
this.loadLegacyViewer();
}
}, },
error() { error() {
this.displayError(); this.displayError();
...@@ -69,6 +66,7 @@ export default { ...@@ -69,6 +66,7 @@ export default {
data() { data() {
return { return {
legacyRichViewer: null, legacyRichViewer: null,
legacySimpleViewer: null,
isBinary: false, isBinary: false,
isLoadingLegacyViewer: false, isLoadingLegacyViewer: false,
activeViewerType: SIMPLE_BLOB_VIEWER, activeViewerType: SIMPLE_BLOB_VIEWER,
...@@ -115,7 +113,7 @@ export default { ...@@ -115,7 +113,7 @@ export default {
return isLoggedIn(); return isLoggedIn();
}, },
isLoading() { isLoading() {
return this.$apollo.queries.project.loading || this.isLoadingLegacyViewer; return this.$apollo.queries.project.loading;
}, },
isBinaryFileType() { isBinaryFileType() {
return this.isBinary || this.blobInfo.simpleViewer?.fileType !== 'text'; return this.isBinary || this.blobInfo.simpleViewer?.fileType !== 'text';
...@@ -153,22 +151,41 @@ export default { ...@@ -153,22 +151,41 @@ export default {
}, },
}, },
methods: { methods: {
loadLegacyViewer() { loadLegacyViewer(type) {
if (this.legacyViewerLoaded(type)) {
return;
}
this.isLoadingLegacyViewer = true; this.isLoadingLegacyViewer = true;
axios axios
.get(`${this.blobInfo.webPath}?format=json&viewer=rich`) .get(`${this.blobInfo.webPath}?format=json&viewer=${type}`)
.then(({ data: { html, binary } }) => { .then(({ data: { html, binary } }) => {
this.legacyRichViewer = html; if (type === 'simple') {
this.legacySimpleViewer = html;
} else {
this.legacyRichViewer = html;
}
this.isBinary = binary; this.isBinary = binary;
this.isLoadingLegacyViewer = false; this.isLoadingLegacyViewer = false;
}) })
.catch(() => this.displayError()); .catch(() => this.displayError());
}, },
legacyViewerLoaded(type) {
return (
(type === SIMPLE_BLOB_VIEWER && this.legacySimpleViewer) ||
(type === RICH_BLOB_VIEWER && this.legacyRichViewer)
);
},
displayError() { displayError() {
createFlash({ message: __('An error occurred while loading the file. Please try again.') }); createFlash({ message: __('An error occurred while loading the file. Please try again.') });
}, },
switchViewer(newViewer) { switchViewer(newViewer) {
this.activeViewerType = newViewer || SIMPLE_BLOB_VIEWER; this.activeViewerType = newViewer || SIMPLE_BLOB_VIEWER;
if (!this.blobViewer) {
this.loadLegacyViewer(this.activeViewerType);
}
}, },
}, },
}; };
...@@ -210,10 +227,11 @@ export default { ...@@ -210,10 +227,11 @@ export default {
v-if="!blobViewer" v-if="!blobViewer"
:rich-viewer="legacyRichViewer" :rich-viewer="legacyRichViewer"
:blob="blobInfo" :blob="blobInfo"
:content="blobInfo.rawTextBlob" :content="legacySimpleViewer"
:is-raw-content="true" :is-raw-content="true"
:active-viewer="viewer" :active-viewer="viewer"
:loading="false" :hide-line-numbers="true"
:loading="isLoadingLegacyViewer"
/> />
<component :is="blobViewer" v-else v-bind="viewerProps" class="blob-viewer" /> <component :is="blobViewer" v-else v-bind="viewerProps" class="blob-viewer" />
</div> </div>
......
...@@ -3,7 +3,9 @@ export const loadViewer = (type) => { ...@@ -3,7 +3,9 @@ export const loadViewer = (type) => {
case 'empty': case 'empty':
return () => import(/* webpackChunkName: 'blob_empty_viewer' */ './empty_viewer.vue'); return () => import(/* webpackChunkName: 'blob_empty_viewer' */ './empty_viewer.vue');
case 'text': case 'text':
return () => import(/* webpackChunkName: 'blob_text_viewer' */ './text_viewer.vue'); return gon.features.refactorTextViewer
? () => import(/* webpackChunkName: 'blob_text_viewer' */ './text_viewer.vue')
: null;
case 'download': case 'download':
return () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue'); return () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
case 'image': case 'image':
......
...@@ -27,6 +27,11 @@ export default { ...@@ -27,6 +27,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
hideLineNumbers: {
type: Boolean,
required: false,
default: false,
},
}, },
mounted() { mounted() {
eventHub.$emit(SNIPPET_MEASURE_BLOBS_CONTENT); eventHub.$emit(SNIPPET_MEASURE_BLOBS_CONTENT);
......
...@@ -8,8 +8,6 @@ export default { ...@@ -8,8 +8,6 @@ export default {
name: 'SimpleViewer', name: 'SimpleViewer',
components: { components: {
GlIcon, GlIcon,
SourceEditor: () =>
import(/* webpackChunkName: 'SourceEditor' */ '~/vue_shared/components/source_editor.vue'),
}, },
mixins: [ViewerMixin, glFeatureFlagsMixin()], mixins: [ViewerMixin, glFeatureFlagsMixin()],
inject: ['blobHash'], inject: ['blobHash'],
...@@ -22,9 +20,6 @@ export default { ...@@ -22,9 +20,6 @@ export default {
lineNumbers() { lineNumbers() {
return this.content.split('\n').length; return this.content.split('\n').length;
}, },
refactorBlobViewerEnabled() {
return this.glFeatures.refactorBlobViewer;
},
}, },
mounted() { mounted() {
const { hash } = window.location; const { hash } = window.location;
...@@ -52,14 +47,8 @@ export default { ...@@ -52,14 +47,8 @@ export default {
</script> </script>
<template> <template>
<div> <div>
<source-editor <div class="file-content code js-syntax-highlight" :class="$options.userColorScheme">
v-if="isRawContent && refactorBlobViewerEnabled" <div v-if="!hideLineNumbers" class="line-numbers">
:value="content"
:file-name="fileName"
:editor-options="{ readOnly: true }"
/>
<div v-else class="file-content code js-syntax-highlight" :class="$options.userColorScheme">
<div class="line-numbers">
<a <a
v-for="line in lineNumbers" v-for="line in lineNumbers"
:id="`L${line}`" :id="`L${line}`"
......
...@@ -43,6 +43,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -43,6 +43,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action do before_action do
push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml) push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml)
push_frontend_feature_flag(:refactor_text_viewer, @project, default_enabled: :yaml)
push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml) push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
end end
......
...@@ -34,6 +34,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -34,6 +34,7 @@ class ProjectsController < Projects::ApplicationController
before_action do before_action do
push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml) push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml)
push_frontend_feature_flag(:refactor_text_viewer, @project, default_enabled: :yaml)
push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml) push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml)
push_frontend_feature_flag(:paginated_tree_graphql_query, @project, default_enabled: :yaml) push_frontend_feature_flag(:paginated_tree_graphql_query, @project, default_enabled: :yaml)
end end
......
...@@ -178,7 +178,13 @@ class Member < ApplicationRecord ...@@ -178,7 +178,13 @@ class Member < ApplicationRecord
after_destroy :post_destroy_hook, unless: :pending?, if: :hook_prerequisites_met? after_destroy :post_destroy_hook, unless: :pending?, if: :hook_prerequisites_met?
after_save :log_invitation_token_cleanup after_save :log_invitation_token_cleanup
after_commit :refresh_member_authorized_projects, unless: :importing? after_commit on: [:create, :update], unless: :importing? do
refresh_member_authorized_projects(blocking: true)
end
after_commit on: [:destroy], unless: :importing? do
refresh_member_authorized_projects(blocking: Feature.disabled?(:member_destroy_async_auth_refresh, type: :ops))
end
default_value_for :notification_level, NotificationSetting.levels[:global] default_value_for :notification_level, NotificationSetting.levels[:global]
...@@ -395,8 +401,8 @@ class Member < ApplicationRecord ...@@ -395,8 +401,8 @@ class Member < ApplicationRecord
# transaction has been committed, resulting in the job either throwing an # transaction has been committed, resulting in the job either throwing an
# error or not doing any meaningful work. # error or not doing any meaningful work.
# rubocop: disable CodeReuse/ServiceClass # rubocop: disable CodeReuse/ServiceClass
def refresh_member_authorized_projects def refresh_member_authorized_projects(blocking:)
UserProjectAccessChangedService.new(user_id).execute UserProjectAccessChangedService.new(user_id).execute(blocking: blocking)
end end
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
......
...@@ -50,8 +50,10 @@ class GroupMember < Member ...@@ -50,8 +50,10 @@ class GroupMember < Member
{ group: group } { group: group }
end end
private
override :refresh_member_authorized_projects override :refresh_member_authorized_projects
def refresh_member_authorized_projects def refresh_member_authorized_projects(blocking:)
# Here, `destroyed_by_association` will be present if the # Here, `destroyed_by_association` will be present if the
# GroupMember is being destroyed due to the `dependent: :destroy` # GroupMember is being destroyed due to the `dependent: :destroy`
# callback on Group. In this case, there is no need to refresh the # callback on Group. In this case, there is no need to refresh the
...@@ -63,8 +65,6 @@ class GroupMember < Member ...@@ -63,8 +65,6 @@ class GroupMember < Member
super super
end end
private
def access_level_inclusion def access_level_inclusion
return if access_level.in?(Gitlab::Access.all_values) return if access_level.in?(Gitlab::Access.all_values)
......
...@@ -90,24 +90,28 @@ class ProjectMember < Member ...@@ -90,24 +90,28 @@ class ProjectMember < Member
{ project: project } { project: project }
end end
private
override :refresh_member_authorized_projects override :refresh_member_authorized_projects
def refresh_member_authorized_projects def refresh_member_authorized_projects(blocking:)
return super unless Feature.enabled?(:specialized_service_for_project_member_auth_refresh) return super unless Feature.enabled?(:specialized_service_for_project_member_auth_refresh)
return unless user return unless user
# rubocop:disable CodeReuse/ServiceClass # rubocop:disable CodeReuse/ServiceClass
AuthorizedProjectUpdate::ProjectRecalculatePerUserService.new(project, user).execute if blocking
AuthorizedProjectUpdate::ProjectRecalculatePerUserService.new(project, user).execute
else
AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker.perform_async(project.id, user.id)
end
# Until we compare the inconsistency rates of the new, specialized service and # Until we compare the inconsistency rates of the new, specialized service and
# the old approach, we still run AuthorizedProjectsWorker # the old approach, we still run AuthorizedProjectsWorker
# but with some delay and lower urgency as a safety net. # but with some delay and lower urgency as a safety net.
UserProjectAccessChangedService.new(user_id) UserProjectAccessChangedService.new(user_id)
.execute(blocking: false, priority: UserProjectAccessChangedService::LOW_PRIORITY) .execute(blocking: false, priority: UserProjectAccessChangedService::LOW_PRIORITY)
# rubocop:enable CodeReuse/ServiceClass # rubocop:enable CodeReuse/ServiceClass
end end
private
def send_invite def send_invite
run_after_commit_or_now { notification_service.invite_project_member(self, @raw_invite_token) } run_after_commit_or_now { notification_service.invite_project_member(self, @raw_invite_token) }
......
...@@ -318,6 +318,7 @@ class User < ApplicationRecord ...@@ -318,6 +318,7 @@ class User < ApplicationRecord
delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true
delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true
delegate :pronunciation, :pronunciation=, to: :user_detail, allow_nil: true delegate :pronunciation, :pronunciation=, to: :user_detail, allow_nil: true
delegate :registration_objective, :registration_objective=, to: :user_detail, allow_nil: true
accepts_nested_attributes_for :user_preference, update_only: true accepts_nested_attributes_for :user_preference, update_only: true
accepts_nested_attributes_for :user_detail, update_only: true accepts_nested_attributes_for :user_detail, update_only: true
......
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
class UserDetail < ApplicationRecord class UserDetail < ApplicationRecord
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include IgnorableColumns include IgnorableColumns
ignore_columns %i[bio_html cached_markdown_version], remove_with: '13.6', remove_after: '2021-10-22' ignore_columns %i[bio_html cached_markdown_version], remove_with: '13.6', remove_after: '2021-10-22'
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
belongs_to :user belongs_to :user
validates :pronouns, length: { maximum: 50 } validates :pronouns, length: { maximum: 50 }
...@@ -14,6 +17,8 @@ class UserDetail < ApplicationRecord ...@@ -14,6 +17,8 @@ class UserDetail < ApplicationRecord
before_save :prevent_nil_bio before_save :prevent_nil_bio
enum registration_objective: REGISTRATION_OBJECTIVE_PAIRS, _suffix: true
private private
def prevent_nil_bio def prevent_nil_bio
......
...@@ -30,6 +30,15 @@ ...@@ -30,6 +30,15 @@
:weight: 1 :weight: 1
:idempotent: true :idempotent: true
:tags: [] :tags: []
- :name: authorized_project_update:authorized_project_update_project_recalculate_per_user
:worker_name: AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker
:feature_category: :authentication_and_authorization
:has_external_dependencies:
:urgency: :high
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: authorized_project_update:authorized_project_update_user_refresh_from_replica - :name: authorized_project_update:authorized_project_update_user_refresh_from_replica
:worker_name: AuthorizedProjectUpdate::UserRefreshFromReplicaWorker :worker_name: AuthorizedProjectUpdate::UserRefreshFromReplicaWorker
:feature_category: :authentication_and_authorization :feature_category: :authentication_and_authorization
......
# frozen_string_literal: true
module AuthorizedProjectUpdate
class ProjectRecalculatePerUserWorker < ProjectRecalculateWorker
data_consistency :always
feature_category :authentication_and_authorization
urgency :high
queue_namespace :authorized_project_update
deduplicate :until_executing, including_scheduled: true
idempotent!
def perform(project_id, user_id)
project = Project.find_by_id(project_id)
user = User.find_by_id(user_id)
return unless project && user
in_lock(lock_key(project), ttl: 10.seconds) do
AuthorizedProjectUpdate::ProjectRecalculatePerUserService.new(project, user).execute
end
end
end
end
...@@ -26,7 +26,9 @@ module AuthorizedProjectUpdate ...@@ -26,7 +26,9 @@ module AuthorizedProjectUpdate
private private
def lock_key(project) def lock_key(project)
"#{self.class.name.underscore}/projects/#{project.id}" # The self.class.name.underscore value is hardcoded here as the prefix, so that the same
# lock_key for this superclass will be used by the ProjectRecalculatePerUserWorker subclass.
"authorized_project_update/project_recalculate_worker/projects/#{project.id}"
end end
end end
end end
---
name: refactor_text_viewer
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70909
rollout_issue_url:
milestone: '14.4'
type: development
group: 'group::source code'
default_enabled: false
---
name: member_destroy_async_auth_refresh
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66424
rollout_issue_url:
milestone: '14.4'
type: ops
group: group::access
default_enabled: false
# frozen_string_literal: true
class AddRegistrationObjectiveToUserDetail < Gitlab::Database::Migration[1.0]
def change
add_column :user_details, :registration_objective, :smallint
end
end
9204c844b22ad0d3a938ed908377c8baacdda038725a5cf105e4b11841c1ae21
\ No newline at end of file
...@@ -19764,6 +19764,7 @@ CREATE TABLE user_details ( ...@@ -19764,6 +19764,7 @@ CREATE TABLE user_details (
provisioned_by_group_id bigint, provisioned_by_group_id bigint,
pronouns text, pronouns text,
pronunciation text, pronunciation text,
registration_objective smallint,
CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)), CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)),
CONSTRAINT check_b132136b01 CHECK ((char_length(other_role) <= 100)), CONSTRAINT check_b132136b01 CHECK ((char_length(other_role) <= 100)),
CONSTRAINT check_eeeaf8d4f0 CHECK ((char_length(pronouns) <= 50)), CONSTRAINT check_eeeaf8d4f0 CHECK ((char_length(pronouns) <= 50)),
...@@ -4,7 +4,7 @@ import Tracking from '~/tracking'; ...@@ -4,7 +4,7 @@ import Tracking from '~/tracking';
const select = document.querySelector('.js-jobs-to-be-done-dropdown'); const select = document.querySelector('.js-jobs-to-be-done-dropdown');
if (select) { if (select) {
Tracking.enableFormTracking( Tracking.enableFormTracking(
{ fields: { allow: ['jobs_to_be_done', 'jobs_to_be_done_other'] } }, { fields: { allow: ['jobs_to_be_done_other'] } },
getExperimentContexts('jobs_to_be_done'), getExperimentContexts('jobs_to_be_done'),
); );
......
...@@ -44,7 +44,7 @@ module EE ...@@ -44,7 +44,7 @@ module EE
override :update_params override :update_params
def update_params def update_params
clean_params = super.merge(params.require(:user).permit(:email_opted_in)) clean_params = super.merge(params.require(:user).permit(:email_opted_in, :registration_objective))
return clean_params unless ::Gitlab.dev_env_or_com? return clean_params unless ::Gitlab.dev_env_or_com?
......
...@@ -10,8 +10,10 @@ module EE ...@@ -10,8 +10,10 @@ module EE
super.merge(api_path: suggestion_path) super.merge(api_path: suggestion_path)
end end
def shuffled_jobs_to_be_done_options def shuffled_registration_objective_options
jobs_to_be_done_options.shuffle.append([_('A different reason'), 'other']) options = registration_objective_options
other = options.extract!(:other).to_a.flatten
options.to_a.shuffle.append(other).map { |option| option.reverse }
end end
private private
...@@ -23,15 +25,10 @@ module EE ...@@ -23,15 +25,10 @@ module EE
end end
end end
def jobs_to_be_done_options def registration_objective_options
[ localized_jobs_to_be_done_choices.merge(
_('I want to learn the basics of Git'), joining_team: _('I’m joining my team who’s already on GitLab')
_('I want to move my repository to GitLab from somewhere else'), )
_('I want to store my code'),
_('I want to explore GitLab to see if it’s worth switching to'),
_('I want to use GitLab CI with my existing repository'),
_('I’m joining my team who’s already on GitLab')
]
end end
end end
end end
...@@ -2,10 +2,8 @@ ...@@ -2,10 +2,8 @@
- e.try do - e.try do
.row .row
.form-group.col-sm-12 .form-group.col-sm-12
= label_tag :jobs_to_be_done, _("I'm signing up for GitLab because:") = label_tag :user_registration_objective, _("I'm signing up for GitLab because:")
= select_tag :jobs_to_be_done, = f.select :registration_objective, shuffled_registration_objective_options, { include_blank: _('Please select...') }, class: 'form-control js-jobs-to-be-done-dropdown'
options_for_select(shuffled_jobs_to_be_done_options),
include_blank: _('Please select...'), class: 'form-control js-jobs-to-be-done-dropdown'
.row .row
.form-group.col-sm-12.js-jobs-to-be-done-other-group.hidden .form-group.col-sm-12.js-jobs-to-be-done-other-group.hidden
= label_tag :jobs_to_be_done_other, _('Why are you signing up? (Optional)') = label_tag :jobs_to_be_done_other, _('Why are you signing up? (Optional)')
......
...@@ -135,7 +135,8 @@ RSpec.describe Registrations::WelcomeController do ...@@ -135,7 +135,8 @@ RSpec.describe Registrations::WelcomeController do
user: { user: {
role: 'software_developer', role: 'software_developer',
setup_for_company: setup_for_company, setup_for_company: setup_for_company,
email_opted_in: email_opted_in email_opted_in: email_opted_in,
registration_objective: 'code_storage'
} }
} }
end end
...@@ -179,6 +180,14 @@ RSpec.describe Registrations::WelcomeController do ...@@ -179,6 +180,14 @@ RSpec.describe Registrations::WelcomeController do
allow(::Gitlab).to receive(:com?).and_return(true) allow(::Gitlab).to receive(:com?).and_return(true)
end end
context 'when registration_objective field is provided' do
it 'sets the registration_objective' do
subject
expect(controller.current_user.registration_objective).to eq('code_storage')
end
end
context 'when setup for company is false' do context 'when setup for company is false' do
context 'when the user opted in' do context 'when the user opted in' do
let(:email_opted_in) { '1' } let(:email_opted_in) { '1' }
......
...@@ -59,7 +59,7 @@ RSpec.describe 'Welcome screen', :js do ...@@ -59,7 +59,7 @@ RSpec.describe 'Welcome screen', :js do
it 'allows specifying other for the jobs_to_be_done experiment', :experiment do it 'allows specifying other for the jobs_to_be_done experiment', :experiment do
expect(page).not_to have_content('Why are you signing up? (Optional)') expect(page).not_to have_content('Why are you signing up? (Optional)')
select 'A different reason', from: 'jobs_to_be_done' select 'A different reason', from: 'user_registration_objective'
expect(page).to have_content('Why are you signing up? (Optional)') expect(page).to have_content('Why are you signing up? (Optional)')
......
...@@ -9,25 +9,17 @@ RSpec.describe EE::RegistrationsHelper do ...@@ -9,25 +9,17 @@ RSpec.describe EE::RegistrationsHelper do
end end
end end
describe '#shuffled_jobs_to_be_done_options' do describe '#shuffled_registration_objective_options' do
subject { helper.shuffled_jobs_to_be_done_options } subject(:shuffled_options) { helper.shuffled_registration_objective_options }
let(:array_double) { double(:array) } it 'has values that match all UserDetail registration objective keys' do
shuffled_option_values = shuffled_options.map { |item| item.last }
it 'uses shuffle' do expect(shuffled_option_values).to contain_exactly(*UserDetail.registration_objectives.keys)
allow(helper).to receive(:jobs_to_be_done_options).and_return(array_double)
expect(array_double).to receive(:shuffle).and_return([])
subject
end
it 'has a number of options' do
expect(subject.count).to eq(7)
end end
it '"other" is always the last option' do it '"other" is always the last option' do
expect(subject.last).to eq(['A different reason', 'other']) expect(shuffled_options.last).to eq(['A different reason', 'other'])
end end
end end
end end
...@@ -282,7 +282,7 @@ RSpec.describe EE::Gitlab::Auth::Ldap::Sync::Group do ...@@ -282,7 +282,7 @@ RSpec.describe EE::Gitlab::Auth::Ldap::Sync::Group do
.to eq(::Gitlab::Access::OWNER) .to eq(::Gitlab::Access::OWNER)
end end
it 'updates projects authorizations' do it 'updates projects authorizations', :sidekiq_inline do
project = create(:project, namespace: group) project = create(:project, namespace: group)
group.add_user(user, Gitlab::Access::MAINTAINER) group.add_user(user, Gitlab::Access::MAINTAINER)
......
...@@ -1336,7 +1336,7 @@ RSpec.describe ApprovalState do ...@@ -1336,7 +1336,7 @@ RSpec.describe ApprovalState do
expect(subject.can_approve?(nil)).to be_falsey expect(subject.can_approve?(nil)).to be_falsey
end end
context 'when an approver does not have access to the merge request' do context 'when an approver does not have access to the merge request', :sidekiq_inline do
before do before do
project.members.find_by(user_id: developer.id).destroy! project.members.find_by(user_id: developer.id).destroy!
end end
......
...@@ -324,7 +324,7 @@ RSpec.describe TodoService do ...@@ -324,7 +324,7 @@ RSpec.describe TodoService do
let(:project) { create(:project, :private, :repository) } let(:project) { create(:project, :private, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, author: author) } let(:merge_request) { create(:merge_request, source_project: project, author: author) }
context 'an approver has lost access to the project' do context 'an approver has lost access to the project', :sidekiq_inline do
before do before do
create(:approver, user: non_member, target: project) create(:approver, user: non_member, target: project)
project.members.find_by(user_id: non_member.id).destroy project.members.find_by(user_id: non_member.id).destroy
......
...@@ -66,7 +66,7 @@ RSpec.describe 'registrations/welcome/show' do ...@@ -66,7 +66,7 @@ RSpec.describe 'registrations/welcome/show' do
let_it_be(:stubbed_experiments) { { jobs_to_be_done: :candidate } } let_it_be(:stubbed_experiments) { { jobs_to_be_done: :candidate } }
it 'renders a select and text field for additional information' do it 'renders a select and text field for additional information' do
is_expected.to have_selector('select[name="jobs_to_be_done"]') is_expected.to have_selector('select[name="user[registration_objective]"]')
is_expected.to have_selector('input[name="jobs_to_be_done_other"]', visible: false) is_expected.to have_selector('input[name="jobs_to_be_done_other"]', visible: false)
end end
end end
......
...@@ -239,7 +239,7 @@ RSpec.describe Projects::BranchesController do ...@@ -239,7 +239,7 @@ RSpec.describe Projects::BranchesController do
end end
end end
context 'without issue feature access' do context 'without issue feature access', :sidekiq_inline do
before do before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
......
...@@ -409,7 +409,7 @@ RSpec.describe Projects::CompareController do ...@@ -409,7 +409,7 @@ RSpec.describe Projects::CompareController do
end end
end end
context 'when the user does not have access to the project' do context 'when the user does not have access to the project', :sidekiq_inline do
before do before do
project.team.truncate project.team.truncate
project.update!(visibility: 'private') project.update!(visibility: 'private')
......
...@@ -159,8 +159,13 @@ describe('Blob content viewer component', () => { ...@@ -159,8 +159,13 @@ describe('Blob content viewer component', () => {
const findBlobContent = () => wrapper.findComponent(BlobContent); const findBlobContent = () => wrapper.findComponent(BlobContent);
const findBlobButtonGroup = () => wrapper.findComponent(BlobButtonGroup); const findBlobButtonGroup = () => wrapper.findComponent(BlobButtonGroup);
beforeEach(() => {
gon.features = { refactorTextViewer: true };
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
mockAxios.reset();
}); });
it('renders a GlLoadingIcon component', () => { it('renders a GlLoadingIcon component', () => {
...@@ -183,7 +188,6 @@ describe('Blob content viewer component', () => { ...@@ -183,7 +188,6 @@ describe('Blob content viewer component', () => {
it('renders a BlobContent component', () => { it('renders a BlobContent component', () => {
expect(findBlobContent().props('loading')).toEqual(false); expect(findBlobContent().props('loading')).toEqual(false);
expect(findBlobContent().props('content')).toEqual('raw content');
expect(findBlobContent().props('isRawContent')).toBe(true); expect(findBlobContent().props('isRawContent')).toBe(true);
expect(findBlobContent().props('activeViewer')).toEqual({ expect(findBlobContent().props('activeViewer')).toEqual({
fileType: 'text', fileType: 'text',
...@@ -192,6 +196,16 @@ describe('Blob content viewer component', () => { ...@@ -192,6 +196,16 @@ describe('Blob content viewer component', () => {
renderError: null, renderError: null,
}); });
}); });
describe('legacy viewers', () => {
it('loads a legacy viewer when a viewer component is not available', async () => {
createComponentWithApollo({ blobs: { ...simpleMockData, fileType: 'unknown' } });
await waitForPromises();
expect(mockAxios.history.get).toHaveLength(1);
expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=simple');
});
});
}); });
describe('rich viewer', () => { describe('rich viewer', () => {
...@@ -210,7 +224,6 @@ describe('Blob content viewer component', () => { ...@@ -210,7 +224,6 @@ describe('Blob content viewer component', () => {
it('renders a BlobContent component', () => { it('renders a BlobContent component', () => {
expect(findBlobContent().props('loading')).toEqual(false); expect(findBlobContent().props('loading')).toEqual(false);
expect(findBlobContent().props('content')).toEqual('raw content');
expect(findBlobContent().props('isRawContent')).toBe(true); expect(findBlobContent().props('isRawContent')).toBe(true);
expect(findBlobContent().props('activeViewer')).toEqual({ expect(findBlobContent().props('activeViewer')).toEqual({
fileType: 'markup', fileType: 'markup',
...@@ -241,18 +254,12 @@ describe('Blob content viewer component', () => { ...@@ -241,18 +254,12 @@ describe('Blob content viewer component', () => {
}); });
describe('legacy viewers', () => { describe('legacy viewers', () => {
it('does not load a legacy viewer when a rich viewer is not available', async () => { it('loads a legacy viewer when a viewer component is not available', async () => {
createComponentWithApollo({ blobs: simpleMockData }); createComponentWithApollo({ blobs: { ...richMockData, fileType: 'unknown' } });
await waitForPromises();
expect(mockAxios.history.get).toHaveLength(0);
});
it('loads a legacy viewer when a rich viewer is available', async () => {
createComponentWithApollo({ blobs: richMockData });
await waitForPromises(); await waitForPromises();
expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get).toHaveLength(1);
expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=rich');
}); });
}); });
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import { HIGHLIGHT_CLASS_NAME } from '~/vue_shared/components/blob_viewers/constants'; import { HIGHLIGHT_CLASS_NAME } from '~/vue_shared/components/blob_viewers/constants';
import SimpleViewer from '~/vue_shared/components/blob_viewers/simple_viewer.vue'; import SimpleViewer from '~/vue_shared/components/blob_viewers/simple_viewer.vue';
import SourceEditor from '~/vue_shared/components/source_editor.vue';
describe('Blob Simple Viewer component', () => { describe('Blob Simple Viewer component', () => {
let wrapper; let wrapper;
const contentMock = `<span id="LC1">First</span>\n<span id="LC2">Second</span>\n<span id="LC3">Third</span>`; const contentMock = `<span id="LC1">First</span>\n<span id="LC2">Second</span>\n<span id="LC3">Third</span>`;
const blobHash = 'foo-bar'; const blobHash = 'foo-bar';
function createComponent( function createComponent(content = contentMock, isRawContent = false) {
content = contentMock,
isRawContent = false,
isRefactorFlagEnabled = false,
) {
wrapper = shallowMount(SimpleViewer, { wrapper = shallowMount(SimpleViewer, {
provide: { provide: {
blobHash, blobHash,
glFeatures: {
refactorBlobViewer: isRefactorFlagEnabled,
},
}, },
propsData: { propsData: {
content, content,
...@@ -94,32 +85,4 @@ describe('Blob Simple Viewer component', () => { ...@@ -94,32 +85,4 @@ describe('Blob Simple Viewer component', () => {
}); });
}); });
}); });
describe('Vue refactoring to use Source Editor', () => {
const findSourceEditor = () => wrapper.find(SourceEditor);
it.each`
doesRender | condition | isRawContent | isRefactorFlagEnabled
${'Does not'} | ${'rawContent is not specified'} | ${false} | ${true}
${'Does not'} | ${'feature flag is disabled is not specified'} | ${true} | ${false}
${'Does not'} | ${'both, the FF and rawContent are not specified'} | ${false} | ${false}
${'Does'} | ${'both, the FF and rawContent are specified'} | ${true} | ${true}
`(
'$doesRender render Source Editor component in readonly mode when $condition',
async ({ isRawContent, isRefactorFlagEnabled } = {}) => {
createComponent('raw content', isRawContent, isRefactorFlagEnabled);
await waitForPromises();
if (isRawContent && isRefactorFlagEnabled) {
expect(findSourceEditor().exists()).toBe(true);
expect(findSourceEditor().props('value')).toBe('raw content');
expect(findSourceEditor().props('fileName')).toBe('test.js');
expect(findSourceEditor().props('editorOptions')).toEqual({ readOnly: true });
} else {
expect(findSourceEditor().exists()).toBe(false);
}
},
);
});
}); });
...@@ -187,7 +187,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -187,7 +187,7 @@ RSpec.describe GitlabSchema.types['Project'] do
expect(analyzer['enabled']).to eq(true) expect(analyzer['enabled']).to eq(true)
end end
context "with guest user" do context 'with guest user' do
before do before do
project.add_guest(user) project.add_guest(user)
end end
...@@ -195,7 +195,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -195,7 +195,7 @@ RSpec.describe GitlabSchema.types['Project'] do
context 'when project is private' do context 'when project is private' do
let(:project) { create(:project, :private, :repository) } let(:project) { create(:project, :private, :repository) }
it "returns no configuration" do it 'returns no configuration' do
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration') secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration')
expect(secure_analyzers_prefix).to be_nil expect(secure_analyzers_prefix).to be_nil
end end
...@@ -215,7 +215,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -215,7 +215,7 @@ RSpec.describe GitlabSchema.types['Project'] do
end end
end end
context "with non-member user" do context 'with non-member user', :sidekiq_inline do
before do before do
project.team.truncate project.team.truncate
end end
...@@ -223,7 +223,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -223,7 +223,7 @@ RSpec.describe GitlabSchema.types['Project'] do
context 'when project is private' do context 'when project is private' do
let(:project) { create(:project, :private, :repository) } let(:project) { create(:project, :private, :repository) }
it "returns no configuration" do it 'returns no configuration' do
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration') secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration')
expect(secure_analyzers_prefix).to be_nil expect(secure_analyzers_prefix).to be_nil
end end
...@@ -241,7 +241,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -241,7 +241,7 @@ RSpec.describe GitlabSchema.types['Project'] do
end end
context 'when repository is accessible only by team members' do context 'when repository is accessible only by team members' do
it "returns no configuration" do it 'returns no configuration' do
project.project_feature.update!( project.project_feature.update!(
merge_requests_access_level: ProjectFeature::DISABLED, merge_requests_access_level: ProjectFeature::DISABLED,
builds_access_level: ProjectFeature::DISABLED, builds_access_level: ProjectFeature::DISABLED,
......
...@@ -98,7 +98,7 @@ RSpec.describe Gitlab::Middleware::Go do ...@@ -98,7 +98,7 @@ RSpec.describe Gitlab::Middleware::Go do
end end
end end
context 'without access to the project' do context 'without access to the project', :sidekiq_inline do
before do before do
project.team.find_member(current_user).destroy project.team.find_member(current_user).destroy
end end
......
...@@ -95,7 +95,7 @@ RSpec.describe Gitlab::SlashCommands::IssueMove, service: true do ...@@ -95,7 +95,7 @@ RSpec.describe Gitlab::SlashCommands::IssueMove, service: true do
end end
end end
context 'when the user cannot see the target project' do context 'when the user cannot see the target project', :sidekiq_inline do
it 'returns not found' do it 'returns not found' do
message = "issue move #{issue.iid} #{other_project.full_path}" message = "issue move #{issue.iid} #{other_project.full_path}"
other_project.team.truncate other_project.team.truncate
......
...@@ -7,11 +7,11 @@ RSpec.describe Member do ...@@ -7,11 +7,11 @@ RSpec.describe Member do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
describe "Associations" do describe 'Associations' do
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
end end
describe "Validation" do describe 'Validation' do
subject { described_class.new(access_level: Member::GUEST) } subject { described_class.new(access_level: Member::GUEST) }
it { is_expected.to validate_presence_of(:user) } it { is_expected.to validate_presence_of(:user) }
...@@ -28,7 +28,7 @@ RSpec.describe Member do ...@@ -28,7 +28,7 @@ RSpec.describe Member do
subject { build(:project_member) } subject { build(:project_member) }
end end
context "when an invite email is provided" do context 'when an invite email is provided' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:member) { build(:project_member, source: project, invite_email: "user@example.com", user: nil) } let(:member) { build(:project_member, source: project, invite_email: "user@example.com", user: nil) }
...@@ -37,29 +37,29 @@ RSpec.describe Member do ...@@ -37,29 +37,29 @@ RSpec.describe Member do
expect(member).to be_valid expect(member).to be_valid
end end
it "requires a valid invite email" do it 'requires a valid invite email' do
member.invite_email = "nope" member.invite_email = "nope"
expect(member).not_to be_valid expect(member).not_to be_valid
end end
it "requires a unique invite email scoped to this source" do it 'requires a unique invite email scoped to this source' do
create(:project_member, source: member.source, invite_email: member.invite_email) create(:project_member, source: member.source, invite_email: member.invite_email)
expect(member).not_to be_valid expect(member).not_to be_valid
end end
end end
context "when an invite email is not provided" do context 'when an invite email is not provided' do
let(:member) { build(:project_member) } let(:member) { build(:project_member) }
it "requires a user" do it 'requires a user' do
member.user = nil member.user = nil
expect(member).not_to be_valid expect(member).not_to be_valid
end end
it "is valid otherwise" do it 'is valid otherwise' do
expect(member).to be_valid expect(member).to be_valid
end end
end end
...@@ -107,13 +107,13 @@ RSpec.describe Member do ...@@ -107,13 +107,13 @@ RSpec.describe Member do
end end
end end
context "when a child member inherits its access level" do context 'when a child member inherits its access level' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:member) { create(:group_member, :developer, user: user) } let(:member) { create(:group_member, :developer, user: user) }
let(:child_group) { create(:group, parent: member.group) } let(:child_group) { create(:group, parent: member.group) }
let(:child_member) { build(:group_member, group: child_group, user: user) } let(:child_member) { build(:group_member, group: child_group, user: user) }
it "requires a higher level" do it 'requires a higher level' do
child_member.access_level = GroupMember::REPORTER child_member.access_level = GroupMember::REPORTER
child_member.validate child_member.validate
...@@ -123,7 +123,7 @@ RSpec.describe Member do ...@@ -123,7 +123,7 @@ RSpec.describe Member do
# Membership in a subgroup confers certain access rights, such as being # Membership in a subgroup confers certain access rights, such as being
# able to merge or push code to protected branches. # able to merge or push code to protected branches.
it "is valid with an equal level" do it 'is valid with an equal level' do
child_member.access_level = GroupMember::DEVELOPER child_member.access_level = GroupMember::DEVELOPER
child_member.validate child_member.validate
...@@ -131,7 +131,7 @@ RSpec.describe Member do ...@@ -131,7 +131,7 @@ RSpec.describe Member do
expect(child_member).to be_valid expect(child_member).to be_valid
end end
it "is valid with a higher level" do it 'is valid with a higher level' do
child_member.access_level = GroupMember::MAINTAINER child_member.access_level = GroupMember::MAINTAINER
child_member.validate child_member.validate
...@@ -538,7 +538,7 @@ RSpec.describe Member do ...@@ -538,7 +538,7 @@ RSpec.describe Member do
end end
end end
describe "Delegate methods" do describe 'Delegate methods' do
it { is_expected.to respond_to(:user_name) } it { is_expected.to respond_to(:user_name) }
it { is_expected.to respond_to(:user_email) } it { is_expected.to respond_to(:user_email) }
end end
...@@ -608,29 +608,29 @@ RSpec.describe Member do ...@@ -608,29 +608,29 @@ RSpec.describe Member do
end end
end end
describe "#accept_invite!" do describe '#accept_invite!' do
let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) } let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
let(:user) { create(:user) } let(:user) { create(:user) }
it "resets the invite token" do it 'resets the invite token' do
member.accept_invite!(user) member.accept_invite!(user)
expect(member.invite_token).to be_nil expect(member.invite_token).to be_nil
end end
it "sets the invite accepted timestamp" do it 'sets the invite accepted timestamp' do
member.accept_invite!(user) member.accept_invite!(user)
expect(member.invite_accepted_at).not_to be_nil expect(member.invite_accepted_at).not_to be_nil
end end
it "sets the user" do it 'sets the user' do
member.accept_invite!(user) member.accept_invite!(user)
expect(member.user).to eq(user) expect(member.user).to eq(user)
end end
it "calls #after_accept_invite" do it 'calls #after_accept_invite' do
expect(member).to receive(:after_accept_invite) expect(member).to receive(:after_accept_invite)
member.accept_invite!(user) member.accept_invite!(user)
...@@ -657,26 +657,26 @@ RSpec.describe Member do ...@@ -657,26 +657,26 @@ RSpec.describe Member do
end end
end end
describe "#decline_invite!" do describe '#decline_invite!' do
let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) } let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
it "destroys the member" do it 'destroys the member' do
member.decline_invite! member.decline_invite!
expect(member).to be_destroyed expect(member).to be_destroyed
end end
it "calls #after_decline_invite" do it 'calls #after_decline_invite' do
expect(member).to receive(:after_decline_invite) expect(member).to receive(:after_decline_invite)
member.decline_invite! member.decline_invite!
end end
end end
describe "#generate_invite_token" do describe '#generate_invite_token' do
let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) } let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
it "sets the invite token" do it 'sets the invite token' do
expect { member.generate_invite_token }.to change { member.invite_token } expect { member.generate_invite_token }.to change { member.invite_token }
end end
end end
...@@ -684,12 +684,12 @@ RSpec.describe Member do ...@@ -684,12 +684,12 @@ RSpec.describe Member do
describe 'generate invite token on create' do describe 'generate invite token on create' do
let!(:member) { build(:project_member, invite_email: "user@example.com") } let!(:member) { build(:project_member, invite_email: "user@example.com") }
it "sets the invite token" do it 'sets the invite token' do
expect { member.save! }.to change { member.invite_token }.to(kind_of(String)) expect { member.save! }.to change { member.invite_token }.to(kind_of(String))
end end
context 'when invite was already accepted' do context 'when invite was already accepted' do
it "does not set invite token" do it 'does not set invite token' do
member.invite_accepted_at = 1.day.ago member.invite_accepted_at = 1.day.ago
expect { member.save! }.not_to change { member.invite_token }.from(nil) expect { member.save! }.not_to change { member.invite_token }.from(nil)
...@@ -744,7 +744,7 @@ RSpec.describe Member do ...@@ -744,7 +744,7 @@ RSpec.describe Member do
end end
end end
describe "#invite_to_unknown_user?" do describe '#invite_to_unknown_user?' do
subject { member.invite_to_unknown_user? } subject { member.invite_to_unknown_user? }
let(:member) { create(:project_member, invite_email: "user@example.com", invite_token: '1234', user: user) } let(:member) { create(:project_member, invite_email: "user@example.com", invite_token: '1234', user: user) }
...@@ -762,7 +762,7 @@ RSpec.describe Member do ...@@ -762,7 +762,7 @@ RSpec.describe Member do
end end
end end
describe "destroying a record", :delete do describe 'destroying a record', :delete, :sidekiq_inline do
it "refreshes user's authorized projects" do it "refreshes user's authorized projects" do
project = create(:project, :private) project = create(:project, :private)
user = create(:user) user = create(:user)
......
...@@ -244,12 +244,32 @@ RSpec.describe ProjectMember do ...@@ -244,12 +244,32 @@ RSpec.describe ProjectMember do
project.add_user(user, Gitlab::Access::GUEST) project.add_user(user, Gitlab::Access::GUEST)
end end
it 'changes access level' do context 'when :member_destroy_async_auth_refresh feature flag is enabled' do
expect { action }.to change { user.can?(:guest_access, project) }.from(true).to(false) it 'changes access level', :sidekiq_inline do
expect { action }.to change { user.can?(:guest_access, project) }.from(true).to(false)
end
it 'calls AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker to recalculate authorizations' do
expect(AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker).to receive(:perform_async).with(project.id, user.id)
action
end
it_behaves_like 'calls AuthorizedProjectUpdate::UserRefreshFromReplicaWorker with a delay to update project authorizations'
end end
it_behaves_like 'calls AuthorizedProjectUpdate::ProjectRecalculatePerUserService to recalculate authorizations' context 'when :member_destroy_async_auth_refresh feature flag is disabled' do
it_behaves_like 'calls AuthorizedProjectUpdate::UserRefreshFromReplicaWorker with a delay to update project authorizations' before do
stub_feature_flags(member_destroy_async_auth_refresh: false)
end
it 'changes access level' do
expect { action }.to change { user.can?(:guest_access, project) }.from(true).to(false)
end
it_behaves_like 'calls AuthorizedProjectUpdate::ProjectRecalculatePerUserService to recalculate authorizations'
it_behaves_like 'calls AuthorizedProjectUpdate::UserRefreshFromReplicaWorker with a delay to update project authorizations'
end
end end
context 'when the feature flag `specialized_service_for_project_member_auth_refresh` is disabled' do context 'when the feature flag `specialized_service_for_project_member_auth_refresh` is disabled' do
...@@ -298,7 +318,7 @@ RSpec.describe ProjectMember do ...@@ -298,7 +318,7 @@ RSpec.describe ProjectMember do
project.add_user(user, Gitlab::Access::GUEST) project.add_user(user, Gitlab::Access::GUEST)
end end
it 'changes access level' do it 'changes access level', :sidekiq_inline do
expect { action }.to change { user.can?(:guest_access, project) }.from(true).to(false) expect { action }.to change { user.can?(:guest_access, project) }.from(true).to(false)
end end
......
...@@ -11,6 +11,8 @@ RSpec.describe NamespaceSetting, type: :model do ...@@ -11,6 +11,8 @@ RSpec.describe NamespaceSetting, type: :model do
it { is_expected.to belong_to(:namespace) } it { is_expected.to belong_to(:namespace) }
end end
it { is_expected.to define_enum_for(:jobs_to_be_done).with_values([:basics, :move_repository, :code_storage, :exploring, :ci, :other]).with_suffix }
describe "validations" do describe "validations" do
describe "#default_branch_name_content" do describe "#default_branch_name_content" do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
......
...@@ -4,6 +4,7 @@ require 'spec_helper' ...@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe UserDetail do RSpec.describe UserDetail do
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
it { is_expected.to define_enum_for(:registration_objective).with_values([:basics, :move_repository, :code_storage, :exploring, :ci, :other, :joining_team]).with_suffix }
describe 'validations' do describe 'validations' do
describe '#job_title' do describe '#job_title' do
......
...@@ -79,6 +79,9 @@ RSpec.describe User do ...@@ -79,6 +79,9 @@ RSpec.describe User do
it { is_expected.to delegate_method(:bio).to(:user_detail).allow_nil } it { is_expected.to delegate_method(:bio).to(:user_detail).allow_nil }
it { is_expected.to delegate_method(:bio=).to(:user_detail).with_arguments(:args).allow_nil } it { is_expected.to delegate_method(:bio=).to(:user_detail).with_arguments(:args).allow_nil }
it { is_expected.to delegate_method(:registration_objective).to(:user_detail).allow_nil }
it { is_expected.to delegate_method(:registration_objective=).to(:user_detail).with_arguments(:args).allow_nil }
end end
describe 'associations' do describe 'associations' do
...@@ -123,7 +126,7 @@ RSpec.describe User do ...@@ -123,7 +126,7 @@ RSpec.describe User do
it { is_expected.to have_many(:callouts).class_name('UserCallout') } it { is_expected.to have_many(:callouts).class_name('UserCallout') }
it { is_expected.to have_many(:group_callouts).class_name('Users::GroupCallout') } it { is_expected.to have_many(:group_callouts).class_name('Users::GroupCallout') }
describe "#user_detail" do describe '#user_detail' do
it 'does not persist `user_detail` by default' do it 'does not persist `user_detail` by default' do
expect(create(:user).user_detail).not_to be_persisted expect(create(:user).user_detail).not_to be_persisted
end end
...@@ -160,25 +163,25 @@ RSpec.describe User do ...@@ -160,25 +163,25 @@ RSpec.describe User do
end end
end end
describe "#abuse_report" do describe '#abuse_report' do
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:other_user) { create(:user) } let(:other_user) { create(:user) }
it { is_expected.to have_one(:abuse_report) } it { is_expected.to have_one(:abuse_report) }
it "refers to the abuse report whose user_id is the current user" do it 'refers to the abuse report whose user_id is the current user' do
abuse_report = create(:abuse_report, reporter: other_user, user: current_user) abuse_report = create(:abuse_report, reporter: other_user, user: current_user)
expect(current_user.abuse_report).to eq(abuse_report) expect(current_user.abuse_report).to eq(abuse_report)
end end
it "does not refer to the abuse report whose reporter_id is the current user" do it 'does not refer to the abuse report whose reporter_id is the current user' do
create(:abuse_report, reporter: current_user, user: other_user) create(:abuse_report, reporter: current_user, user: other_user)
expect(current_user.abuse_report).to be_nil expect(current_user.abuse_report).to be_nil
end end
it "does not update the user_id of an abuse report when the user is updated" do it 'does not update the user_id of an abuse report when the user is updated' do
abuse_report = create(:abuse_report, reporter: current_user, user: other_user) abuse_report = create(:abuse_report, reporter: current_user, user: other_user)
current_user.block current_user.block
...@@ -716,7 +719,7 @@ RSpec.describe User do ...@@ -716,7 +719,7 @@ RSpec.describe User do
end end
end end
describe "scopes" do describe 'scopes' do
context 'blocked users' do context 'blocked users' do
let_it_be(:active_user) { create(:user) } let_it_be(:active_user) { create(:user) }
let_it_be(:blocked_user) { create(:user, :blocked) } let_it_be(:blocked_user) { create(:user, :blocked) }
...@@ -754,8 +757,8 @@ RSpec.describe User do ...@@ -754,8 +757,8 @@ RSpec.describe User do
end end
end end
describe ".with_two_factor" do describe '.with_two_factor' do
it "returns users with 2fa enabled via OTP" do it 'returns users with 2fa enabled via OTP' do
user_with_2fa = create(:user, :two_factor_via_otp) user_with_2fa = create(:user, :two_factor_via_otp)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_with_two_factor = described_class.with_two_factor.pluck(:id) users_with_two_factor = described_class.with_two_factor.pluck(:id)
...@@ -764,8 +767,8 @@ RSpec.describe User do ...@@ -764,8 +767,8 @@ RSpec.describe User do
expect(users_with_two_factor).not_to include(user_without_2fa.id) expect(users_with_two_factor).not_to include(user_without_2fa.id)
end end
shared_examples "returns the right users" do |trait| shared_examples 'returns the right users' do |trait|
it "returns users with 2fa enabled via hardware token" do it 'returns users with 2fa enabled via hardware token' do
user_with_2fa = create(:user, trait) user_with_2fa = create(:user, trait)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_with_two_factor = described_class.with_two_factor.pluck(:id) users_with_two_factor = described_class.with_two_factor.pluck(:id)
...@@ -774,7 +777,7 @@ RSpec.describe User do ...@@ -774,7 +777,7 @@ RSpec.describe User do
expect(users_with_two_factor).not_to include(user_without_2fa.id) expect(users_with_two_factor).not_to include(user_without_2fa.id)
end end
it "returns users with 2fa enabled via OTP and hardware token" do it 'returns users with 2fa enabled via OTP and hardware token' do
user_with_2fa = create(:user, :two_factor_via_otp, trait) user_with_2fa = create(:user, :two_factor_via_otp, trait)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_with_two_factor = described_class.with_two_factor.pluck(:id) users_with_two_factor = described_class.with_two_factor.pluck(:id)
...@@ -792,17 +795,17 @@ RSpec.describe User do ...@@ -792,17 +795,17 @@ RSpec.describe User do
end end
end end
describe "and U2F" do describe 'and U2F' do
it_behaves_like "returns the right users", :two_factor_via_u2f it_behaves_like "returns the right users", :two_factor_via_u2f
end end
describe "and WebAuthn" do describe 'and WebAuthn' do
it_behaves_like "returns the right users", :two_factor_via_webauthn it_behaves_like "returns the right users", :two_factor_via_webauthn
end end
end end
describe ".without_two_factor" do describe '.without_two_factor' do
it "excludes users with 2fa enabled via OTP" do it 'excludes users with 2fa enabled via OTP' do
user_with_2fa = create(:user, :two_factor_via_otp) user_with_2fa = create(:user, :two_factor_via_otp)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id) users_without_two_factor = described_class.without_two_factor.pluck(:id)
...@@ -811,8 +814,8 @@ RSpec.describe User do ...@@ -811,8 +814,8 @@ RSpec.describe User do
expect(users_without_two_factor).not_to include(user_with_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id)
end end
describe "and u2f" do describe 'and u2f' do
it "excludes users with 2fa enabled via U2F" do it 'excludes users with 2fa enabled via U2F' do
user_with_2fa = create(:user, :two_factor_via_u2f) user_with_2fa = create(:user, :two_factor_via_u2f)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id) users_without_two_factor = described_class.without_two_factor.pluck(:id)
...@@ -821,7 +824,7 @@ RSpec.describe User do ...@@ -821,7 +824,7 @@ RSpec.describe User do
expect(users_without_two_factor).not_to include(user_with_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id)
end end
it "excludes users with 2fa enabled via OTP and U2F" do it 'excludes users with 2fa enabled via OTP and U2F' do
user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f) user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id) users_without_two_factor = described_class.without_two_factor.pluck(:id)
...@@ -831,8 +834,8 @@ RSpec.describe User do ...@@ -831,8 +834,8 @@ RSpec.describe User do
end end
end end
describe "and webauthn" do describe 'and webauthn' do
it "excludes users with 2fa enabled via WebAuthn" do it 'excludes users with 2fa enabled via WebAuthn' do
user_with_2fa = create(:user, :two_factor_via_webauthn) user_with_2fa = create(:user, :two_factor_via_webauthn)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id) users_without_two_factor = described_class.without_two_factor.pluck(:id)
...@@ -841,7 +844,7 @@ RSpec.describe User do ...@@ -841,7 +844,7 @@ RSpec.describe User do
expect(users_without_two_factor).not_to include(user_with_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id)
end end
it "excludes users with 2fa enabled via OTP and WebAuthn" do it 'excludes users with 2fa enabled via OTP and WebAuthn' do
user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_webauthn) user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_webauthn)
user_without_2fa = create(:user) user_without_2fa = create(:user)
users_without_two_factor = described_class.without_two_factor.pluck(:id) users_without_two_factor = described_class.without_two_factor.pluck(:id)
...@@ -1074,7 +1077,7 @@ RSpec.describe User do ...@@ -1074,7 +1077,7 @@ RSpec.describe User do
end end
end end
describe "Respond to" do describe 'Respond to' do
it { is_expected.to respond_to(:admin?) } it { is_expected.to respond_to(:admin?) }
it { is_expected.to respond_to(:name) } it { is_expected.to respond_to(:name) }
it { is_expected.to respond_to(:external?) } it { is_expected.to respond_to(:external?) }
...@@ -1096,7 +1099,7 @@ RSpec.describe User do ...@@ -1096,7 +1099,7 @@ RSpec.describe User do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:external_user) { create(:user, external: true) } let(:external_user) { create(:user, external: true) }
it "sets other properties as well" do it 'sets other properties as well' do
expect(external_user.can_create_team).to be_falsey expect(external_user.can_create_team).to be_falsey
expect(external_user.can_create_group).to be_falsey expect(external_user.can_create_group).to be_falsey
expect(external_user.projects_limit).to be 0 expect(external_user.projects_limit).to be 0
...@@ -1515,7 +1518,7 @@ RSpec.describe User do ...@@ -1515,7 +1518,7 @@ RSpec.describe User do
end end
describe '#generate_password' do describe '#generate_password' do
it "does not generate password by default" do it 'does not generate password by default' do
user = create(:user, password: 'abcdefghe') user = create(:user, password: 'abcdefghe')
expect(user.password).to eq('abcdefghe') expect(user.password).to eq('abcdefghe')
...@@ -1883,14 +1886,14 @@ RSpec.describe User do ...@@ -1883,14 +1886,14 @@ RSpec.describe User do
describe 'deactivating a user' do describe 'deactivating a user' do
let(:user) { create(:user, name: 'John Smith') } let(:user) { create(:user, name: 'John Smith') }
context "an active user" do context 'an active user' do
it "can be deactivated" do it 'can be deactivated' do
user.deactivate user.deactivate
expect(user.deactivated?).to be_truthy expect(user.deactivated?).to be_truthy
end end
context "when user deactivation emails are disabled" do context 'when user deactivation emails are disabled' do
before do before do
stub_application_setting(user_deactivation_emails_enabled: false) stub_application_setting(user_deactivation_emails_enabled: false)
end end
...@@ -1901,7 +1904,7 @@ RSpec.describe User do ...@@ -1901,7 +1904,7 @@ RSpec.describe User do
end end
end end
context "when user deactivation emails are enabled" do context 'when user deactivation emails are enabled' do
it 'sends deactivated user an email' do it 'sends deactivated user an email' do
expect_next_instance_of(NotificationService) do |notification| expect_next_instance_of(NotificationService) do |notification|
allow(notification).to receive(:user_deactivated).with(user.name, user.notification_email_or_default) allow(notification).to receive(:user_deactivated).with(user.name, user.notification_email_or_default)
...@@ -1912,12 +1915,12 @@ RSpec.describe User do ...@@ -1912,12 +1915,12 @@ RSpec.describe User do
end end
end end
context "a user who is blocked" do context 'a user who is blocked' do
before do before do
user.block user.block
end end
it "cannot be deactivated" do it 'cannot be deactivated' do
user.deactivate user.deactivate
expect(user.reload.deactivated?).to be_falsy expect(user.reload.deactivated?).to be_falsy
...@@ -2084,7 +2087,7 @@ RSpec.describe User do ...@@ -2084,7 +2087,7 @@ RSpec.describe User do
describe 'with defaults' do describe 'with defaults' do
let(:user) { described_class.new } let(:user) { described_class.new }
it "applies defaults to user" do it 'applies defaults to user' do
expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit) expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit)
expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group) expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group)
expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme) expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme)
...@@ -2096,7 +2099,7 @@ RSpec.describe User do ...@@ -2096,7 +2099,7 @@ RSpec.describe User do
describe 'with default overrides' do describe 'with default overrides' do
let(:user) { described_class.new(projects_limit: 123, can_create_group: false, can_create_team: true) } let(:user) { described_class.new(projects_limit: 123, can_create_group: false, can_create_team: true) }
it "applies defaults to user" do it 'applies defaults to user' do
expect(user.projects_limit).to eq(123) expect(user.projects_limit).to eq(123)
expect(user.can_create_group).to be_falsey expect(user.can_create_group).to be_falsey
expect(user.theme_id).to eq(1) expect(user.theme_id).to eq(1)
...@@ -2115,7 +2118,7 @@ RSpec.describe User do ...@@ -2115,7 +2118,7 @@ RSpec.describe User do
stub_application_setting(user_default_external: true) stub_application_setting(user_default_external: true)
end end
it "creates external user by default" do it 'creates external user by default' do
user = create(:user) user = create(:user)
expect(user.external).to be_truthy expect(user.external).to be_truthy
...@@ -2124,7 +2127,7 @@ RSpec.describe User do ...@@ -2124,7 +2127,7 @@ RSpec.describe User do
end end
describe 'with default overrides' do describe 'with default overrides' do
it "creates a non-external user" do it 'creates a non-external user' do
user = create(:user, external: false) user = create(:user, external: false)
expect(user.external).to be_falsey expect(user.external).to be_falsey
...@@ -2140,7 +2143,7 @@ RSpec.describe User do ...@@ -2140,7 +2143,7 @@ RSpec.describe User do
} }
protocol_and_expectation.each do |protocol, expected| protocol_and_expectation.each do |protocol, expected|
it "has correct require_ssh_key?" do it 'has correct require_ssh_key?' do
stub_application_setting(enabled_git_access_protocol: protocol) stub_application_setting(enabled_git_access_protocol: protocol)
user = build(:user) user = build(:user)
...@@ -2624,7 +2627,7 @@ RSpec.describe User do ...@@ -2624,7 +2627,7 @@ RSpec.describe User do
describe 'all_ssh_keys' do describe 'all_ssh_keys' do
it { is_expected.to have_many(:keys).dependent(:destroy) } it { is_expected.to have_many(:keys).dependent(:destroy) }
it "has all ssh keys" do it 'has all ssh keys' do
user = create :user user = create :user
key = create :key, key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD33bWLBxu48Sev9Fert1yzEO4WGcWglWF7K/AwblIUFselOt/QdOL9DSjpQGxLagO1s9wl53STIO8qGS4Ms0EJZyIXOEFMjFJ5xmjSy+S37By4sG7SsltQEHMxtbtFOaW5LV2wCrX+rUsRNqLMamZjgjcPO0/EgGCXIGMAYW4O7cwGZdXWYIhQ1Vwy+CsVMDdPkPgBXqK7nR/ey8KMs8ho5fMNgB5hBw/AL9fNGhRw3QTD6Q12Nkhl4VZES2EsZqlpNnJttnPdp847DUsT6yuLRlfiQfz5Cn9ysHFdXObMN5VYIiPFwHeYCZp1X2S4fDZooRE8uOLTfxWHPXwrhqSH", user_id: user.id key = create :key, key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD33bWLBxu48Sev9Fert1yzEO4WGcWglWF7K/AwblIUFselOt/QdOL9DSjpQGxLagO1s9wl53STIO8qGS4Ms0EJZyIXOEFMjFJ5xmjSy+S37By4sG7SsltQEHMxtbtFOaW5LV2wCrX+rUsRNqLMamZjgjcPO0/EgGCXIGMAYW4O7cwGZdXWYIhQ1Vwy+CsVMDdPkPgBXqK7nR/ey8KMs8ho5fMNgB5hBw/AL9fNGhRw3QTD6Q12Nkhl4VZES2EsZqlpNnJttnPdp847DUsT6yuLRlfiQfz5Cn9ysHFdXObMN5VYIiPFwHeYCZp1X2S4fDZooRE8uOLTfxWHPXwrhqSH", user_id: user.id
...@@ -2660,10 +2663,10 @@ RSpec.describe User do ...@@ -2660,10 +2663,10 @@ RSpec.describe User do
end end
end end
describe "#clear_avatar_caches" do describe '#clear_avatar_caches' do
let(:user) { create(:user) } let(:user) { create(:user) }
it "clears the avatar cache when saving" do it 'clears the avatar cache when saving' do
allow(user).to receive(:avatar_changed?).and_return(true) allow(user).to receive(:avatar_changed?).and_return(true)
expect(Gitlab::AvatarCache).to receive(:delete_by_email).with(*user.verified_emails) expect(Gitlab::AvatarCache).to receive(:delete_by_email).with(*user.verified_emails)
...@@ -3189,7 +3192,7 @@ RSpec.describe User do ...@@ -3189,7 +3192,7 @@ RSpec.describe User do
end end
end end
describe "#last_active_at" do describe '#last_active_at' do
let(:last_activity_on) { 5.days.ago.to_date } let(:last_activity_on) { 5.days.ago.to_date }
let(:current_sign_in_at) { 8.days.ago } let(:current_sign_in_at) { 8.days.ago }
...@@ -3227,7 +3230,7 @@ RSpec.describe User do ...@@ -3227,7 +3230,7 @@ RSpec.describe User do
end end
end end
describe "#can_be_deactivated?" do describe '#can_be_deactivated?' do
let(:activity) { {} } let(:activity) { {} }
let(:user) { create(:user, name: 'John Smith', **activity) } let(:user) { create(:user, name: 'John Smith', **activity) }
let(:day_within_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.pred.days.ago } let(:day_within_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.pred.days.ago }
...@@ -3245,7 +3248,7 @@ RSpec.describe User do ...@@ -3245,7 +3248,7 @@ RSpec.describe User do
end end
end end
context "a user who is not active" do context 'a user who is not active' do
before do before do
user.block user.block
end end
...@@ -3286,7 +3289,7 @@ RSpec.describe User do ...@@ -3286,7 +3289,7 @@ RSpec.describe User do
end end
end end
describe "#contributed_projects" do describe '#contributed_projects' do
subject { create(:user) } subject { create(:user) }
let!(:project1) { create(:project) } let!(:project1) { create(:project) }
...@@ -3301,11 +3304,11 @@ RSpec.describe User do ...@@ -3301,11 +3304,11 @@ RSpec.describe User do
project2.add_maintainer(subject) project2.add_maintainer(subject)
end end
it "includes IDs for projects the user has pushed to" do it 'includes IDs for projects the user has pushed to' do
expect(subject.contributed_projects).to include(project1) expect(subject.contributed_projects).to include(project1)
end end
it "includes IDs for projects the user has had merge requests merged into" do it 'includes IDs for projects the user has had merge requests merged into' do
expect(subject.contributed_projects).to include(project3) expect(subject.contributed_projects).to include(project3)
end end
...@@ -3399,7 +3402,7 @@ RSpec.describe User do ...@@ -3399,7 +3402,7 @@ RSpec.describe User do
end end
end end
describe "#recent_push" do describe '#recent_push' do
let(:user) { build(:user) } let(:user) { build(:user) }
let(:project) { build(:project) } let(:project) { build(:project) }
let(:event) { build(:push_event) } let(:event) { build(:push_event) }
...@@ -3563,7 +3566,7 @@ RSpec.describe User do ...@@ -3563,7 +3566,7 @@ RSpec.describe User do
expect(user.authorized_projects).to include(project) expect(user.authorized_projects).to include(project)
end end
it "includes personal projects user has been given access to" do it 'includes personal projects user has been given access to' do
user1 = create(:user) user1 = create(:user)
user2 = create(:user) user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace) project = create(:project, :private, namespace: user1.namespace)
...@@ -3573,7 +3576,7 @@ RSpec.describe User do ...@@ -3573,7 +3576,7 @@ RSpec.describe User do
expect(user2.authorized_projects).to include(project) expect(user2.authorized_projects).to include(project)
end end
it "includes projects of groups user has been added to" do it 'includes projects of groups user has been added to' do
group = create(:group) group = create(:group)
project = create(:project, group: group) project = create(:project, group: group)
user = create(:user) user = create(:user)
...@@ -3583,7 +3586,7 @@ RSpec.describe User do ...@@ -3583,7 +3586,7 @@ RSpec.describe User do
expect(user.authorized_projects).to include(project) expect(user.authorized_projects).to include(project)
end end
it "does not include projects of groups user has been removed from" do it 'does not include projects of groups user has been removed from', :sidekiq_inline do
group = create(:group) group = create(:group)
project = create(:project, group: group) project = create(:project, group: group)
user = create(:user) user = create(:user)
...@@ -3608,7 +3611,7 @@ RSpec.describe User do ...@@ -3608,7 +3611,7 @@ RSpec.describe User do
expect(user.authorized_projects).to include(project) expect(user.authorized_projects).to include(project)
end end
it "does not include destroyed projects user had access to" do it 'does not include destroyed projects user had access to' do
user1 = create(:user) user1 = create(:user)
user2 = create(:user) user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace) project = create(:project, :private, namespace: user1.namespace)
...@@ -3622,7 +3625,7 @@ RSpec.describe User do ...@@ -3622,7 +3625,7 @@ RSpec.describe User do
expect(user2.authorized_projects).not_to include(project) expect(user2.authorized_projects).not_to include(project)
end end
it "does not include projects of destroyed groups user had access to" do it 'does not include projects of destroyed groups user had access to' do
group = create(:group) group = create(:group)
project = create(:project, namespace: group) project = create(:project, namespace: group)
user = create(:user) user = create(:user)
...@@ -4175,7 +4178,7 @@ RSpec.describe User do ...@@ -4175,7 +4178,7 @@ RSpec.describe User do
expect(user.admin).to be true expect(user.admin).to be true
end end
it "accepts string values in addition to symbols" do it 'accepts string values in addition to symbols' do
user.access_level = 'admin' user.access_level = 'admin'
expect(user.access_level).to eq(:admin) expect(user.access_level).to eq(:admin)
...@@ -4256,7 +4259,7 @@ RSpec.describe User do ...@@ -4256,7 +4259,7 @@ RSpec.describe User do
expect(ghost.user_type).to eq 'ghost' expect(ghost.user_type).to eq 'ghost'
end end
it "does not create a second ghost user if one is already present" do it 'does not create a second ghost user if one is already present' do
expect do expect do
described_class.ghost described_class.ghost
described_class.ghost described_class.ghost
...@@ -4265,7 +4268,7 @@ RSpec.describe User do ...@@ -4265,7 +4268,7 @@ RSpec.describe User do
end end
context "when a regular user exists with the username 'ghost'" do context "when a regular user exists with the username 'ghost'" do
it "creates a ghost user with a non-conflicting username" do it 'creates a ghost user with a non-conflicting username' do
create(:user, username: 'ghost') create(:user, username: 'ghost')
ghost = described_class.ghost ghost = described_class.ghost
...@@ -4275,7 +4278,7 @@ RSpec.describe User do ...@@ -4275,7 +4278,7 @@ RSpec.describe User do
end end
context "when a regular user exists with the email 'ghost@example.com'" do context "when a regular user exists with the email 'ghost@example.com'" do
it "creates a ghost user with a non-conflicting email" do it 'creates a ghost user with a non-conflicting email' do
create(:user, email: 'ghost@example.com') create(:user, email: 'ghost@example.com')
ghost = described_class.ghost ghost = described_class.ghost
...@@ -4755,7 +4758,7 @@ RSpec.describe User do ...@@ -4755,7 +4758,7 @@ RSpec.describe User do
it { is_expected.to be true } it { is_expected.to be true }
end end
context 'when email and username aren\'t changed' do context "when email and username aren't changed" do
before do before do
user.name = 'new_name' user.name = 'new_name'
end end
...@@ -5066,26 +5069,26 @@ RSpec.describe User do ...@@ -5066,26 +5069,26 @@ RSpec.describe User do
subject { user.required_terms_not_accepted? } subject { user.required_terms_not_accepted? }
context "when terms are not enforced" do context 'when terms are not enforced' do
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end end
context "when terms are enforced" do context 'when terms are enforced' do
before do before do
enforce_terms enforce_terms
end end
it "is not accepted by the user" do it 'is not accepted by the user' do
expect(subject).to be_truthy expect(subject).to be_truthy
end end
it "is accepted by the user" do it 'is accepted by the user' do
accept_terms(user) accept_terms(user)
expect(subject).to be_falsey expect(subject).to be_falsey
end end
it "auto accepts the term for project bots" do it 'auto accepts the term for project bots' do
expect(project_bot.required_terms_not_accepted?).to be_falsey expect(project_bot.required_terms_not_accepted?).to be_falsey
end end
end end
......
...@@ -38,7 +38,7 @@ RSpec.describe API::PackageFiles do ...@@ -38,7 +38,7 @@ RSpec.describe API::PackageFiles do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
it 'returns 404 for a user without access to the project' do it 'returns 404 for a user without access to the project', :sidekiq_inline do
project.team.truncate project.team.truncate
get api(url, user) get api(url, user)
......
...@@ -275,7 +275,7 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do ...@@ -275,7 +275,7 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
expect(subject[:merge_pipeline]).to be_nil expect(subject[:merge_pipeline]).to be_nil
end end
context 'when is merged' do context 'when is merged', :sidekiq_inline do
let(:resource) { create(:merged_merge_request, source_project: project, merge_commit_sha: project.commit.id) } let(:resource) { create(:merged_merge_request, source_project: project, merge_commit_sha: project.commit.id) }
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.target_branch, sha: resource.merge_commit_sha) } let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.target_branch, sha: resource.merge_commit_sha) }
......
...@@ -17,7 +17,7 @@ RSpec.describe MergeRequests::AssignIssuesService do ...@@ -17,7 +17,7 @@ RSpec.describe MergeRequests::AssignIssuesService do
expect(service.assignable_issues.map(&:id)).to include(issue.id) expect(service.assignable_issues.map(&:id)).to include(issue.id)
end end
it 'ignores issues the user cannot update assignee on' do it 'ignores issues the user cannot update assignee on', :sidekiq_inline do
project.team.truncate project.team.truncate
expect(service.assignable_issues).to be_empty expect(service.assignable_issues).to be_empty
......
...@@ -440,7 +440,7 @@ RSpec.describe MergeRequests::BuildService do ...@@ -440,7 +440,7 @@ RSpec.describe MergeRequests::BuildService do
expect(merge_request.title).to eq('Closes #1234 Second commit') expect(merge_request.title).to eq('Closes #1234 Second commit')
end end
it 'adds the remaining lines of the first multi-line commit message as the description' do it 'adds the remaining lines of the first multi-line commit message as the description', :sidekiq_inline do
expect(merge_request.description).to eq('Create the app') expect(merge_request.description).to eq('Create the app')
end end
end end
......
...@@ -701,7 +701,7 @@ RSpec.describe MergeRequests::PushOptionsHandlerService do ...@@ -701,7 +701,7 @@ RSpec.describe MergeRequests::PushOptionsHandlerService do
let(:push_options) { { create: true } } let(:push_options) { { create: true } }
let(:changes) { new_branch_changes } let(:changes) { new_branch_changes }
it 'records an error' do it 'records an error', :sidekiq_inline do
Members::DestroyService.new(user1).execute(ProjectMember.find_by!(user_id: user1.id)) Members::DestroyService.new(user1).execute(ProjectMember.find_by!(user_id: user1.id))
service.execute service.execute
......
...@@ -47,7 +47,7 @@ RSpec.describe Notes::QuickActionsService do ...@@ -47,7 +47,7 @@ RSpec.describe Notes::QuickActionsService do
let(:note_text) { "/relate #{other_issue.to_reference}" } let(:note_text) { "/relate #{other_issue.to_reference}" }
let(:note) { create(:note_on_issue, noteable: issue, project: project, note: note_text) } let(:note) { create(:note_on_issue, noteable: issue, project: project, note: note_text) }
context 'user cannot relate issues' do context 'user cannot relate issues', :sidekiq_inline do
before do before do
project.team.find_member(maintainer.id).destroy! project.team.find_member(maintainer.id).destroy!
project.update!(visibility: Gitlab::VisibilityLevel::PUBLIC) project.update!(visibility: Gitlab::VisibilityLevel::PUBLIC)
......
...@@ -3155,7 +3155,7 @@ RSpec.describe NotificationService, :mailer do ...@@ -3155,7 +3155,7 @@ RSpec.describe NotificationService, :mailer do
notification.pipeline_finished(pipeline) notification.pipeline_finished(pipeline)
end end
it 'does not send emails' do it 'does not send emails', :sidekiq_inline do
should_not_email_anyone should_not_email_anyone
end end
end end
......
...@@ -26,7 +26,7 @@ RSpec.describe Projects::MoveAccessService do ...@@ -26,7 +26,7 @@ RSpec.describe Projects::MoveAccessService do
describe '#execute' do describe '#execute' do
shared_examples 'move the accesses' do shared_examples 'move the accesses' do
it do it 'moves the accesses', :sidekiq_inline do
expect(project_with_access.project_members.count).to eq 4 expect(project_with_access.project_members.count).to eq 4
expect(project_with_access.project_group_links.count).to eq 3 expect(project_with_access.project_group_links.count).to eq 3
expect(project_with_access.authorized_users.count).to eq 4 expect(project_with_access.authorized_users.count).to eq 4
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker do
include ExclusiveLeaseHelpers
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
subject(:worker) { described_class.new }
include_examples 'an idempotent worker' do
let(:job_args) { [project.id, user.id] }
it 'does not change authorizations when run twice' do
project.add_developer(user)
user.project_authorizations.delete_all
expect { worker.perform(project.id, user.id) }.to change { project.project_authorizations.reload.size }.by(1)
expect { worker.perform(project.id, user.id) }.not_to change { project.project_authorizations.reload.size }
end
end
describe '#perform' do
it 'does not fail if the project does not exist' do
expect do
worker.perform(non_existing_record_id, user.id)
end.not_to raise_error
end
it 'does not fail if the user does not exist' do
expect do
worker.perform(project.id, non_existing_record_id)
end.not_to raise_error
end
it 'calls AuthorizedProjectUpdate::ProjectRecalculatePerUserService' do
expect_next_instance_of(AuthorizedProjectUpdate::ProjectRecalculatePerUserService, project, user) do |service|
expect(service).to receive(:execute)
end
worker.perform(project.id, user.id)
end
context 'exclusive lease' do
let(:lock_key) { "#{described_class.superclass.name.underscore}/projects/#{project.id}" }
let(:timeout) { 10.seconds }
context 'when exclusive lease has not been taken' do
it 'obtains a new exclusive lease' do
expect_to_obtain_exclusive_lease(lock_key, timeout: timeout)
worker.perform(project.id, user.id)
end
end
context 'when exclusive lease has already been taken' do
before do
stub_exclusive_lease_taken(lock_key, timeout: timeout)
end
it 'raises an error' do
expect { worker.perform(project.id, user.id) }.to raise_error(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError)
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment