namespace_spec.rb 28 KB
Newer Older
1 2
require 'spec_helper'

3
describe Namespace do
4 5
  include ProjectForksHelper

6
  let!(:namespace) { create(:namespace) }
7
  let(:gitlab_shell) { Gitlab::Shell.new }
8
  let(:repository_storage) { 'default' }
9

10 11 12 13 14 15
  describe 'associations' do
    it { is_expected.to have_many :projects }
    it { is_expected.to have_many :project_statistics }
    it { is_expected.to belong_to :parent }
    it { is_expected.to have_many :children }
  end
16

17 18 19 20 21 22 23
  describe 'validations' do
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_length_of(:name).is_at_most(255) }
    it { is_expected.to validate_length_of(:description).is_at_most(255) }
    it { is_expected.to validate_presence_of(:path) }
    it { is_expected.to validate_length_of(:path).is_at_most(255) }
    it { is_expected.to validate_presence_of(:owner) }
24

25 26 27
    it 'does not allow too deep nesting' do
      ancestors = (1..21).to_a
      nested = build(:namespace, parent: namespace)
28

29
      allow(nested).to receive(:ancestors).and_return(ancestors)
30

31 32 33
      expect(nested).not_to be_valid
      expect(nested.errors[:parent_id].first).to eq('has too deep level of nesting')
    end
34 35 36 37 38 39

    describe 'reserved path validation' do
      context 'nested group' do
        let(:group) { build(:group, :nested, path: 'tree') }

        it { expect(group).not_to be_valid }
40 41 42

        it 'rejects nested paths' do
          parent = create(:group, :nested, path: 'environments')
43
          namespace = build(:group, path: 'folders', parent: parent)
44 45 46

          expect(namespace).not_to be_valid
        end
47 48
      end

49
      context "is case insensitive" do
50
        let(:group) { build(:group, path: "Groups") }
51 52 53 54

        it { expect(group).not_to be_valid }
      end

55 56 57 58 59 60
      context 'top-level group' do
        let(:group) { build(:group, path: 'tree') }

        it { expect(group).to be_valid }
      end
    end
61
  end
62 63

  describe "Respond to" do
64 65
    it { is_expected.to respond_to(:human_name) }
    it { is_expected.to respond_to(:to_param) }
66
    it { is_expected.to respond_to(:has_parent?) }
67
  end
68

69 70 71 72 73 74 75 76
  describe 'inclusions' do
    it { is_expected.to include_module(Gitlab::VisibilityLevel) }
  end

  describe '#visibility_level_field' do
    it { expect(namespace.visibility_level_field).to eq(:visibility_level) }
  end

77
  describe '#to_param' do
78
    it { expect(namespace.to_param).to eq(namespace.full_path) }
79 80
  end

81
  describe '#human_name' do
82
    it { expect(namespace.human_name).to eq(namespace.owner_name) }
83 84
  end

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
  describe '#first_project_with_container_registry_tags' do
    let(:container_repository) { create(:container_repository) }
    let!(:project) { create(:project, namespace: namespace, container_repositories: [container_repository]) }

    before do
      stub_container_registry_config(enabled: true)
    end

    it 'returns the project' do
      stub_container_registry_tags(repository: :any, tags: ['tag'])

      expect(namespace.first_project_with_container_registry_tags).to eq(project)
    end

    it 'returns no project' do
      stub_container_registry_tags(repository: :any, tags: nil)

      expect(namespace.first_project_with_container_registry_tags).to be_nil
    end
  end

106
  describe '.search' do
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    let(:namespace) { create(:namespace) }

    it 'returns namespaces with a matching name' do
      expect(described_class.search(namespace.name)).to eq([namespace])
    end

    it 'returns namespaces with a partially matching name' do
      expect(described_class.search(namespace.name[0..2])).to eq([namespace])
    end

    it 'returns namespaces with a matching name regardless of the casing' do
      expect(described_class.search(namespace.name.upcase)).to eq([namespace])
    end

    it 'returns namespaces with a matching path' do
      expect(described_class.search(namespace.path)).to eq([namespace])
123 124
    end

125 126 127 128 129 130 131
    it 'returns namespaces with a partially matching path' do
      expect(described_class.search(namespace.path[0..2])).to eq([namespace])
    end

    it 'returns namespaces with a matching path regardless of the casing' do
      expect(described_class.search(namespace.path.upcase)).to eq([namespace])
    end
132 133
  end

