gitlab-org--gitlab-foss/spec/models/namespace_spec.rb
Grzegorz Bizon fc550b398b Merge branch 'master' into feature/multi-level-container-registry-images
* master: (230 commits)
  Fix N+1 query in loading pipelines in merge requests
  Fix Spinach and Capybara dependencies
  Prevent users from disconnecting gitlab account from CAS
  30276 Move issue, mr, todos next to profile dropdown in top nav
  Refactor SearchController#show
  Properly eagerly-load the Capybara server for JS feature specs only
  Updating documentation to include a missing step in the update procedure
  Eager-load the Capybara server to prevent timeouts
  Increase Capybara's timeout
  Add metrics button to Environment Overview page
  Fix link to Jira service documentation
  Handle parsing OpenBSD ps output properly to display sidekiq infos on ...
  Eliminate unnecessary queries that add ~500 ms of load time for a large issue
  20914 Limits line length for project home page
  Allow users to import GitHub projects to subgroups
  Update dpl CI example
  Fix the docs:check:links job
  Don't clean up the gitlab-test-fork_bare repo
  Make GitLab use Gitaly for commit_is_ancestor
  Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery
  ...
2017-04-03 09:36:09 +02:00

300 lines
11 KiB
Ruby

require 'spec_helper'
describe Namespace, models: true do
let!(:namespace) { create(:namespace) }
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
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) }
it 'does not allow too deep nesting' do
ancestors = (1..21).to_a
nested = build(:namespace, parent: namespace)
allow(nested).to receive(:ancestors).and_return(ancestors)
expect(nested).not_to be_valid
expect(nested.errors[:parent_id].first).to eq('has too deep level of nesting')
end
describe 'reserved path validation' do
context 'nested group' do
let(:group) { build(:group, :nested, path: 'tree') }
it { expect(group).not_to be_valid }
end
context 'top-level group' do
let(:group) { build(:group, path: 'tree') }
it { expect(group).to be_valid }
end
end
end
describe "Respond to" do
it { is_expected.to respond_to(:human_name) }
it { is_expected.to respond_to(:to_param) }
end
describe '#to_param' do
it { expect(namespace.to_param).to eq(namespace.full_path) }
end
describe '#human_name' do
it { expect(namespace.human_name).to eq(namespace.owner_name) }
end
describe '.search' do
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])
end
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
end
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
describe '#move_dir', repository: true do
before do
@namespace = create :namespace
@project = create(:project_empty_repo, namespace: @namespace)
allow(@namespace).to receive(:path_changed?).and_return(true)
end
it "raises error when directory exists" do
expect { @namespace.move_dir }.to raise_error("namespace directory cannot be moved")
end
it "moves dir if path changed" do
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)
expect(@namespace).to receive(:remove_exports!)
expect(@namespace.move_dir).to be_truthy
end
context "when any project has container images" do
let(:container_repository) { create(:container_repository) }
before do
stub_container_registry_config(enabled: true)
stub_container_registry_tags('tag')
create(:empty_project, namespace: @namespace, container_repositories: [container_repository])
allow(@namespace).to receive(:path_was).and_return(@namespace.path)
allow(@namespace).to receive(:path).and_return('new_path')
end
it { expect { @namespace.move_dir }.to raise_error('Namespace cannot be moved, because at least one project has images in container registry') }
end
context 'renaming a sub-group' do
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) }
let(:uploads_dir) { File.join(CarrierWave.root, 'uploads', 'parent') }
let(:pages_dir) { File.join(TestEnv.pages_path, 'parent') }
before do
FileUtils.mkdir_p(File.join(uploads_dir, 'child', 'the-project'))
FileUtils.mkdir_p(File.join(pages_dir, 'child', 'the-project'))
end
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, 'renamed', 'the-project')
expected_pages_path = File.join(pages_dir, 'renamed', 'the-project')
child.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
end
end
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)
namespace.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)
namespace.destroy
end
context 'in sub-groups' do
let(:parent) { create(:namespace, path: 'parent') }
let(:child) { create(:namespace, parent: parent, path: 'child') }
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)
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
end
it 'removes the exports folder' do
expect(namespace).to receive(:remove_exports!)
namespace.destroy
end
end
describe '.find_by_path_or_name' do
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
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")
expect(Namespace.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name")
end
end
describe '#ancestors' 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.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
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.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])
end
end
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
end