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

Douwe Maan's avatar
Douwe Maan committed
3
describe Namespace, models: true do
4 5
  let!(:namespace) { create(:namespace) }

6 7 8 9 10 11
  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
12

13 14 15 16 17 18 19 20
  describe 'validations' do
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) }
    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) }
21

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

26
      allow(nested).to receive(:ancestors).and_return(ancestors)
27

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

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

        it { expect(group).not_to be_valid }
37 38 39

        it 'rejects nested paths' do
          parent = create(:group, :nested, path: 'environments')
40
          namespace = build(:group, path: 'folders', parent: parent)
41 42 43

          expect(namespace).not_to be_valid
        end
44 45 46 47 48 49 50 51
      end

      context 'top-level group' do
        let(:group) { build(:group, path: 'tree') }

        it { expect(group).to be_valid }
      end
    end
52
  end
53 54

  describe "Respond to" do
55 56
    it { is_expected.to respond_to(:human_name) }
    it { is_expected.to respond_to(:to_param) }
57
    it { is_expected.to respond_to(:has_parent?) }
58
  end
59

60
  describe '#to_param' do
61
    it { expect(namespace.to_param).to eq(namespace.full_path) }
62 63
  end

64
  describe '#human_name' do
65
    it { expect(namespace.human_name).to eq(namespace.owner_name) }
66 67
  end

68
  describe '.search' do
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    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])
85 86
    end

87 88 89 90 91 92 93
    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
94 95
  end

96 97 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 130 131 132 133 134 135 136 137 138 139
  describe '.with_statistics' do
    let(:namespace) { create :namespace }

    let(:project1) do
      create(:empty_project,
             namespace: namespace,
             statistics: build(:project_statistics,
                               storage_size:         606,
                               repository_size:      101,
                               lfs_objects_size:     202,
                               build_artifacts_size: 303))
    end

    let(:project2) do
      create(:empty_project,
             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
      statistics = Namespace.with_statistics.find(namespace.id)

      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
      statistics = Namespace.with_statistics.find(namespace.id)

      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

140
  describe '#move_dir', repository: true do
141 142
    before do
      @namespace = create :namespace
143
      @project = create(:project_empty_repo, namespace: @namespace)
144
      allow(@namespace).to receive(:path_changed?).and_return(true)
145 146
    end

147
    it "raises error when directory exists" do
148
      expect { @namespace.move_dir }.to raise_error("namespace directory cannot be moved")
149 150
    end

151
    it "moves dir if path changed" do
152 153 154
      new_path = @namespace.full_path + "_new"
      allow(@namespace).to receive(:full_path_was).and_return(@namespace.full_path)
      allow(@namespace).to receive(:full_path).and_return(new_path)
155
      expect(@namespace).to receive(:remove_exports!)
156
      expect(@namespace.move_dir).to be_truthy
157
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
158

159 160 161
    context "when any project has container images" do
      let(:container_repository) { create(:container_repository) }

Kamil Trzcinski's avatar
Kamil Trzcinski committed
162 163
      before do
        stub_container_registry_config(enabled: true)
164
        stub_container_registry_tags(repository: :any, tags: ['tag'])
Kamil Trzcinski's avatar
Kamil Trzcinski committed
165

166
        create(:empty_project, namespace: @namespace, container_repositories: [container_repository])
Kamil Trzcinski's avatar
Kamil Trzcinski committed
167 168 169 170 171

        allow(@namespace).to receive(:path_was).and_return(@namespace.path)
        allow(@namespace).to receive(:path).and_return('new_path')
      end

172 173 174
      it 'raises an error about not movable project' do
        expect { @namespace.move_dir }.to raise_error(/Namespace cannot be moved/)
      end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
175
    end
176

177
    context 'with subgroups' do
178 179 180
      let(:parent) { create(:group, name: 'parent', path: 'parent') }
      let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
      let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child) }
181 182
      let(:uploads_dir) { File.join(CarrierWave.root, 'uploads') }
      let(:pages_dir) { TestEnv.pages_path }
183 184

      before do
185 186
        FileUtils.mkdir_p(File.join(uploads_dir, 'parent', 'child', 'the-project'))
        FileUtils.mkdir_p(File.join(pages_dir, 'parent', 'child', 'the-project'))
187 188
      end

189 190 191 192 193
      context 'renaming child' do
        it 'correctly moves the repository, uploads and pages' do
          expected_repository_path = File.join(TestEnv.repos_path, 'parent', 'renamed', 'the-project.git')
          expected_upload_path = File.join(uploads_dir, 'parent', 'renamed', 'the-project')
          expected_pages_path = File.join(pages_dir, 'parent', 'renamed', 'the-project')