134 135 136 137
  describe '.with_statistics' do
    let(:namespace) { create :namespace }

    let(:project1) do
138
      create(:project,
139 140 141 142 143 144 145 146 147
             namespace: namespace,
             statistics: build(:project_statistics,
                               storage_size:         606,
                               repository_size:      101,
                               lfs_objects_size:     202,
                               build_artifacts_size: 303))
    end

    let(:project2) do
148
      create(:project,
149 150 151 152 153 154 155 156 157 158 159
             namespace: namespace,
             statistics: build(:project_statistics,
                               storage_size:         60,
                               repository_size:      10,
                               lfs_objects_size:     20,
                               build_artifacts_size: 30))
    end

    it "sums all project storage counters in the namespace" do
      project1
      project2
160
      statistics = described_class.with_statistics.find(namespace.id)
161 162 163 164 165 166 167 168

      expect(statistics.storage_size).to eq 666
      expect(statistics.repository_size).to eq 111
      expect(statistics.lfs_objects_size).to eq 222
      expect(statistics.build_artifacts_size).to eq 333
    end

    it "correctly handles namespaces without projects" do
169
      statistics = described_class.with_statistics.find(namespace.id)
170 171 172 173 174 175 176 177

      expect(statistics.storage_size).to eq 0
      expect(statistics.repository_size).to eq 0
      expect(statistics.lfs_objects_size).to eq 0
      expect(statistics.build_artifacts_size).to eq 0
    end
  end

178 179 180 181 182 183 184 185 186 187 188 189 190 191
  describe '#ancestors_upto', :nested_groups do
    let(:parent) { create(:group) }
    let(:child) { create(:group, parent: parent) }
    let(:child2) { create(:group, parent: child) }

    it 'returns all ancestors when no namespace is given' do
      expect(child2.ancestors_upto).to contain_exactly(child, parent)
    end

    it 'includes ancestors upto but excluding the given ancestor' do
      expect(child2.ancestors_upto(parent)).to contain_exactly(child)
    end
  end

192
  describe '#move_dir', :request_store do
193 194 195
    shared_examples "namespace restrictions" do
      context "when any project has container images" do
        let(:container_repository) { create(:container_repository) }
196

197 198 199
        before do
          stub_container_registry_config(enabled: true)
          stub_container_registry_tags(repository: :any, tags: ['tag'])
200

201
          create(:project, namespace: namespace, container_repositories: [container_repository])
202

203 204 205
          allow(namespace).to receive(:path_was).and_return(namespace.path)
          allow(namespace).to receive(:path).and_return('new_path')
        end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
206

207
        it 'raises an error about not movable project' do
208 209
          expect { namespace.move_dir }.to raise_error(Gitlab::UpdatePathError,
                                                       /Namespace .* cannot be moved/)
210 211 212
        end
      end
    end
213

214 215 216
    context 'legacy storage' do
      let(:namespace) { create(:namespace) }
      let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: namespace) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
217

218
      it_behaves_like 'namespace restrictions'
Kamil Trzcinski's avatar
Kamil Trzcinski committed
219

220 221
      it "raises error when directory exists" do
        expect { namespace.move_dir }.to raise_error("namespace directory cannot be moved")
Kamil Trzcinski's avatar
Kamil Trzcinski committed
222 223
      end

224
      it "moves dir if path changed" do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
225
        namespace.update(path: namespace.full_path + '_new')
226

227
        expect(gitlab_shell.exists?(project.repository_storage, "#{namespace.path}/#{project.path}.git")).to be_truthy
228
      end
229

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
      context 'when #write_projects_repository_config raises an error' do
        context 'in test environment' do
          it 'raises an exception' do
            expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')

            expect do
              namespace.update(path: namespace.full_path + '_new')
            end.to raise_error('foo')
          end
        end

        context 'in production environment' do
          it 'does not cancel later callbacks' do
            expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
            expect(namespace).to receive(:move_dir).and_wrap_original do |m, *args|
              move_dir_result = m.call(*args)

              expect(move_dir_result).to be_truthy # Must be truthy, or else later callbacks would be canceled

              move_dir_result
            end
            expect(Gitlab::Sentry).to receive(:should_raise?).and_return(false) # like prod

            namespace.update(path: namespace.full_path + '_new')
          end
        end
      end

258
      context 'with subgroups', :nested_groups do
259
        let(:parent) { create(:group, name: 'parent', path: 'parent') }
260
        let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
