diff options
author | Kamil Trzciński <ayufan@ayufan.eu> | 2017-10-07 08:07:33 +0000 |
---|---|---|
committer | Kamil Trzciński <ayufan@ayufan.eu> | 2017-10-07 08:07:33 +0000 |
commit | 5ee20b637553dcb70590edfa30b81d972de37170 (patch) | |
tree | 7079e097120fc25438b411c6162a1ae81656c129 /spec/models | |
parent | c0cfc9ebd26583c444f2cce1a23f939bfa7d8969 (diff) | |
parent | d4f3963e98397a0aee4b7a65c76ae404300b41d1 (diff) | |
download | gitlab-ce-5ee20b637553dcb70590edfa30b81d972de37170.tar.gz |
Merge branch 'master' into '37970-ci-sections-tracking'
# Conflicts:
# db/schema.rb
Diffstat (limited to 'spec/models')
-rw-r--r-- | spec/models/ci/artifact_blob_spec.rb | 50 | ||||
-rw-r--r-- | spec/models/ci/pipeline_spec.rb | 10 | ||||
-rw-r--r-- | spec/models/concerns/has_status_spec.rb | 12 | ||||
-rw-r--r-- | spec/models/diff_note_spec.rb | 40 | ||||
-rw-r--r-- | spec/models/email_spec.rb | 29 | ||||
-rw-r--r-- | spec/models/gcp/cluster_spec.rb | 240 | ||||
-rw-r--r-- | spec/models/gpg_key_spec.rb | 42 | ||||
-rw-r--r-- | spec/models/gpg_key_subkey_spec.rb | 15 | ||||
-rw-r--r-- | spec/models/gpg_signature_spec.rb | 26 | ||||
-rw-r--r-- | spec/models/namespace_spec.rb | 28 | ||||
-rw-r--r-- | spec/models/note_spec.rb | 50 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 15 | ||||
-rw-r--r-- | spec/models/repository_spec.rb | 4 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 206 |
14 files changed, 721 insertions, 46 deletions
diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb index a10a8af5303..d5ba088af53 100644 --- a/spec/models/ci/artifact_blob_spec.rb +++ b/spec/models/ci/artifact_blob_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' describe Ci::ArtifactBlob do - let(:build) { create(:ci_build, :artifacts) } + set(:project) { create(:project, :public) } + set(:build) { create(:ci_build, :artifacts, project: project) } let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') } subject { described_class.new(entry) } @@ -41,4 +42,51 @@ describe Ci::ArtifactBlob do expect(subject.external_storage).to eq(:build_artifact) end end + + describe '#external_url' do + before do + allow(Gitlab.config.pages).to receive(:enabled).and_return(true) + allow(Gitlab.config.pages).to receive(:artifacts_server).and_return(true) + end + + context '.gif extension' do + it 'returns nil' do + expect(subject.external_url(build.project, build)).to be_nil + end + end + + context 'txt extensions' do + let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/doc_sample.txt') } + + it 'returns a URL' do + url = subject.external_url(build.project, build) + + expect(url).not_to be_nil + expect(url).to start_with("http") + expect(url).to match Gitlab.config.pages.host + expect(url).to end_with(entry.path) + end + end + end + + describe '#external_link?' do + before do + allow(Gitlab.config.pages).to receive(:enabled).and_return(true) + allow(Gitlab.config.pages).to receive(:artifacts_server).and_return(true) + end + + context 'gif extensions' do + it 'returns false' do + expect(subject.external_link?(build)).to be false + end + end + + context 'txt extensions' do + let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/doc_sample.txt') } + + it 'returns true' do + expect(subject.external_link?(build)).to be true + end + end + end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 9c1e460ab20..2c9e7013b77 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -238,7 +238,7 @@ describe Ci::Pipeline, :mailer do describe '#stage_seeds' do let(:pipeline) do - create(:ci_pipeline, config: { rspec: { script: 'rake' } }) + build(:ci_pipeline, config: { rspec: { script: 'rake' } }) end it 'returns preseeded stage seeds object' do @@ -247,6 +247,14 @@ describe Ci::Pipeline, :mailer do end end + describe '#seeds_size' do + let(:pipeline) { build(:ci_pipeline_with_one_job) } + + it 'returns number of jobs in stage seeds' do + expect(pipeline.seeds_size).to eq 1 + end + end + describe '#legacy_stages' do subject { pipeline.legacy_stages } diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb index a38f2553eb1..6866b43432c 100644 --- a/spec/models/concerns/has_status_spec.rb +++ b/spec/models/concerns/has_status_spec.rb @@ -231,6 +231,18 @@ describe HasStatus do end end + describe '.alive' do + subject { CommitStatus.alive } + + %i[running pending created].each do |status| + it_behaves_like 'containing the job', status + end + + %i[failed success].each do |status| + it_behaves_like 'not containing the job', status + end + end + describe '.created_or_pending' do subject { CommitStatus.created_or_pending } diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb index 4aa9ec789a3..eb0a3e9e0d3 100644 --- a/spec/models/diff_note_spec.rb +++ b/spec/models/diff_note_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe DiffNote do include RepoHelpers - let(:merge_request) { create(:merge_request) } + let!(:merge_request) { create(:merge_request) } let(:project) { merge_request.project } let(:commit) { project.commit(sample_commit.id) } @@ -98,14 +98,14 @@ describe DiffNote do diff_line = subject.diff_line expect(diff_line.added?).to be true - expect(diff_line.new_line).to eq(position.new_line) + expect(diff_line.new_line).to eq(position.formatter.new_line) expect(diff_line.text).to eq("+ vars = {") end end describe "#line_code" do it "returns the correct line code" do - line_code = Gitlab::Diff::LineCode.generate(position.file_path, position.new_line, 15) + line_code = Gitlab::Diff::LineCode.generate(position.file_path, position.formatter.new_line, 15) expect(subject.line_code).to eq(line_code) end @@ -255,4 +255,38 @@ describe DiffNote do end end end + + describe "image diff notes" do + let(:path) { "files/images/any_image.png" } + + let!(:position) do + Gitlab::Diff::Position.new( + old_path: path, + new_path: path, + width: 10, + height: 10, + x: 1, + y: 1, + diff_refs: merge_request.diff_refs, + position_type: "image" + ) + end + + describe "validations" do + subject { build(:diff_note_on_merge_request, project: project, position: position, noteable: merge_request) } + + it { is_expected.not_to validate_presence_of(:line_code) } + + it "does not validate diff line" do + diff_line = subject.diff_line + + expect(diff_line).to be nil + expect(subject).to be_valid + end + end + + it "returns true for on_image?" do + expect(subject.on_image?).to be_truthy + end + end end diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index 1d6fabe48b1..b32dd31ae6d 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -11,4 +11,33 @@ describe Email do expect(described_class.new(email: ' inFO@exAMPLe.com ').email) .to eq 'info@example.com' end + + describe '#update_invalid_gpg_signatures' do + let(:user) do + create(:user, email: 'tula.torphy@abshire.ca').tap do |user| + user.skip_reconfirmation! + end + end + let(:user) { create(:user) } + + it 'synchronizes the gpg keys when the email is updated' do + email = user.emails.create(email: 'new@email.com') + + expect(user).to receive(:update_invalid_gpg_signatures) + + email.confirm + end + end + + describe 'scopes' do + let(:user) { create(:user) } + + it 'scopes confirmed emails' do + create(:email, :confirmed, user: user) + create(:email, user: user) + + expect(user.emails.count).to eq 2 + expect(user.emails.confirmed.count).to eq 1 + end + end end diff --git a/spec/models/gcp/cluster_spec.rb b/spec/models/gcp/cluster_spec.rb new file mode 100644 index 00000000000..350fbc257d9 --- /dev/null +++ b/spec/models/gcp/cluster_spec.rb @@ -0,0 +1,240 @@ +require 'spec_helper' + +describe Gcp::Cluster do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:service) } + + it { is_expected.to validate_presence_of(:gcp_cluster_zone) } + + describe '#default_value_for' do + let(:cluster) { described_class.new } + + it { expect(cluster.gcp_cluster_zone).to eq('us-central1-a') } + it { expect(cluster.gcp_cluster_size).to eq(3) } + it { expect(cluster.gcp_machine_type).to eq('n1-standard-4') } + end + + describe '#validates' do + subject { cluster.valid? } + + context 'when validates gcp_project_id' do + let(:cluster) { build(:gcp_cluster, gcp_project_id: gcp_project_id) } + + context 'when valid' do + let(:gcp_project_id) { 'gcp-project-12345' } + + it { is_expected.to be_truthy } + end + + context 'when empty' do + let(:gcp_project_id) { '' } + + it { is_expected.to be_falsey } + end + + context 'when too long' do + let(:gcp_project_id) { 'A' * 64 } + + it { is_expected.to be_falsey } + end + + context 'when includes abnormal character' do + let(:gcp_project_id) { '!!!!!!' } + + it { is_expected.to be_falsey } + end + end + + context 'when validates gcp_cluster_name' do + let(:cluster) { build(:gcp_cluster, gcp_cluster_name: gcp_cluster_name) } + + context 'when valid' do + let(:gcp_cluster_name) { 'test-cluster' } + + it { is_expected.to be_truthy } + end + + context 'when empty' do + let(:gcp_cluster_name) { '' } + + it { is_expected.to be_falsey } + end + + context 'when too long' do + let(:gcp_cluster_name) { 'A' * 64 } + + it { is_expected.to be_falsey } + end + + context 'when includes abnormal character' do + let(:gcp_cluster_name) { '!!!!!!' } + + it { is_expected.to be_falsey } + end + end + + context 'when validates gcp_cluster_size' do + let(:cluster) { build(:gcp_cluster, gcp_cluster_size: gcp_cluster_size) } + + context 'when valid' do + let(:gcp_cluster_size) { 1 } + + it { is_expected.to be_truthy } + end + + context 'when zero' do + let(:gcp_cluster_size) { 0 } + + it { is_expected.to be_falsey } + end + end + + context 'when validates project_namespace' do + let(:cluster) { build(:gcp_cluster, project_namespace: project_namespace) } + + context 'when valid' do + let(:project_namespace) { 'default-namespace' } + + it { is_expected.to be_truthy } + end + + context 'when empty' do + let(:project_namespace) { '' } + + it { is_expected.to be_truthy } + end + + context 'when too long' do + let(:project_namespace) { 'A' * 64 } + + it { is_expected.to be_falsey } + end + + context 'when includes abnormal character' do + let(:project_namespace) { '!!!!!!' } + + it { is_expected.to be_falsey } + end + end + + context 'when validates restrict_modification' do + let(:cluster) { create(:gcp_cluster) } + + before do + cluster.make_creating! + end + + context 'when created' do + before do + cluster.make_created! + end + + it { is_expected.to be_truthy } + end + + context 'when creating' do + it { is_expected.to be_falsey } + end + end + end + + describe '#state_machine' do + let(:cluster) { build(:gcp_cluster) } + + context 'when transits to created state' do + before do + cluster.gcp_token = 'tmp' + cluster.gcp_operation_id = 'tmp' + cluster.make_created! + end + + it 'nullify gcp_token and gcp_operation_id' do + expect(cluster.gcp_token).to be_nil + expect(cluster.gcp_operation_id).to be_nil + expect(cluster).to be_created + end + end + + context 'when transits to errored state' do + let(:reason) { 'something wrong' } + + before do + cluster.make_errored!(reason) + end + + it 'sets status_reason' do + expect(cluster.status_reason).to eq(reason) + expect(cluster).to be_errored + end + end + end + + describe '#project_namespace_placeholder' do + subject { cluster.project_namespace_placeholder } + + let(:cluster) { create(:gcp_cluster) } + + it 'returns a placeholder' do + is_expected.to eq("#{cluster.project.path}-#{cluster.project.id}") + end + end + + describe '#on_creation?' do + subject { cluster.on_creation? } + + let(:cluster) { create(:gcp_cluster) } + + context 'when status is creating' do + before do + cluster.make_creating! + end + + it { is_expected.to be_truthy } + end + + context 'when status is created' do + before do + cluster.make_created! + end + + it { is_expected.to be_falsey } + end + end + + describe '#api_url' do + subject { cluster.api_url } + + let(:cluster) { create(:gcp_cluster, :created_on_gke) } + let(:api_url) { 'https://' + cluster.endpoint } + + it { is_expected.to eq(api_url) } + end + + describe '#restrict_modification' do + subject { cluster.restrict_modification } + + let(:cluster) { create(:gcp_cluster) } + + context 'when status is created' do + before do + cluster.make_created! + end + + it { is_expected.to be_truthy } + end + + context 'when status is creating' do + before do + cluster.make_creating! + end + + it { is_expected.to be_falsey } + + it 'sets error' do + is_expected.to be_falsey + expect(cluster.errors).not_to be_empty + end + end + end +end diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb index 4a4d079b721..33e6f1de3d1 100644 --- a/spec/models/gpg_key_spec.rb +++ b/spec/models/gpg_key_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' describe GpgKey do describe "associations" do it { is_expected.to belong_to(:user) } + it { is_expected.to have_many(:subkeys) } end describe "validation" do @@ -38,6 +39,14 @@ describe GpgKey do expect(gpg_key.primary_keyid).to eq GpgHelpers::User1.primary_keyid end end + + describe 'generate_subkeys' do + it 'extracts the subkeys from the gpg key' do + gpg_key = create(:gpg_key, key: GpgHelpers::User1.public_key_with_extra_signing_key) + + expect(gpg_key.subkeys.count).to eq(2) + end + end end describe '#key=' do @@ -90,11 +99,20 @@ describe GpgKey do it 'email is verified if the user has the matching email' do user = create :user, email: 'bette.cartwright@example.com' gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + create :email, user: user + user.reload expect(gpg_key.emails_with_verified_status).to eq( 'bette.cartwright@example.com' => true, 'bette.cartwright@example.net' => false ) + + create :email, :confirmed, user: user, email: 'bette.cartwright@example.net' + user.reload + expect(gpg_key.emails_with_verified_status).to eq( + 'bette.cartwright@example.com' => true, + 'bette.cartwright@example.net' => true + ) end end @@ -173,5 +191,29 @@ describe GpgKey do expect(unrelated_gpg_key.destroyed?).to be false end + + it 'deletes all the associated subkeys' do + gpg_key = create :gpg_key, key: GpgHelpers::User3.public_key + + expect(gpg_key.subkeys).to be_present + + gpg_key.revoke + + expect(gpg_key.subkeys(true)).to be_blank + end + + it 'invalidates all signatures associated to the subkeys' do + gpg_key = create :gpg_key, key: GpgHelpers::User3.public_key + gpg_key_subkey = gpg_key.subkeys.last + gpg_signature = create :gpg_signature, verification_status: :verified, gpg_key: gpg_key_subkey + + gpg_key.revoke + + expect(gpg_signature.reload).to have_attributes( + verification_status: 'unknown_key', + gpg_key: nil, + gpg_key_subkey: nil + ) + end end end diff --git a/spec/models/gpg_key_subkey_spec.rb b/spec/models/gpg_key_subkey_spec.rb new file mode 100644 index 00000000000..3c86837f47f --- /dev/null +++ b/spec/models/gpg_key_subkey_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +describe GpgKeySubkey do + subject { build(:gpg_key_subkey) } + + describe 'associations' do + it { is_expected.to belong_to(:gpg_key) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:gpg_key_id) } + it { is_expected.to validate_presence_of(:fingerprint) } + it { is_expected.to validate_presence_of(:keyid) } + end +end diff --git a/spec/models/gpg_signature_spec.rb b/spec/models/gpg_signature_spec.rb index c58fd46762a..db033016c37 100644 --- a/spec/models/gpg_signature_spec.rb +++ b/spec/models/gpg_signature_spec.rb @@ -1,9 +1,13 @@ require 'rails_helper' RSpec.describe GpgSignature do + let(:gpg_key) { create(:gpg_key) } + let(:gpg_key_subkey) { create(:gpg_key_subkey) } + describe 'associations' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:gpg_key) } + it { is_expected.to belong_to(:gpg_key_subkey) } end describe 'validation' do @@ -25,4 +29,26 @@ RSpec.describe GpgSignature do gpg_signature.commit end end + + describe '#gpg_key=' do + it 'supports the assignment of a GpgKey' do + gpg_signature = create(:gpg_signature, gpg_key: gpg_key) + + expect(gpg_signature.gpg_key).to be_an_instance_of(GpgKey) + end + + it 'supports the assignment of a GpgKeySubkey' do + gpg_signature = create(:gpg_signature, gpg_key: gpg_key_subkey) + + expect(gpg_signature.gpg_key).to be_an_instance_of(GpgKeySubkey) + end + + it 'clears gpg_key and gpg_key_subkey_id when passing nil' do + gpg_signature = create(:gpg_signature, gpg_key: gpg_key_subkey) + gpg_signature.update_attribute(:gpg_key, nil) + + expect(gpg_signature.gpg_key_id).to be_nil + expect(gpg_signature.gpg_key_subkey_id).to be_nil + end + end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 81d5ab7a6d3..3ea614776ca 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -152,22 +152,24 @@ describe Namespace do end describe '#move_dir' do + let(:namespace) { create(:namespace) } + let!(:project) { create(:project_empty_repo, namespace: namespace) } + before do - @namespace = create :namespace - @project = create(:project_empty_repo, namespace: @namespace) - allow(@namespace).to receive(:path_changed?).and_return(true) + 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") + 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 + 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 @@ -177,14 +179,14 @@ describe Namespace do stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: ['tag']) - create(:project, namespace: @namespace, container_repositories: [container_repository]) + create(: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') + allow(namespace).to receive(:path_was).and_return(namespace.path) + allow(namespace).to receive(:path).and_return('new_path') end it 'raises an error about not movable project' do - expect { @namespace.move_dir }.to raise_error(/Namespace cannot be moved/) + expect { namespace.move_dir }.to raise_error(/Namespace cannot be moved/) end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index b214074fdce..1ecb50586c7 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -314,6 +314,56 @@ describe Note do expect(subject[active_diff_note1.line_code].first.id).to eq(active_diff_note1.discussion_id) expect(subject[active_diff_note3.line_code].first.id).to eq(active_diff_note3.discussion_id) end + + context 'with image discussions' do + let(:merge_request2) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, title: "Added images and changes") } + let(:image_path) { "files/images/ee_repo_logo.png" } + let(:text_path) { "bar/branch-test.txt" } + let!(:image_note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request2, position: image_position) } + let!(:text_note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request2, position: text_position) } + + let(:image_position) do + Gitlab::Diff::Position.new( + old_path: image_path, + new_path: image_path, + width: 100, + height: 100, + x: 1, + y: 1, + position_type: "image", + diff_refs: merge_request2.diff_refs + ) + end + + let(:text_position) do + Gitlab::Diff::Position.new( + old_path: text_path, + new_path: text_path, + old_line: nil, + new_line: 2, + position_type: "text", + diff_refs: merge_request2.diff_refs + ) + end + + it "groups image discussions by file identifier" do + diff_discussion = DiffDiscussion.new([image_note]) + + discussions = merge_request2.notes.grouped_diff_discussions + + expect(discussions.size).to eq(2) + expect(discussions[image_note.diff_file.new_path]).to include(diff_discussion) + end + + it "groups text discussions by line code" do + diff_discussion = DiffDiscussion.new([text_note]) + + discussions = merge_request2.notes.grouped_diff_discussions + + expect(discussions.size).to eq(2) + expect(discussions[text_note.line_code]).to include(diff_discussion) + end + end end context 'diff discussions for older diff refs' do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1c04cb8cb5f..760ea7da322 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -77,6 +77,7 @@ describe Project do it { is_expected.to have_many(:uploads).dependent(:destroy) } it { is_expected.to have_many(:pipeline_schedules) } it { is_expected.to have_many(:members_and_requesters) } + it { is_expected.to have_one(:cluster) } context 'after initialized' do it "has a project_feature" do @@ -422,20 +423,10 @@ describe Project do end describe '#repository_storage_path' do - let(:project) { create(:project, repository_storage: 'custom') } - - before do - FileUtils.mkdir('tmp/tests/custom_repositories') - storages = { 'custom' => { 'path' => 'tmp/tests/custom_repositories' } } - allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) - end - - after do - FileUtils.rm_rf('tmp/tests/custom_repositories') - end + let(:project) { create(:project) } it 'returns the repository storage path' do - expect(project.repository_storage_path).to eq('tmp/tests/custom_repositories') + expect(Dir.exist?(project.repository_storage_path)).to be(true) end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 7156c1b7aa8..5764c451b67 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -634,6 +634,10 @@ describe Repository do end describe '#fetch_ref' do + # Setting the var here, sidesteps the stub that makes gitaly raise an error + # before the actual test call + set(:broken_repository) { create(:project, :broken_storage).repository } + describe 'when storage is broken', broken_storage: true do it 'should raise a storage error' do expect_to_raise_storage_error do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 62890dd5002..995211845ce 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -360,9 +360,22 @@ describe User do expect(external_user.projects_limit).to be 0 end end + + describe '#check_for_verified_email' do + let(:user) { create(:user) } + let(:secondary) { create(:email, :confirmed, email: 'secondary@example.com', user: user) } + + it 'allows a verfied secondary email to be used as the primary without needing reconfirmation' do + user.update_attributes!(email: secondary.email) + user.reload + expect(user.email).to eq secondary.email + expect(user.unconfirmed_email).to eq nil + expect(user.confirmed?).to be_truthy + end + end end - describe 'after update hook' do + describe 'after commit hook' do describe '.update_invalid_gpg_signatures' do let(:user) do create(:user, email: 'tula.torphy@abshire.ca').tap do |user| @@ -376,10 +389,50 @@ describe User do end it 'synchronizes the gpg keys when the email is updated' do - expect(user).to receive(:update_invalid_gpg_signatures) + expect(user).to receive(:update_invalid_gpg_signatures).at_most(:twice) user.update_attributes!(email: 'shawnee.ritchie@denesik.com') end end + + describe '#update_emails_with_primary_email' do + before do + @user = create(:user, email: 'primary@example.com').tap do |user| + user.skip_reconfirmation! + end + @secondary = create :email, email: 'secondary@example.com', user: @user + @user.reload + end + + it 'gets called when email updated' do + expect(@user).to receive(:update_emails_with_primary_email) + + @user.update_attributes!(email: 'new_primary@example.com') + end + + it 'adds old primary to secondary emails when secondary is a new email ' do + @user.update_attributes!(email: 'new_primary@example.com') + @user.reload + + expect(@user.emails.count).to eq 2 + expect(@user.emails.pluck(:email)).to match_array([@secondary.email, 'primary@example.com']) + end + + it 'adds old primary to secondary emails if secondary is becoming a primary' do + @user.update_attributes!(email: @secondary.email) + @user.reload + + expect(@user.emails.count).to eq 1 + expect(@user.emails.first.email).to eq 'primary@example.com' + end + + it 'transfers old confirmation values into new secondary' do + @user.update_attributes!(email: @secondary.email) + @user.reload + + expect(@user.emails.count).to eq 1 + expect(@user.emails.first.confirmed_at).not_to eq nil + end + end end describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do @@ -467,6 +520,7 @@ describe User do describe '#generate_password' do it "does not generate password by default" do user = create(:user, password: 'abcdefghe') + expect(user.password).to eq('abcdefghe') end end @@ -474,6 +528,7 @@ describe User do describe 'authentication token' do it "has authentication token" do user = create(:user) + expect(user.authentication_token).not_to be_blank end end @@ -481,6 +536,7 @@ describe User do describe 'ensure incoming email token' do it 'has incoming email token' do user = create(:user) + expect(user.incoming_email_token).not_to be_blank end end @@ -523,6 +579,7 @@ describe User do it 'ensures an rss token on read' do user = create(:user, rss_token: nil) rss_token = user.rss_token + expect(rss_token).not_to be_blank expect(user.reload.rss_token).to eq rss_token end @@ -633,6 +690,7 @@ describe User do it "blocks user" do user.block + expect(user.blocked?).to be_truthy end end @@ -966,6 +1024,7 @@ describe User do it 'is case-insensitive' do user = create(:user, username: 'JohnDoe') + expect(described_class.find_by_username('JOHNDOE')).to eq user end end @@ -978,6 +1037,7 @@ describe User do it 'is case-insensitive' do user = create(:user, username: 'JohnDoe') + expect(described_class.find_by_username!('JOHNDOE')).to eq user end end @@ -1067,11 +1127,13 @@ describe User do it 'is true if avatar is image' do user.update_attribute(:avatar, 'uploads/avatar.png') + expect(user.avatar_type).to be_truthy end it 'is false if avatar is html page' do user.update_attribute(:avatar, 'uploads/avatar.html') + expect(user.avatar_type).to eq(['only images allowed']) end end @@ -1094,6 +1156,50 @@ describe User do end end + describe '#all_emails' do + let(:user) { create(:user) } + + it 'returns all emails' do + email_confirmed = create :email, user: user, confirmed_at: Time.now + email_unconfirmed = create :email, user: user + user.reload + + expect(user.all_emails).to match_array([user.email, email_unconfirmed.email, email_confirmed.email]) + end + end + + describe '#verified_emails' do + let(:user) { create(:user) } + + it 'returns only confirmed emails' do + email_confirmed = create :email, user: user, confirmed_at: Time.now + create :email, user: user + user.reload + + expect(user.verified_emails).to match_array([user.email, email_confirmed.email]) + end + end + + describe '#verified_email?' do + let(:user) { create(:user) } + + it 'returns true when the email is verified/confirmed' do + email_confirmed = create :email, user: user, confirmed_at: Time.now + create :email, user: user + user.reload + + expect(user.verified_email?(user.email)).to be_truthy + expect(user.verified_email?(email_confirmed.email.titlecase)).to be_truthy + end + + it 'returns false when the email is not verified/confirmed' do + email_unconfirmed = create :email, user: user + user.reload + + expect(user.verified_email?(email_unconfirmed.email)).to be_falsy + end + end + describe '#requires_ldap_check?' do let(:user) { described_class.new } @@ -1101,6 +1207,7 @@ describe User do # Create a condition which would otherwise cause 'true' to be returned allow(user).to receive(:ldap_user?).and_return(true) user.last_credential_check_at = nil + expect(user.requires_ldap_check?).to be_falsey end @@ -1111,6 +1218,7 @@ describe User do it 'is false for non-LDAP users' do allow(user).to receive(:ldap_user?).and_return(false) + expect(user.requires_ldap_check?).to be_falsey end @@ -1121,11 +1229,13 @@ describe User do it 'is true when the user has never had an LDAP check before' do user.last_credential_check_at = nil + expect(user.requires_ldap_check?).to be_truthy end it 'is true when the last LDAP check happened over 1 hour ago' do user.last_credential_check_at = 2.hours.ago + expect(user.requires_ldap_check?).to be_truthy end end @@ -1136,16 +1246,19 @@ describe User do describe '#ldap_user?' do it 'is true if provider name starts with ldap' do user = create(:omniauth_user, provider: 'ldapmain') + expect(user.ldap_user?).to be_truthy end it 'is false for other providers' do user = create(:omniauth_user, provider: 'other-provider') + expect(user.ldap_user?).to be_falsey end it 'is false if no extern_uid is provided' do user = create(:omniauth_user, extern_uid: nil) + expect(user.ldap_user?).to be_falsey end end @@ -1153,6 +1266,7 @@ describe User do describe '#ldap_identity' do it 'returns ldap identity' do user = create :omniauth_user + expect(user.ldap_identity.provider).not_to be_empty end end @@ -1162,6 +1276,7 @@ describe User do it 'blocks user flaging the action caming from ldap' do user.ldap_block + expect(user.blocked?).to be_truthy expect(user.ldap_blocked?).to be_truthy end @@ -1234,18 +1349,22 @@ describe User do expect(user.starred?(project2)).to be_falsey star1 = UsersStarProject.create!(project: project1, user: user) + expect(user.starred?(project1)).to be_truthy expect(user.starred?(project2)).to be_falsey star2 = UsersStarProject.create!(project: project2, user: user) + expect(user.starred?(project1)).to be_truthy expect(user.starred?(project2)).to be_truthy star1.destroy + expect(user.starred?(project1)).to be_falsey expect(user.starred?(project2)).to be_truthy star2.destroy + expect(user.starred?(project1)).to be_falsey expect(user.starred?(project2)).to be_falsey end @@ -1257,9 +1376,13 @@ describe User do project = create(:project, :public) expect(user.starred?(project)).to be_falsey + user.toggle_star(project) + expect(user.starred?(project)).to be_truthy + user.toggle_star(project) + expect(user.starred?(project)).to be_falsey end end @@ -1438,9 +1561,11 @@ describe User do user = create(:user) member = group.add_developer(user) + expect(user.authorized_projects).to include(project) member.destroy + expect(user.authorized_projects).not_to include(project) end @@ -1461,9 +1586,11 @@ describe User do project = create(:project, :private, namespace: user1.namespace) project.team << [user2, Gitlab::Access::DEVELOPER] + expect(user2.authorized_projects).to include(project) project.destroy + expect(user2.authorized_projects).not_to include(project) end @@ -1473,9 +1600,11 @@ describe User do user = create(:user) group.add_developer(user) + expect(user.authorized_projects).to include(project) group.destroy + expect(user.authorized_projects).not_to include(project) end end @@ -2019,7 +2148,9 @@ describe User do it 'creates the namespace' do expect(user.namespace).to be_nil + user.save! + expect(user.namespace).not_to be_nil end end @@ -2040,11 +2171,13 @@ describe User do it 'updates the namespace name' do user.update_attributes!(username: new_username) + expect(user.namespace.name).to eq(new_username) end it 'updates the namespace path' do user.update_attributes!(username: new_username) + expect(user.namespace.path).to eq(new_username) end @@ -2058,6 +2191,7 @@ describe User do it 'adds the namespace errors to the user' do user.update_attributes(username: new_username) + expect(user.errors.full_messages.first).to eq('Namespace name has already been taken') end end @@ -2074,56 +2208,49 @@ describe User do end end - describe '#verified_email?' do - it 'returns true when the email is the primary email' do - user = build :user, email: 'email@example.com' - - expect(user.verified_email?('email@example.com')).to be true - end - - it 'returns false when the email is not the primary email' do - user = build :user, email: 'email@example.com' - - expect(user.verified_email?('other_email@example.com')).to be false - end - end - describe '#sync_attribute?' do let(:user) { described_class.new } context 'oauth user' do it 'returns true if name can be synced' do stub_omniauth_setting(sync_profile_attributes: %w(name location)) + expect(user.sync_attribute?(:name)).to be_truthy end it 'returns true if email can be synced' do stub_omniauth_setting(sync_profile_attributes: %w(name email)) + expect(user.sync_attribute?(:email)).to be_truthy end it 'returns true if location can be synced' do stub_omniauth_setting(sync_profile_attributes: %w(location email)) + expect(user.sync_attribute?(:email)).to be_truthy end it 'returns false if name can not be synced' do stub_omniauth_setting(sync_profile_attributes: %w(location email)) + expect(user.sync_attribute?(:name)).to be_falsey end it 'returns false if email can not be synced' do stub_omniauth_setting(sync_profile_attributes: %w(location email)) + expect(user.sync_attribute?(:name)).to be_falsey end it 'returns false if location can not be synced' do stub_omniauth_setting(sync_profile_attributes: %w(location email)) + expect(user.sync_attribute?(:name)).to be_falsey end it 'returns true for all syncable attributes if all syncable attributes can be synced' do stub_omniauth_setting(sync_profile_attributes: true) + expect(user.sync_attribute?(:name)).to be_truthy expect(user.sync_attribute?(:email)).to be_truthy expect(user.sync_attribute?(:location)).to be_truthy @@ -2139,6 +2266,7 @@ describe User do context 'ldap user' do it 'returns true for email if ldap user' do allow(user).to receive(:ldap_user?).and_return(true) + expect(user.sync_attribute?(:name)).to be_falsey expect(user.sync_attribute?(:email)).to be_truthy expect(user.sync_attribute?(:location)).to be_falsey @@ -2147,10 +2275,56 @@ describe User do it 'returns true for email and location if ldap user and location declared as syncable' do allow(user).to receive(:ldap_user?).and_return(true) stub_omniauth_setting(sync_profile_attributes: %w(location)) + expect(user.sync_attribute?(:name)).to be_falsey expect(user.sync_attribute?(:email)).to be_truthy expect(user.sync_attribute?(:location)).to be_truthy end end end + + describe '#confirm_deletion_with_password?' do + where( + password_automatically_set: [true, false], + ldap_user: [true, false], + password_authentication_disabled: [true, false] + ) + + with_them do + let!(:user) { create(:user, password_automatically_set: password_automatically_set) } + let!(:identity) { create(:identity, user: user) if ldap_user } + + # Only confirm deletion with password if all inputs are false + let(:expected) { !(password_automatically_set || ldap_user || password_authentication_disabled) } + + before do + stub_application_setting(password_authentication_enabled: !password_authentication_disabled) + end + + it 'returns false unless all inputs are true' do + expect(user.confirm_deletion_with_password?).to eq(expected) + end + end + end + + describe '#delete_async' do + let(:user) { create(:user) } + let(:deleted_by) { create(:user) } + + it 'blocks the user then schedules them for deletion if a hard delete is specified' do + expect(DeleteUserWorker).to receive(:perform_async).with(deleted_by.id, user.id, hard_delete: true) + + user.delete_async(deleted_by: deleted_by, params: { hard_delete: true }) + + expect(user).to be_blocked + end + + it 'schedules user for deletion without blocking them' do + expect(DeleteUserWorker).to receive(:perform_async).with(deleted_by.id, user.id, {}) + + user.delete_async(deleted_by: deleted_by) + + expect(user).not_to be_blocked + end + end end |