summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ability_spec.rb95
-rw-r--r--spec/models/chat_name_spec.rb20
-rw-r--r--spec/models/ci/build_spec.rb11
-rw-r--r--spec/models/ci/group_variable_spec.rb2
-rw-r--r--spec/models/ci/variable_spec.rb2
-rw-r--r--spec/models/concerns/protected_ref_access_spec.rb31
-rw-r--r--spec/models/group_spec.rb8
-rw-r--r--spec/models/identity_spec.rb33
-rw-r--r--spec/models/issue_spec.rb50
-rw-r--r--spec/models/key_spec.rb21
-rw-r--r--spec/models/namespace_spec.rb206
-rw-r--r--spec/models/notification_recipient_spec.rb16
-rw-r--r--spec/models/pages_domain_spec.rb144
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb8
-rw-r--r--spec/models/project_spec.rb46
-rw-r--r--spec/models/repository_spec.rb12
-rw-r--r--spec/models/user_spec.rb41
17 files changed, 610 insertions, 136 deletions
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 38fb98d4f50..cd175dba6da 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -204,6 +204,78 @@ describe Ability do
end
end
+ describe '.merge_requests_readable_by_user' do
+ context 'with an admin' do
+ it 'returns all merge requests' do
+ user = build(:user, admin: true)
+ merge_request = build(:merge_request)
+
+ expect(described_class.merge_requests_readable_by_user([merge_request], user))
+ .to eq([merge_request])
+ end
+ end
+
+ context 'without a user' do
+ it 'returns merge_requests that are publicly visible' do
+ hidden_merge_request = build(:merge_request)
+ visible_merge_request = build(:merge_request, source_project: build(:project, :public))
+
+ merge_requests = described_class
+ .merge_requests_readable_by_user([hidden_merge_request, visible_merge_request])
+
+ expect(merge_requests).to eq([visible_merge_request])
+ end
+ end
+
+ context 'with a user' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:cross_project_merge_request) do
+ create(:merge_request, source_project: create(:project, :public))
+ end
+ let(:other_merge_request) { create(:merge_request) }
+ let(:all_merge_requests) do
+ [merge_request, cross_project_merge_request, other_merge_request]
+ end
+
+ subject(:readable_merge_requests) do
+ described_class.merge_requests_readable_by_user(all_merge_requests, user)
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns projects visible to the user' do
+ expect(readable_merge_requests).to contain_exactly(merge_request, cross_project_merge_request)
+ end
+
+ context 'when a user cannot read cross project and a filter is passed' do
+ before do
+ allow(described_class).to receive(:allowed?).and_call_original
+ expect(described_class).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ subject(:readable_merge_requests) do
+ read_cross_project_filter = -> (merge_requests) do
+ merge_requests.select { |mr| mr.source_project == project }
+ end
+ described_class.merge_requests_readable_by_user(
+ all_merge_requests, user,
+ filters: { read_cross_project: read_cross_project_filter }
+ )
+ end
+
+ it 'returns only MRs of the specified project without checking access on others' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_merge_request, cross_project_merge_request)
+
+ expect(readable_merge_requests).to contain_exactly(merge_request)
+ end
+ end
+ end
+ end
+
describe '.issues_readable_by_user' do
context 'with an admin user' do
it 'returns all given issues' do
@@ -250,6 +322,29 @@ describe Ability do
expect(issues).to eq([visible_issue])
end
end
+
+ context 'when the user cannot read cross project' do
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue) }
+ let(:other_project_issue) { create(:issue) }
+ let(:project) { issue.project }
+
+ before do
+ project.add_developer(user)
+
+ allow(described_class).to receive(:allowed?).and_call_original
+ allow(described_class).to receive(:allowed?).with(user, :read_cross_project, any_args) { false }
+ end
+
+ it 'excludes issues from other projects whithout checking separatly when passing a scope' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_issue, other_project_issue)
+
+ filters = { read_cross_project: -> (issues) { issues.where(project: project) } }
+ result = described_class.issues_readable_by_user(Issue.all, user, filters: filters)
+
+ expect(result).to contain_exactly(issue)
+ end
+ end
end
describe '.project_disabled_features_rules' do
diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb
index e89e534d914..504bc710b25 100644
--- a/spec/models/chat_name_spec.rb
+++ b/spec/models/chat_name_spec.rb
@@ -14,4 +14,24 @@ describe ChatName do
it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:service_id) }
it { is_expected.to validate_uniqueness_of(:chat_id).scoped_to(:service_id, :team_id) }
+
+ describe '#update_last_used_at', :clean_gitlab_redis_shared_state do
+ it 'updates the last_used_at timestamp' do
+ expect(subject.last_used_at).to be_nil
+
+ subject.update_last_used_at
+
+ expect(subject.last_used_at).to be_present
+ end
+
+ it 'does not update last_used_at if it was recently updated' do
+ subject.update_last_used_at
+
+ time = subject.last_used_at
+
+ subject.update_last_used_at
+
+ expect(subject.last_used_at).to eq(time)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 559e6f12565..917e6eec753 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -335,7 +335,7 @@ describe Ci::Build do
allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
end
- it { is_expected.to be_an(Array).and all(include(key: "key_1")) }
+ it { is_expected.to be_an(Array).and all(include(key: "key-1")) }
end
context 'when project does not have jobs_cache_index' do
@@ -1471,6 +1471,7 @@ describe Ci::Build do
[
{ key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true },
+ { key: 'GITLAB_FEATURES', value: project.namespace.features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
@@ -1647,7 +1648,7 @@ describe Ci::Build do
context 'when the branch is protected' do
before do
- create(:protected_branch, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -1655,7 +1656,7 @@ describe Ci::Build do
context 'when the tag is protected' do
before do
- create(:protected_tag, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -1692,7 +1693,7 @@ describe Ci::Build do
context 'when the branch is protected' do
before do
- create(:protected_branch, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -1700,7 +1701,7 @@ describe Ci::Build do
context 'when the tag is protected' do
before do
- create(:protected_tag, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb
index 145189e7469..1b10501701c 100644
--- a/spec/models/ci/group_variable_spec.rb
+++ b/spec/models/ci/group_variable_spec.rb
@@ -5,7 +5,7 @@ describe Ci::GroupVariable do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to include_module(Presentable) }
- it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id).with_message(/\(\w+\) has already been taken/) }
describe '.unprotected' do
subject { described_class.unprotected }
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index e4ff551151e..875e8b2b682 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -6,7 +6,7 @@ describe Ci::Variable do
describe 'validations' do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to include_module(Presentable) }
- it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope).with_message(/\(\w+\) has already been taken/) }
end
describe '.unprotected' do
diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb
new file mode 100644
index 00000000000..a62ca391e25
--- /dev/null
+++ b/spec/models/concerns/protected_ref_access_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe ProtectedRefAccess do
+ subject(:protected_ref_access) do
+ create(:protected_branch, :masters_can_push).push_access_levels.first
+ end
+
+ let(:project) { protected_ref_access.project }
+
+ describe '#check_access' do
+ it 'is always true for admins' do
+ admin = create(:admin)
+
+ expect(protected_ref_access.check_access(admin)).to be_truthy
+ end
+
+ it 'is true for masters' do
+ master = create(:user)
+ project.add_master(master)
+
+ expect(protected_ref_access.check_access(master)).to be_truthy
+ end
+
+ it 'is for developers of the project' do
+ developer = create(:user)
+ project.add_developer(developer)
+
+ expect(protected_ref_access.check_access(developer)).to be_falsy
+ end
+ end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 338fb314ee9..4f16b73ef38 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -549,7 +549,7 @@ describe Group do
context 'when the ref is a protected branch' do
before do
- create(:protected_branch, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -557,7 +557,7 @@ describe Group do
context 'when the ref is a protected tag' do
before do
- create(:protected_tag, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -571,6 +571,10 @@ describe Group do
let(:variable_child_2) { create(:ci_group_variable, group: group_child_2) }
let(:variable_child_3) { create(:ci_group_variable, group: group_child_3) }
+ before do
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
+ end
+
it 'returns all variables belong to the group and parent groups' do
expected_array1 = [protected_variable, secret_variable]
expected_array2 = [variable_child, variable_child_2, variable_child_3]
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 7c66c98231b..a5ce245c21d 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -70,5 +70,38 @@ describe Identity do
end
end
end
+
+ context 'after_destroy' do
+ let!(:user) { create(:user) }
+ let(:ldap_identity) { create(:identity, provider: 'ldapmain', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', user: user) }
+ let(:ldap_user_synced_attributes) { { provider: 'ldapmain', name_synced: true, email_synced: true } }
+ let(:other_provider_user_synced_attributes) { { provider: 'other', name_synced: true, email_synced: true } }
+
+ describe 'if user synced attributes metadada provider' do
+ context 'matches the identity provider ' do
+ it 'removes the user synced attributes' do
+ user.create_user_synced_attributes_metadata(ldap_user_synced_attributes)
+
+ expect(user.user_synced_attributes_metadata.provider).to eq 'ldapmain'
+
+ ldap_identity.destroy
+
+ expect(user.reload.user_synced_attributes_metadata).to be_nil
+ end
+ end
+
+ context 'does not matche the identity provider' do
+ it 'does not remove the user synced attributes' do
+ user.create_user_synced_attributes_metadata(other_provider_user_synced_attributes)
+
+ expect(user.user_synced_attributes_metadata.provider).to eq 'other'
+
+ ldap_identity.destroy
+
+ expect(user.reload.user_synced_attributes_metadata.provider).to eq 'other'
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index f5c9f551e65..feed7968f09 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -221,27 +221,55 @@ describe Issue do
end
describe '#referenced_merge_requests' do
- it 'returns the referenced merge requests' do
- project = create(:project, :public)
-
- mr1 = create(:merge_request,
- source_project: project,
- source_branch: 'master',
- target_branch: 'feature')
+ let(:project) { create(:project, :public) }
+ let(:issue) do
+ create(:issue, description: merge_request.to_reference, project: project)
+ end
+ let!(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'master',
+ target_branch: 'feature')
+ end
+ it 'returns the referenced merge requests' do
mr2 = create(:merge_request,
source_project: project,
source_branch: 'feature',
target_branch: 'master')
- issue = create(:issue, description: mr1.to_reference, project: project)
-
create(:note_on_issue,
noteable: issue,
note: mr2.to_reference,
project_id: project.id)
- expect(issue.referenced_merge_requests).to eq([mr1, mr2])
+ expect(issue.referenced_merge_requests).to eq([merge_request, mr2])
+ end
+
+ it 'returns cross project referenced merge requests' do
+ other_project = create(:project, :public)
+ cross_project_merge_request = create(:merge_request, source_project: other_project)
+ create(:note_on_issue,
+ noteable: issue,
+ note: cross_project_merge_request.to_reference(issue.project),
+ project_id: issue.project.id)
+
+ expect(issue.referenced_merge_requests).to eq([merge_request, cross_project_merge_request])
+ end
+
+ it 'excludes cross project references if the user cannot read cross project' do
+ user = create(:user)
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ other_project = create(:project, :public)
+ cross_project_merge_request = create(:merge_request, source_project: other_project)
+ create(:note_on_issue,
+ noteable: issue,
+ note: cross_project_merge_request.to_reference(issue.project),
+ project_id: issue.project.id)
+
+ expect(issue.referenced_merge_requests(user)).to eq([merge_request])
end
end
@@ -309,7 +337,7 @@ describe Issue do
end
describe '#related_branches' do
- let(:user) { build(:admin) }
+ let(:user) { create(:admin) }
before do
allow(subject.project.repository).to receive(:branch_names)
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index bf5703ac986..06d26ef89f1 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -12,6 +12,9 @@ describe Key, :mailer do
it { is_expected.to validate_presence_of(:key) }
it { is_expected.to validate_length_of(:key).is_at_most(5000) }
it { is_expected.to allow_value(attributes_for(:rsa_key_2048)[:key]).for(:key) }
+ it { is_expected.to allow_value(attributes_for(:rsa_key_4096)[:key]).for(:key) }
+ it { is_expected.to allow_value(attributes_for(:rsa_key_5120)[:key]).for(:key) }
+ it { is_expected.to allow_value(attributes_for(:rsa_key_8192)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:dsa_key_2048)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:ecdsa_key_256)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:ed25519_key_256)[:key]).for(:key) }
@@ -103,24 +106,6 @@ describe Key, :mailer do
end
end
- context 'validate size' do
- where(:key_content, :result) do
- [
- [Spec::Support::Helpers::KeyGeneratorHelper.new(512).generate, false],
- [Spec::Support::Helpers::KeyGeneratorHelper.new(8192).generate, false],
- [Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate, true]
- ]
- end
-
- with_them do
- it 'validates the size of the key' do
- key = build(:key, key: key_content)
-
- expect(key.valid?).to eq(result)
- end
- end
- end
-
context 'validate it meets key restrictions' do
where(:factory, :minimum, :result) do
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 191b60e4383..e626efd054d 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -168,84 +168,105 @@ describe Namespace do
end
describe '#move_dir', :request_store do
- let(:namespace) { create(:namespace) }
- let!(:project) { create(:project_empty_repo, namespace: namespace) }
+ shared_examples "namespace restrictions" do
+ context "when any project has container images" do
+ let(:container_repository) { create(:container_repository) }
- it "raises error when directory exists" do
- expect { namespace.move_dir }.to raise_error("namespace directory cannot be moved")
- end
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: :any, tags: ['tag'])
- it "moves dir if path changed" do
- namespace.update_attributes(path: namespace.full_path + '_new')
+ create(:project, namespace: namespace, container_repositories: [container_repository])
- expect(gitlab_shell.exists?(project.repository_storage_path, "#{namespace.path}/#{project.path}.git")).to be_truthy
- end
+ allow(namespace).to receive(:path_was).and_return(namespace.path)
+ allow(namespace).to receive(:path).and_return('new_path')
+ end
- context "when any project has container images" do
- let(:container_repository) { create(:container_repository) }
+ it 'raises an error about not movable project' do
+ expect { namespace.move_dir }.to raise_error(/Namespace cannot be moved/)
+ end
+ end
+ end
- before do
- stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: :any, tags: ['tag'])
+ context 'legacy storage' do
+ let(:namespace) { create(:namespace) }
+ let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: namespace) }
- create(:project, namespace: namespace, container_repositories: [container_repository])
+ it_behaves_like 'namespace restrictions'
- allow(namespace).to receive(:path_was).and_return(namespace.path)
- allow(namespace).to receive(:path).and_return('new_path')
+ it "raises error when directory exists" do
+ expect { namespace.move_dir }.to raise_error("namespace directory cannot be moved")
end
- it 'raises an error about not movable project' do
- expect { namespace.move_dir }.to raise_error(/Namespace cannot be moved/)
+ it "moves dir if path changed" do
+ namespace.update_attributes(path: namespace.full_path + '_new')
+
+ expect(gitlab_shell.exists?(project.repository_storage_path, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
- end
- context 'with subgroups' 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, skip_disk_validation: true) }
- let(:uploads_dir) { FileUploader.root }
- let(:pages_dir) { File.join(TestEnv.pages_path) }
+ context 'with subgroups' 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, :legacy_storage, path: 'the-project', namespace: child, skip_disk_validation: true) }
+ let(:uploads_dir) { FileUploader.root }
+ let(:pages_dir) { File.join(TestEnv.pages_path) }
- before do
- FileUtils.mkdir_p(File.join(uploads_dir, 'parent', 'child', 'the-project'))
- FileUtils.mkdir_p(File.join(pages_dir, 'parent', 'child', 'the-project'))
- end
+ before do
+ 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
+ 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')
- 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')
+ child.update_attributes!(path: 'renamed')
- 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
+
+ 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')
- 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)
+ 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
end
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')
+ context 'hashed storage' do
+ let(:namespace) { create(:namespace) }
+ let!(:project) { create(:project_empty_repo, namespace: namespace) }
- parent.update_attributes!(path: 'renamed')
+ it_behaves_like 'namespace restrictions'
- 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
+ it "repository directory remains unchanged if path changed" do
+ before_disk_path = project.disk_path
+ namespace.update_attributes(path: namespace.full_path + '_new')
+
+ expect(before_disk_path).to eq(project.disk_path)
+ expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_truthy
end
end
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)
- project_in_parent_group = create(:project, :repository, namespace: parent, name: 'foo1')
- hashed_project_in_subgroup = create(:project, :repository, :hashed, namespace: subgroup, name: 'foo2')
- legacy_project_in_subgroup = create(:project, :repository, namespace: subgroup, name: 'foo3')
+ 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')
parent.update(path: 'mygroup_new')
@@ -260,38 +281,18 @@ describe Namespace do
end
describe '#rm_dir', 'callback' 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(:group, path: 'parent') }
- let(:child) { create(:group, 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) }
+ context 'legacy storage' do
+ let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: namespace) }
it 'renames its dirs when deleted' do
allow(GitlabShellWorker).to receive(:perform_in)
- child.destroy
+ namespace.destroy
expect(File.exist?(deleted_path_in_dir)).to be(true)
end
@@ -299,14 +300,57 @@ describe Namespace do
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
+ 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
+ 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
- it 'removes the exports folder' do
- expect(namespace).to receive(:remove_exports!)
+ 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)
- namespace.destroy
+ namespace.destroy
+
+ expect(File.exist?(deleted_path_in_dir)).to be(false)
+ end
+
+ it 'removes the exports folder' do
+ expect(namespace).to receive(:remove_exports!)
+
+ namespace.destroy
+ end
end
end
@@ -567,8 +611,8 @@ describe Namespace do
end
describe '#remove_exports' do
- let(:legacy_project) { create(:project, :with_export, namespace: namespace) }
- let(:hashed_project) { create(:project, :with_export, :hashed, namespace: namespace) }
+ let(:legacy_project) { create(:project, :with_export, :legacy_storage, namespace: namespace) }
+ let(:hashed_project) { create(:project, :with_export, namespace: namespace) }
let(:export_path) { Dir.mktmpdir('namespace_remove_exports_spec') }
let(:legacy_export) { legacy_project.export_project_path }
let(:hashed_export) { hashed_project.export_project_path }
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
new file mode 100644
index 00000000000..eda0e1da835
--- /dev/null
+++ b/spec/models/notification_recipient_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe NotificationRecipient do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:target) { create(:issue, project: project) }
+
+ subject(:recipient) { described_class.new(user, :watch, target: target, project: project) }
+
+ it 'denies access to a target when cross project access is denied' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(false)
+
+ expect(recipient.has_access?).to be_falsy
+ end
+end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 9d12f96c642..95713d8b85b 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -1,6 +1,10 @@
require 'spec_helper'
describe PagesDomain do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:pages_domain) { described_class.new }
+
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
@@ -64,19 +68,51 @@ describe PagesDomain do
end
end
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:verification_code) }
+ end
+
+ describe '#verification_code' do
+ subject { pages_domain.verification_code }
+
+ it 'is set automatically with 128 bits of SecureRandom data' do
+ expect(SecureRandom).to receive(:hex).with(16) { 'verification code' }
+
+ is_expected.to eq('verification code')
+ end
+ end
+
+ describe '#keyed_verification_code' do
+ subject { pages_domain.keyed_verification_code }
+
+ it { is_expected.to eq("gitlab-pages-verification-code=#{pages_domain.verification_code}") }
+ end
+
+ describe '#verification_domain' do
+ subject { pages_domain.verification_domain }
+
+ it { is_expected.to be_nil }
+
+ it 'is a well-known subdomain if the domain is present' do
+ pages_domain.domain = 'example.com'
+
+ is_expected.to eq('_gitlab-pages-verification-code.example.com')
+ end
+ end
+
describe '#url' do
subject { domain.url }
context 'without the certificate' do
let(:domain) { build(:pages_domain, certificate: '') }
- it { is_expected.to eq('http://my.domain.com') }
+ it { is_expected.to eq("http://#{domain.domain}") }
end
context 'with a certificate' do
let(:domain) { build(:pages_domain, :with_certificate) }
- it { is_expected.to eq('https://my.domain.com') }
+ it { is_expected.to eq("https://#{domain.domain}") }
end
end
@@ -154,4 +190,108 @@ describe PagesDomain do
# We test only existence of output, since the output is long
it { is_expected.not_to be_empty }
end
+
+ describe '#update_daemon' do
+ it 'runs when the domain is created' do
+ domain = build(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.save!
+ end
+
+ it 'runs when the domain is destroyed' do
+ domain = create(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.destroy!
+ end
+
+ it 'delegates to Projects::UpdatePagesConfigurationService' do
+ service = instance_double('Projects::UpdatePagesConfigurationService')
+ expect(Projects::UpdatePagesConfigurationService).to receive(:new) { service }
+ expect(service).to receive(:execute)
+
+ create(:pages_domain)
+ end
+
+ context 'configuration updates when attributes change' do
+ set(:project1) { create(:project) }
+ set(:project2) { create(:project) }
+ set(:domain) { create(:pages_domain) }
+
+ where(:attribute, :old_value, :new_value, :update_expected) do
+ now = Time.now
+ future = now + 1.day
+
+ :project | nil | :project1 | true
+ :project | :project1 | :project1 | false
+ :project | :project1 | :project2 | true
+ :project | :project1 | nil | true
+
+ # domain can't be set to nil
+ :domain | 'a.com' | 'a.com' | false
+ :domain | 'a.com' | 'b.com' | true
+
+ # verification_code can't be set to nil
+ :verification_code | 'foo' | 'foo' | false
+ :verification_code | 'foo' | 'bar' | false
+
+ :verified_at | nil | now | false
+ :verified_at | now | now | false
+ :verified_at | now | future | false
+ :verified_at | now | nil | false
+
+ :enabled_until | nil | now | true
+ :enabled_until | now | now | false
+ :enabled_until | now | future | false
+ :enabled_until | now | nil | true
+ end
+
+ with_them do
+ it 'runs if a relevant attribute has changed' do
+ a = old_value.is_a?(Symbol) ? send(old_value) : old_value
+ b = new_value.is_a?(Symbol) ? send(new_value) : new_value
+
+ domain.update!(attribute => a)
+
+ if update_expected
+ expect(domain).to receive(:update_daemon)
+ else
+ expect(domain).not_to receive(:update_daemon)
+ end
+
+ domain.update!(attribute => b)
+ end
+ end
+
+ context 'TLS configuration' do
+ set(:domain_with_tls) { create(:pages_domain, :with_key, :with_certificate) }
+
+ let(:cert1) { domain_with_tls.certificate }
+ let(:cert2) { cert1 + ' ' }
+ let(:key1) { domain_with_tls.key }
+ let(:key2) { key1 + ' ' }
+
+ it 'updates when added' do
+ expect(domain).to receive(:update_daemon)
+
+ domain.update!(key: key1, certificate: cert1)
+ end
+
+ it 'updates when changed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: key2, certificate: cert2)
+ end
+
+ it 'updates when removed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: nil, certificate: nil)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index ed17e019d42..6693e5783a5 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -223,8 +223,8 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with cluster for all environments without prometheus installed' do
context 'without environment supplied' do
- it 'raises PrometheusError because cluster was not found' do
- expect { service.client }.to raise_error(Gitlab::PrometheusError, /couldn't find cluster with Prometheus installed/)
+ it 'raises PrometheusClient::Error because cluster was not found' do
+ expect { service.client }.to raise_error(Gitlab::PrometheusClient::Error, /couldn't find cluster with Prometheus installed/)
end
end
@@ -242,8 +242,8 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with prod environment supplied' do
let!(:environment) { create(:environment, project: project, name: 'prod') }
- it 'raises PrometheusError because cluster was not found' do
- expect { service.client }.to raise_error(Gitlab::PrometheusError, /couldn't find cluster with Prometheus installed/)
+ it 'raises PrometheusClient::Error because cluster was not found' do
+ expect { service.client }.to raise_error(Gitlab::PrometheusClient::Error, /couldn't find cluster with Prometheus installed/)
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c6ca038a2ba..56c2d7b953e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1473,6 +1473,13 @@ describe Project do
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
+
+ it 'returns false when the repo is not empty' do
+ project.add_master(user)
+ expect(project).to receive(:empty_repo?).and_return(false)
+
+ expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
+ end
end
describe '#container_registry_url' do
@@ -2092,7 +2099,7 @@ describe Project do
context 'when the ref is a protected branch' do
before do
- create(:protected_branch, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -2100,7 +2107,7 @@ describe Project do
context 'when the ref is a protected tag' do
before do
- create(:protected_tag, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -2125,6 +2132,8 @@ describe Project do
context 'when the ref is a protected branch' do
before do
+ allow(project).to receive(:repository).and_call_original
+ allow(project).to receive_message_chain(:repository, :branch_exists?).and_return(true)
create(:protected_branch, name: 'ref', project: project)
end
@@ -2135,6 +2144,8 @@ describe Project do
context 'when the ref is a protected tag' do
before do
+ allow(project).to receive_message_chain(:repository, :branch_exists?).and_return(false)
+ allow(project).to receive_message_chain(:repository, :tag_exists?).and_return(true)
create(:protected_tag, name: 'ref', project: project)
end
@@ -2504,6 +2515,7 @@ describe Project do
end
describe '#remove_exports' do
+ let(:legacy_project) { create(:project, :legacy_storage, :with_export) }
let(:project) { create(:project, :with_export) }
it 'removes the exports directory for the project' do
@@ -2516,15 +2528,29 @@ describe Project do
expect(File.exist?(project.export_path)).to be_falsy
end
- it 'is a no-op when there is no namespace' do
+ it 'is a no-op on legacy projects when there is no namespace' do
+ export_path = legacy_project.export_path
+
+ legacy_project.update_column(:namespace_id, nil)
+
+ expect(FileUtils).not_to receive(:rm_rf).with(export_path)
+
+ legacy_project.remove_exports
+
+ expect(File.exist?(export_path)).to be_truthy
+ end
+
+ it 'runs on hashed storage projects when there is no namespace' do
export_path = project.export_path
+
project.update_column(:namespace_id, nil)
- expect(FileUtils).not_to receive(:rm_rf).with(export_path)
+ allow(FileUtils).to receive(:rm_rf).and_call_original
+ expect(FileUtils).to receive(:rm_rf).with(export_path).and_call_original
project.remove_exports
- expect(File.exist?(export_path)).to be_truthy
+ expect(File.exist?(export_path)).to be_falsy
end
it 'is run when the project is destroyed' do
@@ -2545,7 +2571,7 @@ describe Project do
end
context 'legacy storage' do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :legacy_storage) }
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project_storage) { project.send(:storage) }
@@ -2719,6 +2745,8 @@ describe Project do
let(:project) { create(:project, :repository, skip_disk_validation: true) }
let(:gitlab_shell) { Gitlab::Shell.new }
let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
+ let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
+ let(:hashed_path) { File.join(hashed_prefix, hash) }
before do
stub_application_setting(hashed_storage_enabled: true)
@@ -2744,14 +2772,12 @@ describe Project do
describe '#base_dir' do
it 'returns base_dir based on hash of project id' do
- expect(project.base_dir).to eq("@hashed/#{hash[0..1]}/#{hash[2..3]}")
+ expect(project.base_dir).to eq(hashed_prefix)
end
end
describe '#disk_path' do
it 'returns disk_path based on hash of project id' do
- hashed_path = "@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}"
-
expect(project.disk_path).to eq(hashed_path)
end
end
@@ -2760,7 +2786,7 @@ describe Project do
it 'delegates to gitlab_shell to ensure namespace is created' do
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
- expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, "@hashed/#{hash[0..1]}/#{hash[2..3]}")
+ expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, hashed_prefix)
project.ensure_storage_path_exists
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index a6d48e369ac..0bc07dc7a85 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -873,6 +873,18 @@ describe Repository do
expect(repository.license_key).to be_nil
end
+ it 'returns nil when the commit SHA does not exist' do
+ allow(repository.head_commit).to receive(:sha).and_return('1' * 40)
+
+ expect(repository.license_key).to be_nil
+ end
+
+ it 'returns nil when master does not exist' do
+ repository.rm_branch(user, 'master')
+
+ expect(repository.license_key).to be_nil
+ end
+
it 'returns the license key' do
repository.create_file(user, 'LICENSE',
Licensee::License.new('mit').content,
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index cb02d526a98..3531de244bd 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -496,6 +496,14 @@ describe User do
user2.update_tracked_fields!(request)
end.to change { user2.reload.current_sign_in_at }
end
+
+ it 'does not write if the DB is in read-only mode' do
+ expect(Gitlab::Database).to receive(:read_only?).and_return(true)
+
+ expect do
+ user.update_tracked_fields!(request)
+ end.not_to change { user.reload.current_sign_in_at }
+ end
end
shared_context 'user keys' do
@@ -893,6 +901,14 @@ describe User do
end
end
+ describe '.find_for_database_authentication' do
+ it 'strips whitespace from login' do
+ user = create(:user)
+
+ expect(described_class.find_for_database_authentication({ login: " #{user.username} " })).to eq user
+ end
+ end
+
describe '.find_by_any_email' do
it 'finds by primary email' do
user = create(:user, email: 'foo@example.com')
@@ -1586,14 +1602,37 @@ describe User do
describe '#authorized_groups' do
let!(:user) { create(:user) }
let!(:private_group) { create(:group) }
+ let!(:child_group) { create(:group, parent: private_group) }
+
+ let!(:project_group) { create(:group) }
+ let!(:project) { create(:project, group: project_group) }
before do
private_group.add_user(user, Gitlab::Access::MASTER)
+ project.add_master(user)
end
subject { user.authorized_groups }
- it { is_expected.to eq([private_group]) }
+ it { is_expected.to contain_exactly private_group, project_group }
+ end
+
+ describe '#membership_groups' do
+ let!(:user) { create(:user) }
+ let!(:parent_group) { create(:group) }
+ let!(:child_group) { create(:group, parent: parent_group) }
+
+ before do
+ parent_group.add_user(user, Gitlab::Access::MASTER)
+ end
+
+ subject { user.membership_groups }
+
+ if Group.supports_nested_groups?
+ it { is_expected.to contain_exactly parent_group, child_group }
+ else
+ it { is_expected.to contain_exactly parent_group }
+ end
end
describe '#authorized_projects', :delete do