261 262 263 264
        let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
        let!(:project) { create(:project_empty_repo, :legacy_storage, path: 'the-project', namespace: child, skip_disk_validation: true) }
        let(:uploads_dir) { FileUploader.root }
        let(:pages_dir) { File.join(TestEnv.pages_path) }
265

266 267 268 269 270
        def expect_project_directories_at(namespace_path)
          expected_repository_path = File.join(TestEnv.repos_path, namespace_path, 'the-project.git')
          expected_upload_path = File.join(uploads_dir, namespace_path, 'the-project')
          expected_pages_path = File.join(pages_dir, namespace_path, 'the-project')

Michael Kozono's avatar
Michael Kozono committed
271 272 273
          expect(File.directory?(expected_repository_path)).to be_truthy
          expect(File.directory?(expected_upload_path)).to be_truthy
          expect(File.directory?(expected_pages_path)).to be_truthy
274 275
        end

276
        before do
277
          FileUtils.mkdir_p(File.join(TestEnv.repos_path, "#{project.full_path}.git"))
278 279 280 281 282 283
          FileUtils.mkdir_p(File.join(uploads_dir, project.full_path))
          FileUtils.mkdir_p(File.join(pages_dir, project.full_path))
        end

        context 'renaming child' do
          it 'correctly moves the repository, uploads and pages' do
284
            child.update!(path: 'renamed')
285

286
            expect_project_directories_at('parent/renamed')
287 288 289 290 291
          end
        end

        context 'renaming parent' do
          it 'correctly moves the repository, uploads and pages' do
292 293 294 295 296 297 298 299 300
            parent.update!(path: 'renamed')

            expect_project_directories_at('renamed/child')
          end
        end

        context 'moving from one parent to another' do
          it 'correctly moves the repository, uploads and pages' do
            child.update!(parent: new_parent)
301

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
            expect_project_directories_at('new_parent/child')
          end
        end

        context 'moving from having a parent to root' do
          it 'correctly moves the repository, uploads and pages' do
            child.update!(parent: nil)

            expect_project_directories_at('child')
          end
        end

        context 'moving from root to having a parent' do
          it 'correctly moves the repository, uploads and pages' do
            parent.update!(parent: new_parent)
317

318
            expect_project_directories_at('new_parent/parent/child')
319
          end
320 321
        end
      end
322
    end
323

324 325 326
    context 'hashed storage' do
      let(:namespace) { create(:namespace) }
      let!(:project) { create(:project_empty_repo, namespace: namespace) }
327

328
      it_behaves_like 'namespace restrictions'
329

330 331
      it "repository directory remains unchanged if path changed" do
        before_disk_path = project.disk_path
Lin Jen-Shin's avatar
Lin Jen-Shin committed
332
        namespace.update(path: namespace.full_path + '_new')
333 334

        expect(before_disk_path).to eq(project.disk_path)
335
        expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_truthy
336 337
      end
    end
338 339 340 341

    it 'updates project full path in .git/config for each project inside namespace' do
      parent = create(:group, name: 'mygroup', path: 'mygroup')
      subgroup = create(:group, name: 'mysubgroup', path: 'mysubgroup', parent: parent)
342 343 344
      project_in_parent_group = create(:project, :legacy_storage, :repository, namespace: parent, name: 'foo1')
      hashed_project_in_subgroup = create(:project, :repository, namespace: subgroup, name: 'foo2')
      legacy_project_in_subgroup = create(:project, :legacy_storage, :repository, namespace: subgroup, name: 'foo3')
345 346 347

      parent.update(path: 'mygroup_new')

348 349 350 351 352 353 354 355 356 357
      # Routes are loaded when creating the projects, so we need to manually
      # reload them for the below code to be aware of the above UPDATE.
      [
        project_in_parent_group,
        hashed_project_in_subgroup,
        legacy_project_in_subgroup
      ].each do |project|
        project.route.reload
      end

