group_spec.rb 32.3 KB
Newer Older
1 2
# frozen_string_literal: true

3 4
require 'spec_helper'

5
describe Group do
6
  let!(:group) { create(:group, :access_requestable) }
7

8
  describe 'associations' do
9
    it { is_expected.to have_many :projects }
R
Rémy Coutable 已提交
10 11
    it { is_expected.to have_many(:group_members).dependent(:destroy) }
    it { is_expected.to have_many(:users).through(:group_members) }
12 13
    it { is_expected.to have_many(:owners).through(:group_members) }
    it { is_expected.to have_many(:requesters).dependent(:destroy) }
14
    it { is_expected.to have_many(:members_and_requesters) }
R
Rémy Coutable 已提交
15 16 17
    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) }
D
Douglas Barbosa Alexandre 已提交
18
    it { is_expected.to have_many(:labels).class_name('GroupLabel') }
S
Shinya Maeda 已提交
19
    it { is_expected.to have_many(:variables).class_name('Ci::GroupVariable') }
J
Jan Provaznik 已提交
20
    it { is_expected.to have_many(:uploads) }
21
    it { is_expected.to have_one(:chat_team) }
22
    it { is_expected.to have_many(:custom_attributes).class_name('GroupCustomAttribute') }
23
    it { is_expected.to have_many(:badges).class_name('GroupBadge') }
T
Thong Kuah 已提交
24 25
    it { is_expected.to have_many(:cluster_groups).class_name('Clusters::Group') }
    it { is_expected.to have_many(:clusters).class_name('Clusters::Cluster') }
26 27 28 29 30 31 32 33 34

    describe '#members & #requesters' do
      let(:requester) { create(:user) }
      let(:developer) { create(:user) }
      before do
        group.request_access(requester)
        group.add_developer(developer)
      end

35 36
      it_behaves_like 'members and requesters associations' do
        let(:namespace) { group }
37 38
      end
    end
D
Dmitriy Zaporozhets 已提交
39 40
  end

41 42 43 44 45 46 47 48 49 50
  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 }
51 52
    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) }
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

    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
73

74 75 76 77 78 79 80
    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)
81
        sub_group.add_maintainer(user)
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
      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

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    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
130 131 132 133 134 135 136 137 138 139

    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
140
          expect(internal_group.errors[:visibility_level]).to include('private is not allowed since this group contains projects with higher visibility.')
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
        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
162
          expect(internal_group.errors[:visibility_level]).to include('private is not allowed since there are sub-groups with higher visibility.')
163 164 165 166 167 168 169 170 171 172 173
        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
174 175
  end

176 177 178
  describe '.public_or_visible_to_user' do
    let!(:private_group)  { create(:group, :private)  }
    let!(:internal_group) { create(:group, :internal) }
179

180
    subject { described_class.public_or_visible_to_user(user) }
181

182 183
    context 'when user is nil' do
      let!(:user) { nil }
184

185
      it { is_expected.to match_array([group]) }
186 187
    end

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    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
212 213 214
    end
  end

F
Felipe Artur 已提交
215
  describe 'scopes' do
216 217
    let!(:private_group)  { create(:group, :private)  }
    let!(:internal_group) { create(:group, :internal) }
F
Felipe Artur 已提交
218 219

    describe 'public_only' do
220
      subject { described_class.public_only.to_a }
F
Felipe Artur 已提交
221

222
      it { is_expected.to eq([group]) }
F
Felipe Artur 已提交
223 224 225
    end

    describe 'public_and_internal_only' do
D
Douwe Maan 已提交
226
      subject { described_class.public_and_internal_only.to_a }
F
Felipe Artur 已提交
227

228 229 230 231 232 233 234
      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]) }
F
Felipe Artur 已提交
235 236 237
    end
  end

238 239 240 241 242
  describe '#to_reference' do
    it 'returns a String reference to the object' do
      expect(group.to_reference).to eq "@#{group.name}"
    end
  end
D
Dmitriy Zaporozhets 已提交
243

244
  describe '#users' do
245
    it { expect(group.users).to eq(group.owners) }
D
Dmitriy Zaporozhets 已提交
246 247
  end

248
  describe '#human_name' do
249
    it { expect(group.human_name).to eq(group.name) }
