2019-03-30 03:23:56 -04:00
# frozen_string_literal: true
2012-10-02 11:17:12 -04:00
require 'spec_helper'
2017-07-10 10:24:02 -04:00
describe Group do
2016-11-11 07:51:50 -05:00
let! ( :group ) { create ( :group , :access_requestable ) }
2012-10-02 11:20:46 -04:00
2015-05-02 23:11:21 -04:00
describe 'associations' do
2015-02-12 13:17:35 -05:00
it { is_expected . to have_many :projects }
2016-04-18 12:53:32 -04:00
it { is_expected . to have_many ( :group_members ) . dependent ( :destroy ) }
it { is_expected . to have_many ( :users ) . through ( :group_members ) }
2016-06-27 10:20:57 -04:00
it { is_expected . to have_many ( :owners ) . through ( :group_members ) }
it { is_expected . to have_many ( :requesters ) . dependent ( :destroy ) }
2017-09-05 12:03:24 -04:00
it { is_expected . to have_many ( :members_and_requesters ) }
2016-04-18 12:53:32 -04:00
it { is_expected . to have_many ( :project_group_links ) . dependent ( :destroy ) }
it { is_expected . to have_many ( :shared_projects ) . through ( :project_group_links ) }
it { is_expected . to have_many ( :notification_settings ) . dependent ( :destroy ) }
2016-09-19 11:04:38 -04:00
it { is_expected . to have_many ( :labels ) . class_name ( 'GroupLabel' ) }
2017-05-03 14:51:55 -04:00
it { is_expected . to have_many ( :variables ) . class_name ( 'Ci::GroupVariable' ) }
2018-05-02 12:21:42 -04:00
it { is_expected . to have_many ( :uploads ) }
2017-02-02 09:04:02 -05:00
it { is_expected . to have_one ( :chat_team ) }
2017-09-18 11:07:38 -04:00
it { is_expected . to have_many ( :custom_attributes ) . class_name ( 'GroupCustomAttribute' ) }
2018-03-05 12:51:40 -05:00
it { is_expected . to have_many ( :badges ) . class_name ( 'GroupBadge' ) }
2018-10-14 16:42:29 -04:00
it { is_expected . to have_many ( :cluster_groups ) . class_name ( 'Clusters::Group' ) }
it { is_expected . to have_many ( :clusters ) . class_name ( 'Clusters::Cluster' ) }
2016-06-27 10:20:57 -04:00
describe '#members & #requesters' do
let ( :requester ) { create ( :user ) }
let ( :developer ) { create ( :user ) }
before do
group . request_access ( requester )
group . add_developer ( developer )
end
2017-09-05 12:03:24 -04:00
it_behaves_like 'members and requesters associations' do
let ( :namespace ) { group }
2016-06-27 10:20:57 -04:00
end
end
2013-06-26 08:32:09 -04:00
end
2015-05-02 23:11:21 -04:00
describe 'modules' do
subject { described_class }
it { is_expected . to include_module ( Referable ) }
end
describe 'validations' do
it { is_expected . to validate_presence_of :name }
it { is_expected . to validate_presence_of :path }
it { is_expected . not_to validate_presence_of :owner }
2017-01-24 16:09:58 -05:00
it { is_expected . to validate_presence_of :two_factor_grace_period }
it { is_expected . to validate_numericality_of ( :two_factor_grace_period ) . is_greater_than_or_equal_to ( 0 ) }
2017-04-05 09:41:00 -04:00
describe 'path validation' do
it 'rejects paths reserved on the root namespace when the group has no parent' do
group = build ( :group , path : 'api' )
expect ( group ) . not_to be_valid
end
it 'allows root paths when the group has a parent' do
group = build ( :group , path : 'api' , parent : create ( :group ) )
expect ( group ) . to be_valid
end
it 'rejects any wildcard paths when not a top level group' do
group = build ( :group , path : 'tree' , parent : create ( :group ) )
expect ( group ) . not_to be_valid
end
end
2017-08-15 21:25:47 -04:00
2018-05-28 14:43:46 -04:00
describe '#notification_settings' , :nested_groups do
let ( :user ) { create ( :user ) }
let ( :group ) { create ( :group ) }
let ( :sub_group ) { create ( :group , parent_id : group . id ) }
before do
group . add_developer ( user )
2018-12-06 08:15:29 -05:00
sub_group . add_maintainer ( user )
2018-05-28 14:43:46 -04:00
end
it 'also gets notification settings from parent groups' do
expect ( sub_group . notification_settings . size ) . to eq ( 2 )
expect ( sub_group . notification_settings ) . to include ( group . notification_settings . first )
end
context 'when sub group is deleted' do
it 'does not delete parent notification settings' do
expect do
sub_group . destroy
end . to change { NotificationSetting . count } . by ( - 1 )
end
end
end
2017-08-15 21:25:47 -04:00
describe '#visibility_level_allowed_by_parent' do
let ( :parent ) { create ( :group , :internal ) }
let ( :sub_group ) { build ( :group , parent_id : parent . id ) }
context 'without a parent' do
it 'is valid' do
sub_group . parent_id = nil
expect ( sub_group ) . to be_valid
end
end
context 'with a parent' do
context 'when visibility of sub group is greater than the parent' do
it 'is invalid' do
sub_group . visibility_level = Gitlab :: VisibilityLevel :: PUBLIC
expect ( sub_group ) . to be_invalid
end
end
context 'when visibility of sub group is lower or equal to the parent' do
[ Gitlab :: VisibilityLevel :: INTERNAL , Gitlab :: VisibilityLevel :: PRIVATE ] . each do | level |
it 'is valid' do
sub_group . visibility_level = level
expect ( sub_group ) . to be_valid
end
end
end
end
end
2017-08-23 12:51:11 -04:00
describe '#visibility_level_allowed_by_projects' do
let! ( :internal_group ) { create ( :group , :internal ) }
let! ( :internal_project ) { create ( :project , :internal , group : internal_group ) }
context 'when group has a lower visibility' do
it 'is invalid' do
internal_group . visibility_level = Gitlab :: VisibilityLevel :: PRIVATE
expect ( internal_group ) . to be_invalid
2017-08-25 12:57:16 -04:00
expect ( internal_group . errors [ :visibility_level ] ) . to include ( 'private is not allowed since this group contains projects with higher visibility.' )
2017-08-23 12:51:11 -04:00
end
end
context 'when group has a higher visibility' do
it 'is valid' do
internal_group . visibility_level = Gitlab :: VisibilityLevel :: PUBLIC
expect ( internal_group ) . to be_valid
end
end
end
describe '#visibility_level_allowed_by_sub_groups' do
let! ( :internal_group ) { create ( :group , :internal ) }
let! ( :internal_sub_group ) { create ( :group , :internal , parent : internal_group ) }
context 'when parent group has a lower visibility' do
it 'is invalid' do
internal_group . visibility_level = Gitlab :: VisibilityLevel :: PRIVATE
expect ( internal_group ) . to be_invalid
2017-08-25 12:57:16 -04:00
expect ( internal_group . errors [ :visibility_level ] ) . to include ( 'private is not allowed since there are sub-groups with higher visibility.' )
2017-08-23 12:51:11 -04:00
end
end
context 'when parent group has a higher visibility' do
it 'is valid' do
internal_group . visibility_level = Gitlab :: VisibilityLevel :: PUBLIC
expect ( internal_group ) . to be_valid
end
end
end
2015-05-02 23:11:21 -04:00
end
2018-09-07 08:29:19 -04:00
describe '.public_or_visible_to_user' do
let! ( :private_group ) { create ( :group , :private ) }
let! ( :internal_group ) { create ( :group , :internal ) }
2015-11-18 06:27:21 -05:00
2018-09-07 08:29:19 -04:00
subject { described_class . public_or_visible_to_user ( user ) }
2015-11-18 06:27:21 -05:00
2018-09-07 08:29:19 -04:00
context 'when user is nil' do
let! ( :user ) { nil }
2015-11-18 06:27:21 -05:00
2018-09-07 08:29:19 -04:00
it { is_expected . to match_array ( [ group ] ) }
2015-11-18 06:27:21 -05:00
end
2018-09-07 08:29:19 -04:00
context 'when user' do
let! ( :user ) { create ( :user ) }
context 'when user does not have access to any private group' do
it { is_expected . to match_array ( [ internal_group , group ] ) }
end
context 'when user is a member of private group' do
before do
private_group . add_user ( user , Gitlab :: Access :: DEVELOPER )
end
it { is_expected . to match_array ( [ private_group , internal_group , group ] ) }
end
context 'when user is a member of private subgroup' , :postgresql do
let! ( :private_subgroup ) { create ( :group , :private , parent : private_group ) }
before do
private_subgroup . add_user ( user , Gitlab :: Access :: DEVELOPER )
end
it { is_expected . to match_array ( [ private_subgroup , internal_group , group ] ) }
end
2015-11-18 06:27:21 -05:00
end
end
2016-03-01 10:22:29 -05:00
describe 'scopes' do
2016-03-18 08:28:16 -04:00
let! ( :private_group ) { create ( :group , :private ) }
let! ( :internal_group ) { create ( :group , :internal ) }
2016-03-01 10:22:29 -05:00
describe 'public_only' do
2016-03-18 08:28:16 -04:00
subject { described_class . public_only . to_a }
2016-03-01 10:22:29 -05:00
2017-01-20 06:25:53 -05:00
it { is_expected . to eq ( [ group ] ) }
2016-03-01 10:22:29 -05:00
end
describe 'public_and_internal_only' do
2016-03-21 19:09:20 -04:00
subject { described_class . public_and_internal_only . to_a }
2016-03-01 10:22:29 -05:00
2017-01-20 06:25:53 -05:00
it { is_expected . to match_array ( [ group , internal_group ] ) }
end
describe 'non_public_only' do
subject { described_class . non_public_only . to_a }
it { is_expected . to match_array ( [ private_group , internal_group ] ) }
2016-03-01 10:22:29 -05:00
end
end
2015-05-02 23:11:21 -04:00
describe '#to_reference' do
it 'returns a String reference to the object' do
expect ( group . to_reference ) . to eq " @ #{ group . name } "
end
end
2012-11-20 23:14:05 -05:00
2016-07-11 18:12:31 -04:00
describe '#users' do
2015-02-12 13:17:35 -05:00
it { expect ( group . users ) . to eq ( group . owners ) }
2012-11-20 23:14:05 -05:00
end
2016-07-11 18:12:31 -04:00
describe '#human_name' do
2015-02-12 13:17:35 -05:00
it { expect ( group . human_name ) . to eq ( group . name ) }
2012-11-20 23:14:05 -05:00
end
2013-06-26 08:32:09 -04:00
2016-07-11 18:12:31 -04:00
describe '#add_user' do
2013-06-26 08:32:09 -04:00
let ( :user ) { create ( :user ) }
2017-06-14 14:18:56 -04:00
before do
2018-07-11 10:36:08 -04:00
group . add_user ( user , GroupMember :: MAINTAINER )
2017-06-14 14:18:56 -04:00
end
2013-06-26 08:32:09 -04:00
2018-07-11 10:36:08 -04:00
it { expect ( group . group_members . maintainers . map ( & :user ) ) . to include ( user ) }
2013-06-26 08:32:09 -04:00
end
2013-10-14 08:27:27 -04:00
2016-07-11 18:12:31 -04:00
describe '#add_users' do
2013-10-14 08:27:27 -04:00
let ( :user ) { create ( :user ) }
2017-06-14 14:18:56 -04:00
before do
group . add_users ( [ user . id ] , GroupMember :: GUEST )
end
2013-10-14 08:27:27 -04:00
2016-08-01 11:00:44 -04:00
it " updates the group permission " do
2015-02-12 13:17:35 -05:00
expect ( group . group_members . guests . map ( & :user ) ) . to include ( user )
2014-09-14 12:32:51 -04:00
group . add_users ( [ user . id ] , GroupMember :: DEVELOPER )
2015-02-12 13:17:35 -05:00
expect ( group . group_members . developers . map ( & :user ) ) . to include ( user )
expect ( group . group_members . guests . map ( & :user ) ) . not_to include ( user )
2013-10-14 08:27:27 -04:00
end
end
2014-01-27 16:34:05 -05:00
2016-07-11 18:12:31 -04:00
describe '#avatar_type' do
2014-01-27 16:34:05 -05:00
let ( :user ) { create ( :user ) }
2017-06-14 14:18:56 -04:00
before do
2018-07-11 10:36:08 -04:00
group . add_user ( user , GroupMember :: MAINTAINER )
2017-06-14 14:18:56 -04:00
end
2014-01-27 16:34:05 -05:00
2016-08-01 11:00:44 -04:00
it " is true if avatar is image " do
2014-01-27 16:34:05 -05:00
group . update_attribute ( :avatar , 'uploads/avatar.png' )
2015-02-12 13:17:35 -05:00
expect ( group . avatar_type ) . to be_truthy
2014-01-27 16:34:05 -05:00
end
2016-08-01 11:00:44 -04:00
it " is false if avatar is html page " do
2014-01-27 16:34:05 -05:00
group . update_attribute ( :avatar , 'uploads/avatar.html' )
2017-09-27 07:21:58 -04:00
expect ( group . avatar_type ) . to eq ( [ " file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico " ] )
2014-01-27 16:34:05 -05:00
end
end
2016-03-01 10:15:42 -05:00
2017-05-03 11:25:31 -04:00
describe '#avatar_url' do
let! ( :group ) { create ( :group , :access_requestable , :with_avatar ) }
let ( :user ) { create ( :user ) }
context 'when avatar file is uploaded' do
2017-06-14 14:18:56 -04:00
before do
2018-07-11 10:36:08 -04:00
group . add_maintainer ( user )
2017-06-14 14:18:56 -04:00
end
2017-05-03 11:25:31 -04:00
2017-05-10 00:26:17 -04:00
it 'shows correct avatar url' do
2017-11-09 10:40:41 -05:00
expect ( group . avatar_url ) . to eq ( group . avatar . url )
expect ( group . avatar_url ( only_path : false ) ) . to eq ( [ Gitlab . config . gitlab . url , group . avatar . url ] . join )
2017-05-10 00:26:17 -04:00
end
2017-05-03 11:25:31 -04:00
end
end
2016-03-01 10:15:42 -05:00
describe '.search' do
it 'returns groups with a matching name' do
expect ( described_class . search ( group . name ) ) . to eq ( [ group ] )
end
it 'returns groups with a partially matching name' do
expect ( described_class . search ( group . name [ 0 .. 2 ] ) ) . to eq ( [ group ] )
end
it 'returns groups with a matching name regardless of the casing' do
expect ( described_class . search ( group . name . upcase ) ) . to eq ( [ group ] )
end
it 'returns groups with a matching path' do
expect ( described_class . search ( group . path ) ) . to eq ( [ group ] )
end
it 'returns groups with a partially matching path' do
expect ( described_class . search ( group . path [ 0 .. 2 ] ) ) . to eq ( [ group ] )
end
it 'returns groups with a matching path regardless of the casing' do
expect ( described_class . search ( group . path . upcase ) ) . to eq ( [ group ] )
end
end
2016-04-18 12:53:32 -04:00
describe '#has_owner?' do
2017-06-14 14:18:56 -04:00
before do
@members = setup_group_members ( group )
2017-07-24 06:35:54 -04:00
create ( :group_member , :invited , :owner , group : group )
2017-06-14 14:18:56 -04:00
end
2016-04-18 12:53:32 -04:00
it { expect ( group . has_owner? ( @members [ :owner ] ) ) . to be_truthy }
2018-07-11 10:36:08 -04:00
it { expect ( group . has_owner? ( @members [ :maintainer ] ) ) . to be_falsey }
2016-04-18 12:53:32 -04:00
it { expect ( group . has_owner? ( @members [ :developer ] ) ) . to be_falsey }
it { expect ( group . has_owner? ( @members [ :reporter ] ) ) . to be_falsey }
it { expect ( group . has_owner? ( @members [ :guest ] ) ) . to be_falsey }
it { expect ( group . has_owner? ( @members [ :requester ] ) ) . to be_falsey }
2017-07-24 06:35:54 -04:00
it { expect ( group . has_owner? ( nil ) ) . to be_falsey }
2016-04-18 12:53:32 -04:00
end
2018-07-11 10:36:08 -04:00
describe '#has_maintainer?' do
2017-06-14 14:18:56 -04:00
before do
@members = setup_group_members ( group )
2018-07-11 10:36:08 -04:00
create ( :group_member , :invited , :maintainer , group : group )
2017-06-14 14:18:56 -04:00
end
2016-04-18 12:53:32 -04:00
2018-07-11 10:36:08 -04:00
it { expect ( group . has_maintainer? ( @members [ :owner ] ) ) . to be_falsey }
it { expect ( group . has_maintainer? ( @members [ :maintainer ] ) ) . to be_truthy }
it { expect ( group . has_maintainer? ( @members [ :developer ] ) ) . to be_falsey }
it { expect ( group . has_maintainer? ( @members [ :reporter ] ) ) . to be_falsey }
it { expect ( group . has_maintainer? ( @members [ :guest ] ) ) . to be_falsey }
it { expect ( group . has_maintainer? ( @members [ :requester ] ) ) . to be_falsey }
it { expect ( group . has_maintainer? ( nil ) ) . to be_falsey }
2016-04-18 12:53:32 -04:00
end
2016-09-06 12:48:00 -04:00
describe '#lfs_enabled?' do
context 'LFS enabled globally' do
before do
allow ( Gitlab . config . lfs ) . to receive ( :enabled ) . and_return ( true )
end
it 'returns true when nothing is set' do
expect ( group . lfs_enabled? ) . to be_truthy
end
it 'returns false when set to false' do
group . update_attribute ( :lfs_enabled , false )
expect ( group . lfs_enabled? ) . to be_falsey
end
it 'returns true when set to true' do
group . update_attribute ( :lfs_enabled , true )
expect ( group . lfs_enabled? ) . to be_truthy
end
end
context 'LFS disabled globally' do
before do
allow ( Gitlab . config . lfs ) . to receive ( :enabled ) . and_return ( false )
end
it 'returns false when nothing is set' do
expect ( group . lfs_enabled? ) . to be_falsey
end
it 'returns false when set to false' do
group . update_attribute ( :lfs_enabled , false )
expect ( group . lfs_enabled? ) . to be_falsey
end
it 'returns false when set to true' do
group . update_attribute ( :lfs_enabled , true )
expect ( group . lfs_enabled? ) . to be_falsey
end
end
end
2016-06-15 09:22:05 -04:00
describe '#owners' do
let ( :owner ) { create ( :user ) }
let ( :developer ) { create ( :user ) }
it 'returns the owners of a Group' do
group . add_owner ( owner )
group . add_developer ( developer )
expect ( group . owners ) . to eq ( [ owner ] )
end
end
2016-04-18 12:53:32 -04:00
def setup_group_members ( group )
members = {
owner : create ( :user ) ,
2018-07-11 10:36:08 -04:00
maintainer : create ( :user ) ,
2016-04-18 12:53:32 -04:00
developer : create ( :user ) ,
reporter : create ( :user ) ,
guest : create ( :user ) ,
requester : create ( :user )
}
group . add_user ( members [ :owner ] , GroupMember :: OWNER )
2018-07-11 10:36:08 -04:00
group . add_user ( members [ :maintainer ] , GroupMember :: MAINTAINER )
2016-04-18 12:53:32 -04:00
group . add_user ( members [ :developer ] , GroupMember :: DEVELOPER )
group . add_user ( members [ :reporter ] , GroupMember :: REPORTER )
group . add_user ( members [ :guest ] , GroupMember :: GUEST )
group . request_access ( members [ :requester ] )
members
end
2016-10-26 14:13:58 -04:00
describe '#web_url' do
it 'returns the canonical URL' do
expect ( group . web_url ) . to include ( " groups/ #{ group . name } " )
end
2017-01-24 08:40:01 -05:00
context 'nested group' do
let ( :nested_group ) { create ( :group , :nested ) }
it { expect ( nested_group . web_url ) . to include ( " groups/ #{ nested_group . full_path } " ) }
end
2016-10-26 14:13:58 -04:00
end
2016-12-07 12:16:17 -05:00
describe 'nested group' do
2016-12-13 12:46:06 -05:00
subject { build ( :group , :nested ) }
2016-12-07 12:16:17 -05:00
it { is_expected . to be_valid }
2017-07-25 13:09:00 -04:00
it { expect ( subject . parent ) . to be_kind_of ( described_class ) }
2016-12-07 12:16:17 -05:00
end
2016-12-13 13:59:39 -05:00
Use CTEs for nested groups and authorizations
This commit introduces the usage of Common Table Expressions (CTEs) to
efficiently retrieve nested group hierarchies, without having to rely on
the "routes" table (which is an _incredibly_ inefficient way of getting
the data). This requires a patch to ActiveRecord (found in the added
initializer) to work properly as ActiveRecord doesn't support WITH
statements properly out of the box.
Unfortunately MySQL provides no efficient way of getting nested groups.
For example, the old routes setup could easily take 5-10 seconds
depending on the amount of "routes" in a database. Providing vastly
different logic for both MySQL and PostgreSQL will negatively impact the
development process. Because of this the various nested groups related
methods return empty relations when used in combination with MySQL.
For project authorizations the logic is split up into two classes:
* Gitlab::ProjectAuthorizations::WithNestedGroups
* Gitlab::ProjectAuthorizations::WithoutNestedGroups
Both classes get the fresh project authorizations (= as they should be
in the "project_authorizations" table), including nested groups if
PostgreSQL is used. The logic of these two classes is quite different
apart from their public interface. This complicates development a bit,
but unfortunately there is no way around this.
This commit also introduces Gitlab::GroupHierarchy. This class can be
used to get the ancestors and descendants of a base relation, or both by
using a UNION. This in turn is used by methods such as:
* Namespace#ancestors
* Namespace#descendants
* User#all_expanded_groups
Again this class relies on CTEs and thus only works on PostgreSQL. The
Namespace methods will return an empty relation when MySQL is used,
while User#all_expanded_groups will return only the groups a user is a
direct member of.
Performance wise the impact is quite large. For example, on GitLab.com
Namespace#descendants used to take around 580 ms to retrieve data for a
particular user. Using CTEs we are able to reduce this down to roughly 1
millisecond, returning the exact same data.
== On The Fly Refreshing
Refreshing of authorizations on the fly (= when
users.authorized_projects_populated was not set) is removed with this
commit. This simplifies the code, and ensures any queries used for
authorizations are not mutated because they are executed in a Rails
scope (e.g. Project.visible_to_user).
This commit includes a migration to schedule refreshing authorizations
for all users, ensuring all of them have their authorizations in place.
Said migration schedules users in batches of 5000, with 5 minutes
between every batch to smear the load around a bit.
== Spec Changes
This commit also introduces some changes to various specs. For example,
some specs for ProjectTeam assumed that creating a personal project
would _not_ lead to the owner having access, which is incorrect. Because
we also no longer refresh authorizations on the fly for new users some
code had to be added to the "empty_project" factory. This chunk of code
ensures that the owner's permissions are refreshed after creating the
project, something that is normally done in Projects::CreateService.
2017-04-24 11:19:22 -04:00
describe '#members_with_parents' , :nested_groups do
2016-12-13 13:59:39 -05:00
let! ( :group ) { create ( :group , :nested ) }
2018-07-11 10:36:08 -04:00
let! ( :maintainer ) { group . parent . add_user ( create ( :user ) , GroupMember :: MAINTAINER ) }
2016-12-13 13:59:39 -05:00
let! ( :developer ) { group . add_user ( create ( :user ) , GroupMember :: DEVELOPER ) }
it 'returns parents members' do
expect ( group . members_with_parents ) . to include ( developer )
2018-07-11 10:36:08 -04:00
expect ( group . members_with_parents ) . to include ( maintainer )
2016-12-13 13:59:39 -05:00
end
end
2017-02-07 08:55:42 -05:00
2018-04-26 15:53:13 -04:00
describe '#direct_and_indirect_members' , :nested_groups do
let! ( :group ) { create ( :group , :nested ) }
let! ( :sub_group ) { create ( :group , parent : group ) }
2018-07-11 10:36:08 -04:00
let! ( :maintainer ) { group . parent . add_user ( create ( :user ) , GroupMember :: MAINTAINER ) }
2018-04-26 15:53:13 -04:00
let! ( :developer ) { group . add_user ( create ( :user ) , GroupMember :: DEVELOPER ) }
let! ( :other_developer ) { group . add_user ( create ( :user ) , GroupMember :: DEVELOPER ) }
it 'returns parents members' do
expect ( group . direct_and_indirect_members ) . to include ( developer )
2018-07-11 10:36:08 -04:00
expect ( group . direct_and_indirect_members ) . to include ( maintainer )
2018-04-26 15:53:13 -04:00
end
it 'returns descendant members' do
expect ( group . direct_and_indirect_members ) . to include ( other_developer )
end
end
describe '#users_with_descendants' , :nested_groups do
let ( :user_a ) { create ( :user ) }
let ( :user_b ) { create ( :user ) }
let ( :group ) { create ( :group ) }
let ( :nested_group ) { create ( :group , parent : group ) }
let ( :deep_nested_group ) { create ( :group , parent : nested_group ) }
it 'returns member users on every nest level without duplication' do
group . add_developer ( user_a )
nested_group . add_developer ( user_b )
2018-12-06 08:15:29 -05:00
deep_nested_group . add_maintainer ( user_a )
2018-04-26 15:53:13 -04:00
expect ( group . users_with_descendants ) . to contain_exactly ( user_a , user_b )
expect ( nested_group . users_with_descendants ) . to contain_exactly ( user_a , user_b )
expect ( deep_nested_group . users_with_descendants ) . to contain_exactly ( user_a )
end
end
describe '#direct_and_indirect_users' , :nested_groups do
let ( :user_a ) { create ( :user ) }
let ( :user_b ) { create ( :user ) }
let ( :user_c ) { create ( :user ) }
let ( :user_d ) { create ( :user ) }
let ( :group ) { create ( :group ) }
let ( :nested_group ) { create ( :group , parent : group ) }
let ( :deep_nested_group ) { create ( :group , parent : nested_group ) }
let ( :project ) { create ( :project , namespace : group ) }
before do
group . add_developer ( user_a )
group . add_developer ( user_c )
nested_group . add_developer ( user_b )
deep_nested_group . add_developer ( user_a )
project . add_developer ( user_d )
end
it 'returns member users on every nest level without duplication' do
expect ( group . direct_and_indirect_users ) . to contain_exactly ( user_a , user_b , user_c , user_d )
expect ( nested_group . direct_and_indirect_users ) . to contain_exactly ( user_a , user_b , user_c )
expect ( deep_nested_group . direct_and_indirect_users ) . to contain_exactly ( user_a , user_b , user_c )
end
it 'does not return members of projects belonging to ancestor groups' do
expect ( nested_group . direct_and_indirect_users ) . not_to include ( user_d )
end
end
describe '#project_users_with_descendants' , :nested_groups do
let ( :user_a ) { create ( :user ) }
let ( :user_b ) { create ( :user ) }
let ( :user_c ) { create ( :user ) }
let ( :group ) { create ( :group ) }
let ( :nested_group ) { create ( :group , parent : group ) }
let ( :deep_nested_group ) { create ( :group , parent : nested_group ) }
let ( :project_a ) { create ( :project , namespace : group ) }
let ( :project_b ) { create ( :project , namespace : nested_group ) }
let ( :project_c ) { create ( :project , namespace : deep_nested_group ) }
it 'returns members of all projects in group and subgroups' do
project_a . add_developer ( user_a )
project_b . add_developer ( user_b )
project_c . add_developer ( user_c )
expect ( group . project_users_with_descendants ) . to contain_exactly ( user_a , user_b , user_c )
expect ( nested_group . project_users_with_descendants ) . to contain_exactly ( user_b , user_c )
expect ( deep_nested_group . project_users_with_descendants ) . to contain_exactly ( user_c )
end
end
2017-02-07 08:55:42 -05:00
describe '#user_ids_for_project_authorizations' do
it 'returns the user IDs for which to refresh authorizations' do
2018-07-11 10:36:08 -04:00
maintainer = create ( :user )
2017-02-07 08:55:42 -05:00
developer = create ( :user )
2018-07-11 10:36:08 -04:00
group . add_user ( maintainer , GroupMember :: MAINTAINER )
2017-02-07 08:55:42 -05:00
group . add_user ( developer , GroupMember :: DEVELOPER )
2017-06-21 09:48:12 -04:00
expect ( group . user_ids_for_project_authorizations )
2018-07-11 10:36:08 -04:00
. to include ( maintainer . id , developer . id )
2017-02-07 08:55:42 -05:00
end
end
2017-01-24 16:09:58 -05:00
describe '#update_two_factor_requirement' do
let ( :user ) { create ( :user ) }
before do
group . add_user ( user , GroupMember :: OWNER )
end
it 'is called when require_two_factor_authentication is changed' do
expect_any_instance_of ( User ) . to receive ( :update_two_factor_requirement )
group . update! ( require_two_factor_authentication : true )
end
it 'is called when two_factor_grace_period is changed' do
expect_any_instance_of ( User ) . to receive ( :update_two_factor_requirement )
group . update! ( two_factor_grace_period : 23 )
end
it 'is not called when other attributes are changed' do
expect_any_instance_of ( User ) . not_to receive ( :update_two_factor_requirement )
group . update! ( description : 'foobar' )
end
it 'calls #update_two_factor_requirement on each group member' do
other_user = create ( :user )
group . add_user ( other_user , GroupMember :: OWNER )
calls = 0
allow_any_instance_of ( User ) . to receive ( :update_two_factor_requirement ) do
calls += 1
end
group . update! ( require_two_factor_authentication : true , two_factor_grace_period : 23 )
expect ( calls ) . to eq 2
end
end
2017-05-03 14:51:55 -04:00
2017-11-03 07:26:52 -04:00
describe '#path_changed_hook' do
let ( :system_hook_service ) { SystemHooksService . new }
context 'for a new group' do
let ( :group ) { build ( :group ) }
before do
expect ( group ) . to receive ( :system_hook_service ) . and_return ( system_hook_service )
end
it 'does not trigger system hook' do
expect ( system_hook_service ) . to receive ( :execute_hooks_for ) . with ( group , :create )
group . save!
end
end
context 'for an existing group' do
let ( :group ) { create ( :group , path : 'old-path' ) }
context 'when the path is changed' do
let ( :new_path ) { 'very-new-path' }
it 'triggers the rename system hook' do
expect ( group ) . to receive ( :system_hook_service ) . and_return ( system_hook_service )
expect ( system_hook_service ) . to receive ( :execute_hooks_for ) . with ( group , :rename )
2018-07-02 06:43:06 -04:00
group . update! ( path : new_path )
2017-11-03 07:26:52 -04:00
end
end
context 'when the path is not changed' do
it 'does not trigger system hook' do
expect ( group ) . not_to receive ( :system_hook_service )
2018-07-02 06:43:06 -04:00
group . update! ( name : 'new name' )
2017-11-03 07:26:52 -04:00
end
end
end
end
2018-10-30 04:49:26 -04:00
describe '#ci_variables_for' do
2017-08-02 15:55:11 -04:00
let ( :project ) { create ( :project , group : group ) }
2017-05-03 14:51:55 -04:00
2018-10-30 04:49:26 -04:00
let! ( :ci_variable ) do
2017-05-03 14:51:55 -04:00
create ( :ci_group_variable , value : 'secret' , group : group )
end
let! ( :protected_variable ) do
create ( :ci_group_variable , :protected , value : 'protected' , group : group )
end
2018-10-30 04:49:26 -04:00
subject { group . ci_variables_for ( 'ref' , project ) }
2017-05-03 14:51:55 -04:00
shared_examples 'ref is protected' do
it 'contains all the variables' do
2018-10-30 04:49:26 -04:00
is_expected . to contain_exactly ( ci_variable , protected_variable )
2017-05-03 14:51:55 -04:00
end
end
context 'when the ref is not protected' do
before do
stub_application_setting (
default_branch_protection : Gitlab :: Access :: PROTECTION_NONE )
end
2018-10-30 04:49:26 -04:00
it 'contains only the CI variables' do
is_expected . to contain_exactly ( ci_variable )
2017-05-03 14:51:55 -04:00
end
end
context 'when the ref is a protected branch' do
before do
2018-01-26 12:05:04 -05:00
allow ( project ) . to receive ( :protected_for? ) . with ( 'ref' ) . and_return ( true )
2017-05-03 14:51:55 -04:00
end
it_behaves_like 'ref is protected'
end
context 'when the ref is a protected tag' do
before do
2018-01-26 12:05:04 -05:00
allow ( project ) . to receive ( :protected_for? ) . with ( 'ref' ) . and_return ( true )
2017-05-03 14:51:55 -04:00
end
it_behaves_like 'ref is protected'
end
2017-07-07 14:34:04 -04:00
context 'when group has children' , :postgresql do
let ( :group_child ) { create ( :group , parent : group ) }
let ( :group_child_2 ) { create ( :group , parent : group_child ) }
let ( :group_child_3 ) { create ( :group , parent : group_child_2 ) }
let ( :variable_child ) { create ( :ci_group_variable , group : group_child ) }
let ( :variable_child_2 ) { create ( :ci_group_variable , group : group_child_2 ) }
let ( :variable_child_3 ) { create ( :ci_group_variable , group : group_child_3 ) }
2017-05-03 14:51:55 -04:00
2018-01-26 12:05:04 -05:00
before do
allow ( project ) . to receive ( :protected_for? ) . with ( 'ref' ) . and_return ( true )
end
2017-05-03 14:51:55 -04:00
it 'returns all variables belong to the group and parent groups' do
2018-10-30 04:49:26 -04:00
expected_array1 = [ protected_variable , ci_variable ]
2017-07-05 04:55:48 -04:00
expected_array2 = [ variable_child , variable_child_2 , variable_child_3 ]
2018-10-30 04:49:26 -04:00
got_array = group_child_3 . ci_variables_for ( 'ref' , project ) . to_a
2017-07-05 04:55:48 -04:00
expect ( got_array . shift ( 2 ) ) . to contain_exactly ( * expected_array1 )
expect ( got_array ) . to eq ( expected_array2 )
2017-05-03 14:51:55 -04:00
end
end
end
2018-02-05 19:10:58 -05:00
2019-01-15 12:57:17 -05:00
describe '#highest_group_member' , :nested_groups do
let ( :nested_group ) { create ( :group , parent : group ) }
let ( :nested_group_2 ) { create ( :group , parent : nested_group ) }
let ( :user ) { create ( :user ) }
subject ( :highest_group_member ) { nested_group_2 . highest_group_member ( user ) }
context 'when the user is not a member of any group in the hierarchy' do
it 'returns nil' do
expect ( highest_group_member ) . to be_nil
end
end
context 'when the user is only a member of one group in the hierarchy' do
before do
nested_group . add_developer ( user )
end
it 'returns that group member' do
expect ( highest_group_member . access_level ) . to eq ( Gitlab :: Access :: DEVELOPER )
end
end
context 'when the user is a member of several groups in the hierarchy' do
before do
group . add_owner ( user )
nested_group . add_developer ( user )
nested_group_2 . add_maintainer ( user )
end
it 'returns the group member with the highest access level' do
expect ( highest_group_member . access_level ) . to eq ( Gitlab :: Access :: OWNER )
end
end
end
2018-02-05 19:10:58 -05:00
describe '#has_parent?' do
context 'when the group has a parent' do
it 'should be truthy' do
group = create ( :group , :nested )
expect ( group . has_parent? ) . to be_truthy
end
end
context 'when the group has no parent' do
it 'should be falsy' do
group = create ( :group , parent : nil )
expect ( group . has_parent? ) . to be_falsy
end
end
end
2018-05-02 12:21:42 -04:00
context 'with uploads' do
2018-10-07 14:31:08 -04:00
it_behaves_like 'model with uploads' , true do
2018-05-02 12:21:42 -04:00
let ( :model_object ) { create ( :group , :with_avatar ) }
let ( :upload_attribute ) { :avatar }
let ( :uploader_class ) { AttachmentUploader }
end
end
2018-11-28 19:50:19 -05:00
describe '#group_clusters_enabled?' do
before do
# Override global stub in spec/spec_helper.rb
expect ( Feature ) . to receive ( :enabled? ) . and_call_original
end
subject { group . group_clusters_enabled? }
it { is_expected . to be_truthy }
context 'explicitly disabled for root ancestor' do
before do
feature = Feature . get ( :group_clusters )
feature . disable ( group . root_ancestor )
end
it { is_expected . to be_falsey }
end
context 'explicitly disabled for root ancestor' do
before do
feature = Feature . get ( :group_clusters )
feature . enable ( group . root_ancestor )
end
it { is_expected . to be_truthy }
end
end
2019-03-12 06:15:33 -04:00
describe '#first_auto_devops_config' do
using RSpec :: Parameterized :: TableSyntax
let ( :group ) { create ( :group ) }
subject { group . first_auto_devops_config }
where ( :instance_value , :group_value , :config ) do
# Instance level enabled
true | nil | { status : true , scope : :instance }
true | true | { status : true , scope : :group }
true | false | { status : false , scope : :group }
# Instance level disabled
false | nil | { status : false , scope : :instance }
false | true | { status : true , scope : :group }
false | false | { status : false , scope : :group }
end
with_them do
before do
stub_application_setting ( auto_devops_enabled : instance_value )
group . update_attribute ( :auto_devops_enabled , group_value )
end
it { is_expected . to eq ( config ) }
end
context 'with parent groups' , :nested_groups do
where ( :instance_value , :parent_value , :group_value , :config ) do
# Instance level enabled
true | nil | nil | { status : true , scope : :instance }
true | nil | true | { status : true , scope : :group }
true | nil | false | { status : false , scope : :group }
true | true | nil | { status : true , scope : :group }
true | true | true | { status : true , scope : :group }
true | true | false | { status : false , scope : :group }
true | false | nil | { status : false , scope : :group }
true | false | true | { status : true , scope : :group }
true | false | false | { status : false , scope : :group }
# Instance level disable
false | nil | nil | { status : false , scope : :instance }
false | nil | true | { status : true , scope : :group }
false | nil | false | { status : false , scope : :group }
false | true | nil | { status : true , scope : :group }
false | true | true | { status : true , scope : :group }
false | true | false | { status : false , scope : :group }
false | false | nil | { status : false , scope : :group }
false | false | true | { status : true , scope : :group }
false | false | false | { status : false , scope : :group }
end
with_them do
before do
stub_application_setting ( auto_devops_enabled : instance_value )
parent = create ( :group , auto_devops_enabled : parent_value )
group . update! (
auto_devops_enabled : group_value ,
parent : parent
)
end
it { is_expected . to eq ( config ) }
end
end
end
describe '#auto_devops_enabled?' do
subject { group . auto_devops_enabled? }
context 'when auto devops is explicitly enabled on group' do
let ( :group ) { create ( :group , :auto_devops_enabled ) }
it { is_expected . to be_truthy }
end
context 'when auto devops is explicitly disabled on group' do
let ( :group ) { create ( :group , :auto_devops_disabled ) }
it { is_expected . to be_falsy }
end
context 'when auto devops is implicitly enabled or disabled' do
before do
stub_application_setting ( auto_devops_enabled : false )
group . update! ( parent : parent_group )
end
context 'when auto devops is enabled on root group' do
let ( :root_group ) { create ( :group , :auto_devops_enabled ) }
let ( :subgroup ) { create ( :group , parent : root_group ) }
let ( :parent_group ) { create ( :group , parent : subgroup ) }
it { is_expected . to be_truthy }
end
context 'when auto devops is disabled on root group' do
let ( :root_group ) { create ( :group , :auto_devops_disabled ) }
let ( :subgroup ) { create ( :group , parent : root_group ) }
let ( :parent_group ) { create ( :group , parent : subgroup ) }
it { is_expected . to be_falsy }
end
context 'when auto devops is disabled on parent group and enabled on root group' do
let ( :root_group ) { create ( :group , :auto_devops_enabled ) }
let ( :parent_group ) { create ( :group , :auto_devops_disabled , parent : root_group ) }
it { is_expected . to be_falsy }
end
end
end
2012-10-02 11:17:12 -04:00
end