Jacob Vosmaer's avatar
Jacob Vosmaer committed
358 359 360 361 362 363
      expect(project_rugged(project_in_parent_group).config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
      expect(project_rugged(hashed_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
      expect(project_rugged(legacy_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
    end

    def project_rugged(project)
364 365 366
      Gitlab::GitalyClient::StorageSettings.allow_disk_access do
        project.repository.rugged
      end
367
    end
368 369
  end

370
  describe '#rm_dir', 'callback' do
371 372 373 374 375
    let(:repository_storage_path) do
      Gitlab::GitalyClient::StorageSettings.allow_disk_access do
        Gitlab.config.repositories.storages.default.legacy_disk_path
      end
    end
376 377 378 379
    let(:path_in_dir) { File.join(repository_storage_path, namespace.full_path) }
    let(:deleted_path) { namespace.full_path.gsub(namespace.path, "#{namespace.full_path}+#{namespace.id}+deleted") }
    let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) }

380 381
    context 'legacy storage' do
      let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: namespace) }
382 383 384 385

      it 'renames its dirs when deleted' do
        allow(GitlabShellWorker).to receive(:perform_in)

386
        namespace.destroy
387 388 389 390 391

        expect(File.exist?(deleted_path_in_dir)).to be(true)
      end

      it 'schedules the namespace for deletion' do
392
        expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage, deleted_path)
393

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
        namespace.destroy
      end

      context 'in sub-groups' do
        let(:parent) { create(:group, path: 'parent') }
        let(:child) { create(:group, parent: parent, path: 'child') }
        let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: child) }
        let(:path_in_dir) { File.join(repository_storage_path, 'parent', 'child') }
        let(:deleted_path) { File.join('parent', "child+#{child.id}+deleted") }
        let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) }

        it 'renames its dirs when deleted' do
          allow(GitlabShellWorker).to receive(:perform_in)

          child.destroy

          expect(File.exist?(deleted_path_in_dir)).to be(true)
        end

        it 'schedules the namespace for deletion' do
414
          expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage, deleted_path)
415 416 417 418

          child.destroy
        end
      end
419
    end
420

421 422 423 424 425 426 427
    context 'hashed storage' do
      let!(:project) { create(:project_empty_repo, namespace: namespace) }

      it 'has no repositories base directories to remove' do
        allow(GitlabShellWorker).to receive(:perform_in)

        expect(File.exist?(path_in_dir)).to be(false)
428

429 430 431 432
        namespace.destroy

        expect(File.exist?(deleted_path_in_dir)).to be(false)
      end
433
    end
434
  end
435

436
  describe '.find_by_path_or_name' do
437 438 439 440
    before do
      @namespace = create(:namespace, name: 'WoW', path: 'woW')
    end

441 442 443
    it { expect(described_class.find_by_path_or_name('wow')).to eq(@namespace) }
    it { expect(described_class.find_by_path_or_name('WOW')).to eq(@namespace) }
    it { expect(described_class.find_by_path_or_name('unknown')).to eq(nil) }
444
  end
445 446 447 448 449 450

  describe ".clean_path" do
    let!(:user)       { create(:user, username: "johngitlab-etc") }
    let!(:namespace)  { create(:namespace, path: "JohnGitLab-etc1") }

    it "cleans the path and makes sure it's available" do
451 452
      expect(described_class.clean_path("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2")
      expect(described_class.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name")
453 454
    end
  end
455

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
  describe '#self_and_hierarchy', :nested_groups do
    let!(:group) { create(:group, path: 'git_lab') }
    let!(:nested_group) { create(:group, parent: group) }
    let!(:deep_nested_group) { create(:group, parent: nested_group) }
    let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
    let!(:another_group) { create(:group, path: 'gitllab') }
    let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }

    it 'returns the correct tree' do
      expect(group.self_and_hierarchy).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
      expect(nested_group.self_and_hierarchy).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
      expect(very_deep_nested_group.self_and_hierarchy).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
    end
  end

471
  describe '#ancestors', :nested_groups do
472 473 474
    let(:group) { create(:group) }
    let(:nested_group) { create(:group, parent: group) }
    let(:deep_nested_group) { create(:group, parent: nested_group) }
475
    let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
476

477
    it 'returns the correct ancestors' do
478 479 480
      expect(very_deep_nested_group.ancestors).to include(group, nested_group, deep_nested_group)
      expect(deep_nested_group.ancestors).to include(group, nested_group)
      expect(nested_group.ancestors).to include(group)
481 482 483 484
      expect(group.ancestors).to eq([])
    end
  end

485 486 487 488 489 490 491 492 493 494 495 496 497 498
  describe '#self_and_ancestors', :nested_groups do
    let(:group) { create(:group) }
    let(:nested_group) { create(:group, parent: group) }
    let(:deep_nested_group) { create(:group, parent: nested_group) }
    let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }

    it 'returns the correct ancestors' do
      expect(very_deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
      expect(deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group)
      expect(nested_group.self_and_ancestors).to contain_exactly(group, nested_group)
      expect(group.self_and_ancestors).to contain_exactly(group)
    end
  end

499
  describe '#descendants', :nested_groups do
500
    let!(:group) { create(:group, path: 'git_lab') }
501 502 503
    let!(:nested_group) { create(:group, parent: group) }
    let!(:deep_nested_group) { create(:group, parent: nested_group) }
    let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
504 505
    let!(:another_group) { create(:group, path: 'gitllab') }
    let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
506 507 508

    it 'returns the correct descendants' do
      expect(very_deep_nested_group.descendants.to_a).to eq([])
509 510 511
      expect(deep_nested_group.descendants.to_a).to include(very_deep_nested_group)
      expect(nested_group.descendants.to_a).to include(deep_nested_group, very_deep_nested_group)
      expect(group.descendants.to_a).to include(nested_group, deep_nested_group, very_deep_nested_group)
512
    end
513
  end
514

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
  describe '#self_and_descendants', :nested_groups do
    let!(:group) { create(:group, path: 'git_lab') }
    let!(:nested_group) { create(:group, parent: group) }
    let!(:deep_nested_group) { create(:group, parent: nested_group) }
    let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
    let!(:another_group) { create(:group, path: 'gitllab') }
    let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }

    it 'returns the correct descendants' do
      expect(very_deep_nested_group.self_and_descendants).to contain_exactly(very_deep_nested_group)
      expect(deep_nested_group.self_and_descendants).to contain_exactly(deep_nested_group, very_deep_nested_group)
      expect(nested_group.self_and_descendants).to contain_exactly(nested_group, deep_nested_group, very_deep_nested_group)
      expect(group.self_and_descendants).to contain_exactly(group, nested_group, deep_nested_group, very_deep_nested_group)
    end
  end

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
  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)
      deep_nested_group.add_developer(user_a)

      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