194

195
          child.update_attributes!(path: 'renamed')
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
          expect(File.directory?(expected_repository_path)).to be(true)
          expect(File.directory?(expected_upload_path)).to be(true)
          expect(File.directory?(expected_pages_path)).to be(true)
        end
      end

      context 'renaming parent' do
        it 'correctly moves the repository, uploads and pages' do
          expected_repository_path = File.join(TestEnv.repos_path, 'renamed', 'child', 'the-project.git')
          expected_upload_path = File.join(uploads_dir, 'renamed', 'child', 'the-project')
          expected_pages_path = File.join(pages_dir, 'renamed', 'child', 'the-project')

          parent.update_attributes!(path: 'renamed')

          expect(File.directory?(expected_repository_path)).to be(true)
          expect(File.directory?(expected_upload_path)).to be(true)
          expect(File.directory?(expected_pages_path)).to be(true)
        end
215 216
      end
    end
217 218
  end

219 220 221 222 223 224 225 226 227 228 229 230
  describe '#actual_size_limit' do
    let(:namespace) { build(:namespace) }

    before do
      allow_any_instance_of(ApplicationSetting).to receive(:repository_size_limit).and_return(50)
    end

    it 'returns the correct size limit' do
      expect(namespace.actual_size_limit).to eq(50)
    end
  end

231 232 233 234 235 236 237 238 239
  describe '#rm_dir', 'callback', repository: true do
    let!(:project) { create(:project_empty_repo, namespace: namespace) }
    let(:repository_storage_path) { Gitlab.config.repositories.storages.default['path'] }
    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) }

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

241 242
      namespace.destroy

243 244 245 246 247
      expect(File.exist?(deleted_path_in_dir)).to be(true)
    end

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

249
      namespace.destroy
250 251 252
    end

    context 'in sub-groups' do
253 254
      let(:parent) { create(:group, path: 'parent') }
      let(:child) { create(:group, parent: parent, path: 'child') }
255 256 257 258 259 260 261
      let!(:project) { create(:project_empty_repo, 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)
262

263 264 265 266 267 268 269 270 271 272
        child.destroy

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

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

        child.destroy
      end
273
    end
274 275 276 277 278 279

    it 'removes the exports folder' do
      expect(namespace).to receive(:remove_exports!)

      namespace.destroy
    end
280
  end
281

282
  describe '.find_by_path_or_name' do
283 284 285 286 287 288 289 290
    before do
      @namespace = create(:namespace, name: 'WoW', path: 'woW')
    end

    it { expect(Namespace.find_by_path_or_name('wow')).to eq(@namespace) }
    it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) }
    it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) }
  end
291 292 293 294 295 296 297

  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
      expect(Namespace.clean_path("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2")
298
      expect(Namespace.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name")
299 300
    end
  end
301

302
  describe '#ancestors' do
303 304 305
    let(:group) { create(:group) }
    let(:nested_group) { create(:group, parent: group) }
    let(:deep_nested_group) { create(:group, parent: nested_group) }
306
    let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
307

308 309 310 311 312 313 314 315 316
    it 'returns the correct ancestors' do
      expect(very_deep_nested_group.ancestors).to eq([group, nested_group, deep_nested_group])
      expect(deep_nested_group.ancestors).to eq([group, nested_group])
      expect(nested_group.ancestors).to eq([group])
      expect(group.ancestors).to eq([])
    end
  end

  describe '#descendants' do
317
    let!(:group) { create(:group, path: 'git_lab') }
318 319 320
    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) }
321 322
    let!(:another_group) { create(:group, path: 'gitllab') }
    let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
323 324 325 326 327 328

    it 'returns the correct descendants' do
      expect(very_deep_nested_group.descendants.to_a).to eq([])
      expect(deep_nested_group.descendants.to_a).to eq([very_deep_nested_group])
      expect(nested_group.descendants.to_a).to eq([deep_nested_group, very_deep_nested_group])
      expect(group.descendants.to_a).to eq([nested_group, deep_nested_group, very_deep_nested_group])
329
    end
330
  end
331 332 333 334 335 336 337

  describe '#user_ids_for_project_authorizations' do
    it 'returns the user IDs for which to refresh authorizations' do
      expect(namespace.user_ids_for_project_authorizations).
        to eq([namespace.owner_id])
    end
  end
338 339 340 341 342 343 344 345 346

  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) }

    it { expect(group.all_projects.to_a).to eq([project2, project1]) }
  end
347
end