D
Dmitriy Zaporozhets 已提交
250
  end
D
Dmitriy Zaporozhets 已提交
251

252
  describe '#add_user' do
D
Dmitriy Zaporozhets 已提交
253
    let(:user) { create(:user) }
254 255

    before do
256
      group.add_user(user, GroupMember::MAINTAINER)
257
    end
D
Dmitriy Zaporozhets 已提交
258

259
    it { expect(group.group_members.maintainers.map(&:user)).to include(user) }
D
Dmitriy Zaporozhets 已提交
260
  end
261

262
  describe '#add_users' do
263
    let(:user) { create(:user) }
264 265 266 267

    before do
      group.add_users([user.id], GroupMember::GUEST)
    end
268

269
    it "updates the group permission" do
270
      expect(group.group_members.guests.map(&:user)).to include(user)
271
      group.add_users([user.id], GroupMember::DEVELOPER)
272 273
      expect(group.group_members.developers.map(&:user)).to include(user)
      expect(group.group_members.guests.map(&:user)).not_to include(user)
274 275
    end
  end
S
Steven Thonus 已提交
276

277
  describe '#avatar_type' do
S
Steven Thonus 已提交
278
    let(:user) { create(:user) }
279 280

    before do
281
      group.add_user(user, GroupMember::MAINTAINER)
282
    end
S
Steven Thonus 已提交
283

284
    it "is true if avatar is image" do
S
Steven Thonus 已提交
285
      group.update_attribute(:avatar, 'uploads/avatar.png')
286
      expect(group.avatar_type).to be_truthy
S
Steven Thonus 已提交
287 288
    end

289
    it "is false if avatar is html page" do
S
Steven Thonus 已提交
290
      group.update_attribute(:avatar, 'uploads/avatar.html')
291
      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"])
S
Steven Thonus 已提交
292 293
    end
  end
294

295 296 297 298 299
  describe '#avatar_url' do
    let!(:group) { create(:group, :access_requestable, :with_avatar) }
    let(:user) { create(:user) }

    context 'when avatar file is uploaded' do
300
      before do
301
        group.add_maintainer(user)
302
      end
303

304
      it 'shows correct avatar url' do
305 306
        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)
307
      end
308 309 310
    end
  end

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
  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
R
Rémy Coutable 已提交
336 337

  describe '#has_owner?' do
338 339
    before do
      @members = setup_group_members(group)
340
      create(:group_member, :invited, :owner, group: group)
341
    end
R
Rémy Coutable 已提交
342 343

    it { expect(group.has_owner?(@members[:owner])).to be_truthy }
344
    it { expect(group.has_owner?(@members[:maintainer])).to be_falsey }
R
Rémy Coutable 已提交
345 346 347 348
    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 }
349
    it { expect(group.has_owner?(nil)).to be_falsey }
R
Rémy Coutable 已提交
350 351
  end

352
  describe '#has_maintainer?' do
353 354
    before do
      @members = setup_group_members(group)
355
      create(:group_member, :invited, :maintainer, group: group)
356
    end
R
Rémy Coutable 已提交
357

358 359 360 361 362 363 364
    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 }
R
Rémy Coutable 已提交
365 366
  end

G
Gosia Ksionek 已提交
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
  describe '#last_owner?' do
    before do
      @members = setup_group_members(group)
    end

    it { expect(group.last_owner?(@members[:owner])).to be_truthy }

    context 'with two owners' do
      before do
        create(:group_member, :owner, group: group)
      end

      it { expect(group.last_owner?(@members[:owner])).to be_falsy }
    end

    context 'with owners from a parent', :postgresql do
      before do
        parent_group = create(:group)
        create(:group_member, :owner, group: parent_group)
        group.update(parent: parent_group)
      end

      it { expect(group.last_owner?(@members[:owner])).to be_falsy }
    end
  end

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
  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

439 440 441 442 443 444 445 446 447 448 449 450
  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

R
Rémy Coutable 已提交
451 452 453
  def setup_group_members(group)
    members = {
      owner: create(:user),
454
      maintainer: create(:user),
R
Rémy Coutable 已提交
455 456 457 458 459 460 461
      developer: create(:user),
      reporter: create(:user),
      guest: create(:user),
      requester: create(:user)
    }

    group.add_user(members[:owner], GroupMember::OWNER)