550 551
  describe '#user_ids_for_project_authorizations' do
    it 'returns the user IDs for which to refresh authorizations' do
552 553
      expect(namespace.user_ids_for_project_authorizations)
        .to eq([namespace.owner_id])
554 555
    end
  end
556 557 558 559 560 561 562

  describe '#all_projects' do
    let(:group) { create(:group) }
    let(:child) { create(:group, parent: group) }
    let!(:project1) { create(:project_empty_repo, namespace: group) }
    let!(:project2) { create(:project_empty_repo, namespace: child) }

563
    it { expect(group.all_projects.to_a).to match_array([project2, project1]) }
564
  end
565

566
  describe '#share_with_group_lock with subgroups', :nested_groups do
567 568 569
    context 'when creating a subgroup' do
      let(:subgroup) { create(:group, parent: root_group )}

570
      context 'under a parent with "Share with group lock" enabled' do
571 572
        let(:root_group) { create(:group, share_with_group_lock: true) }

573
        it 'enables "Share with group lock" on the subgroup' do
574 575 576 577
          expect(subgroup.share_with_group_lock).to be_truthy
        end
      end

578
      context 'under a parent with "Share with group lock" disabled' do
579 580
        let(:root_group) { create(:group) }

581
        it 'does not enable "Share with group lock" on the subgroup' do
582 583 584 585 586
          expect(subgroup.share_with_group_lock).to be_falsey
        end
      end
    end

587
    context 'when enabling the parent group "Share with group lock"' do
588 589 590
      let(:root_group) { create(:group) }
      let!(:subgroup) { create(:group, parent: root_group )}

591
      it 'the subgroup "Share with group lock" becomes enabled' do
592
        root_group.update!(share_with_group_lock: true)
593 594 595 596 597

        expect(subgroup.reload.share_with_group_lock).to be_truthy
      end
    end

598
    context 'when disabling the parent group "Share with group lock" (which was already enabled)' do
599 600
      let(:root_group) { create(:group, share_with_group_lock: true) }

601
      context 'and the subgroup "Share with group lock" is enabled' do
602 603
        let(:subgroup) { create(:group, parent: root_group, share_with_group_lock: true )}

604
        it 'the subgroup "Share with group lock" does not change' do
605
          root_group.update!(share_with_group_lock: false)
606 607 608 609 610

          expect(subgroup.reload.share_with_group_lock).to be_truthy
        end
      end

611
      context 'but the subgroup "Share with group lock" is disabled' do
612 613
        let(:subgroup) { create(:group, parent: root_group )}

614
        it 'the subgroup "Share with group lock" does not change' do
615
          root_group.update!(share_with_group_lock: false)