462
    group.add_user(members[:maintainer], GroupMember::MAINTAINER)
R
Rémy Coutable 已提交
463 464 465 466 467 468 469
    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
470 471 472 473 474

  describe '#web_url' do
    it 'returns the canonical URL' do
      expect(group.web_url).to include("groups/#{group.name}")
    end
475 476 477 478 479 480

    context 'nested group' do
      let(:nested_group) { create(:group, :nested) }

      it { expect(nested_group.web_url).to include("groups/#{nested_group.full_path}") }
    end
481
  end
D
Dmitriy Zaporozhets 已提交
482 483

  describe 'nested group' do
484
    subject { build(:group, :nested) }
D
Dmitriy Zaporozhets 已提交
485 486

    it { is_expected.to be_valid }
487
    it { expect(subject.parent).to be_kind_of(described_class) }
D
Dmitriy Zaporozhets 已提交
488
  end
489

490
  describe '#members_with_parents', :nested_groups do
491
    let!(:group) { create(:group, :nested) }
492
    let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
493 494 495 496
    let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }

    it 'returns parents members' do
      expect(group.members_with_parents).to include(developer)
497
      expect(group.members_with_parents).to include(maintainer)
498 499
    end
  end
500

501 502 503
  describe '#direct_and_indirect_members', :nested_groups do
    let!(:group) { create(:group, :nested) }
    let!(:sub_group) { create(:group, parent: group) }
504
    let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
505 506 507 508 509
    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)
510
      expect(group.direct_and_indirect_members).to include(maintainer)
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
    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)
529
      deep_nested_group.add_maintainer(user_a)
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589

      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

590 591
  describe '#user_ids_for_project_authorizations' do
    it 'returns the user IDs for which to refresh authorizations' do
592
      maintainer = create(:user)
593 594
      developer = create(:user)

595
      group.add_user(maintainer, GroupMember::MAINTAINER)
596 597
      group.add_user(developer, GroupMember::DEVELOPER)

598
      expect(group.user_ids_for_project_authorizations)
599
        .to include(maintainer.id, developer.id)
600 601
    end
  end
602 603 604 605

  describe '#update_two_factor_requirement' do
    let(:user) { create(:user) }

606 607 608 609
    context 'group membership' do
      before do
        group.add_user(user, GroupMember::OWNER)
      end
610

611 612
      it 'is called when require_two_factor_authentication is changed' do
        expect_any_instance_of(User).to receive(:update_two_factor_requirement)
613

614 615
        group.update!(require_two_factor_authentication: true)
      end
616

617 618
      it 'is called when two_factor_grace_period is changed' do
        expect_any_instance_of(User).to receive(:update_two_factor_requirement)
619

620 621
        group.update!(two_factor_grace_period: 23)
      end
622

623 624
      it 'is not called when other attributes are changed' do
        expect_any_instance_of(User).not_to receive(:update_two_factor_requirement)
625

626
        group.update!(description: 'foobar')
627 628
      end

629 630 631 632 633 634 635 636 637 638
      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)
639

640 641
        expect(calls).to eq 2
      end
642
    end
643

644 645 646
    context 'sub groups and projects', :nested_groups do
      it 'enables two_factor_requirement for group member' do
        group.add_user(user, GroupMember::OWNER)
647

648
        group.update!(require_two_factor_authentication: true)
649

650 651
        expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
      end
652

653 654
      context 'expanded group members', :nested_groups do
        let(:indirect_user) { create(:user) }
655

656 657 658
        it 'enables two_factor_requirement for subgroup member' do
          subgroup = create(:group, :nested, parent: group)
          subgroup.add_user(indirect_user, GroupMember::OWNER)
659

660 661 662 663 664
          group.update!(require_two_factor_authentication: true)

          expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
        end

665
        it 'does not enable two_factor_requirement for ancestor group member' do
666 667 668 669 670 671
          ancestor_group = create(:group)
          ancestor_group.add_user(indirect_user, GroupMember::OWNER)
          group.update!(parent: ancestor_group)

          group.update!(require_two_factor_authentication: true)

672
          expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_falsey
673 674
        end
      end
675