616 617 618 619 620 621 622

          expect(subgroup.reload.share_with_group_lock?).to be_falsey
        end
      end
    end

    context 'when a group is transferred into a root group' do
623
      context 'when the root group "Share with group lock" is enabled' do
624 625
        let(:root_group) { create(:group, share_with_group_lock: true) }

626
        context 'when the subgroup "Share with group lock" is enabled' do
627 628
          let(:subgroup) { create(:group, share_with_group_lock: true )}

629
          it 'the subgroup "Share with group lock" does not change' do
630 631 632 633 634 635 636
            subgroup.parent = root_group
            subgroup.save!

            expect(subgroup.share_with_group_lock).to be_truthy
          end
        end

637
        context 'when the subgroup "Share with group lock" is disabled' do
638 639
          let(:subgroup) { create(:group)}

640
          it 'the subgroup "Share with group lock" becomes enabled' do
641 642 643 644 645 646 647 648
            subgroup.parent = root_group
            subgroup.save!

            expect(subgroup.share_with_group_lock).to be_truthy
          end
        end
      end

649
      context 'when the root group "Share with group lock" is disabled' do
650 651
        let(:root_group) { create(:group) }

652
        context 'when the subgroup "Share with group lock" is enabled' do
653 654
          let(:subgroup) { create(:group, share_with_group_lock: true )}

655
          it 'the subgroup "Share with group lock" does not change' do
656 657 658 659 660 661 662
            subgroup.parent = root_group
            subgroup.save!

            expect(subgroup.share_with_group_lock).to be_truthy
          end
        end

663
        context 'when the subgroup "Share with group lock" is disabled' do
664 665
          let(:subgroup) { create(:group)}

666
          it 'the subgroup "Share with group lock" does not change' do
667 668 669 670 671 672 673 674 675
            subgroup.parent = root_group
            subgroup.save!

            expect(subgroup.share_with_group_lock).to be_falsey
          end
        end
      end
    end
  end
676

677
  describe '#find_fork_of?' do
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
    let(:project) { create(:project, :public) }
    let!(:forked_project) { fork_project(project, namespace.owner, namespace: namespace) }

    before do
      # Reset the fork network relation
      project.reload
    end

    it 'knows if there is a direct fork in the namespace' do
      expect(namespace.find_fork_of(project)).to eq(forked_project)
    end

    it 'knows when there is as fork-of-fork in the namespace' do
      other_namespace = create(:namespace)
      other_fork = fork_project(forked_project, other_namespace.owner, namespace: other_namespace)

      expect(other_namespace.find_fork_of(project)).to eq(other_fork)
    end
696 697 698 699 700 701 702 703

    context 'with request store enabled', :request_store do
      it 'only queries once' do
        expect(project.fork_network).to receive(:find_forks_in).once.and_call_original

        2.times { namespace.find_fork_of(project) }
      end
    end
704
  end
705

706 707 708 709 710 711 712 713 714 715 716 717 718
  describe '#root_ancestor' do
    it 'returns the top most ancestor', :nested_groups do
      root_group = create(:group)
      nested_group = create(:group, parent: root_group)
      deep_nested_group = create(:group, parent: nested_group)
      very_deep_nested_group = create(:group, parent: deep_nested_group)

      expect(nested_group.root_ancestor).to eq(root_group)
      expect(deep_nested_group.root_ancestor).to eq(root_group)
      expect(very_deep_nested_group.root_ancestor).to eq(root_group)
    end
  end

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
  describe '#full_path_was' do
    context 'when the group has no parent' do
      it 'should return the path was' do
        group = create(:group, parent: nil)
        expect(group.full_path_was).to eq(group.path_was)
      end
    end

    context 'when a parent is assigned to a group with no previous parent' do
      it 'should return the path was' do
        group = create(:group, parent: nil)

        parent = create(:group)
        group.parent = parent

        expect(group.full_path_was).to eq("#{group.path_was}")
      end
    end

    context 'when a parent is removed from the group' do
      it 'should return the parent full path' do
        parent = create(:group)
        group = create(:group, parent: parent)
        group.parent = nil

        expect(group.full_path_was).to eq("#{parent.full_path}/#{group.path}")
      end
    end

    context 'when changing parents' do
      it 'should return the previous parent full path' do
        parent = create(:group)
        group = create(:group, parent: parent)
        new_parent = create(:group)
        group.parent = new_parent
        expect(group.full_path_was).to eq("#{parent.full_path}/#{group.path}")
      end
    end
  end
758
end