676 677 678 679
      context 'project members' do
        it 'does not enable two_factor_requirement for child project member' do
          project = create(:project, group: group)
          project.add_maintainer(user)
680

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
          group.update!(require_two_factor_authentication: true)

          expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
        end

        it 'does not enable two_factor_requirement for subgroup child project member', :nested_groups do
          subgroup = create(:group, :nested, parent: group)
          project = create(:project, group: subgroup)
          project.add_maintainer(user)

          group.update!(require_two_factor_authentication: true)

          expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
        end
      end
696
    end
697
  end
S
Shinya Maeda 已提交
698

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
  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)

L
Lin Jen-Shin 已提交
726
          group.update!(path: new_path)
727 728 729 730 731 732 733
        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)

L
Lin Jen-Shin 已提交
734
          group.update!(name: 'new name')
735 736 737 738 739
        end
      end
    end
  end

740
  describe '#ci_variables_for' do
741
    let(:project) { create(:project, group: group) }
S
Shinya Maeda 已提交
742

743
    let!(:ci_variable) do
S
Shinya Maeda 已提交
744 745 746 747 748 749 750
      create(:ci_group_variable, value: 'secret', group: group)
    end

    let!(:protected_variable) do
      create(:ci_group_variable, :protected, value: 'protected', group: group)
    end

751
    subject { group.ci_variables_for('ref', project) }
S
Shinya Maeda 已提交
752 753 754

    shared_examples 'ref is protected' do
      it 'contains all the variables' do
755
        is_expected.to contain_exactly(ci_variable, protected_variable)
S
Shinya Maeda 已提交
756 757 758 759 760 761 762 763 764
      end
    end

    context 'when the ref is not protected' do
      before do
        stub_application_setting(
          default_branch_protection: Gitlab::Access::PROTECTION_NONE)
      end

765 766
      it 'contains only the CI variables' do
        is_expected.to contain_exactly(ci_variable)
S
Shinya Maeda 已提交
767 768 769 770 771
      end
    end

    context 'when the ref is a protected branch' do
      before do
772
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
S
Shinya Maeda 已提交
773 774 775 776 777 778 779
      end

      it_behaves_like 'ref is protected'
    end

    context 'when the ref is a protected tag' do
      before do
780
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
S
Shinya Maeda 已提交
781 782 783 784 785
      end

      it_behaves_like 'ref is protected'
    end

786 787 788 789 790 791 792
    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) }
S
Shinya Maeda 已提交
793

794 795 796 797
      before do
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
      end

S
Shinya Maeda 已提交
798
      it 'returns all variables belong to the group and parent groups' do
799
        expected_array1 = [protected_variable, ci_variable]
800
        expected_array2 = [variable_child, variable_child_2, variable_child_3]
801
        got_array = group_child_3.ci_variables_for('ref', project).to_a
802 803 804

        expect(got_array.shift(2)).to contain_exactly(*expected_array1)
        expect(got_array).to eq(expected_array2)
S
Shinya Maeda 已提交
805 806 807
      end
    end
  end
808

809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
  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

845 846
  describe '#has_parent?' do
    context 'when the group has a parent' do
847
      it 'is truthy' do
848 849 850 851 852 853
        group = create(:group, :nested)
        expect(group.has_parent?).to be_truthy
      end
    end

    context 'when the group has no parent' do
854
      it 'is falsy' do
855 856 857 858 859
        group = create(:group, parent: nil)
        expect(group.has_parent?).to be_falsy
      end
    end
  end
J
Jan Provaznik 已提交
860 861

  context 'with uploads' do
862
    it_behaves_like 'model with uploads', true do
J
Jan Provaznik 已提交
863 864 865 866 867
      let(:model_object) { create(:group, :with_avatar) }
      let(:upload_attribute) { :avatar }
      let(:uploader_class) { AttachmentUploader }
    end
  end
868

869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
  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
G
Gosia Ksionek 已提交
989 990 991 992 993 994 995 996

  describe 'project_creation_level' do
    it 'outputs the default one if it is nil' do
      group = create(:group, project_creation_level: nil)

      expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation)
    end
  end
997 998 999

  describe 'subgroup_creation_level' do
    it 'defaults to maintainers' do
1000 1001
      expect(group.subgroup_creation_level)
        .to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS)
1002 1003
    end
  end
1004
end