diff options
Diffstat (limited to 'spec/models')
-rw-r--r-- | spec/models/blob_spec.rb | 124 | ||||
-rw-r--r-- | spec/models/blob_viewer/base_spec.rb | 6 | ||||
-rw-r--r-- | spec/models/ci/artifact_blob_spec.rb | 44 | ||||
-rw-r--r-- | spec/models/ci/trigger_schedule_spec.rb | 32 | ||||
-rw-r--r-- | spec/models/commit_spec.rb | 6 | ||||
-rw-r--r-- | spec/models/concerns/cache_markdown_field_spec.rb | 6 | ||||
-rw-r--r-- | spec/models/diff_discussion_spec.rb | 83 | ||||
-rw-r--r-- | spec/models/diff_note_spec.rb | 52 | ||||
-rw-r--r-- | spec/models/group_spec.rb | 26 | ||||
-rw-r--r-- | spec/models/issue_spec.rb | 21 | ||||
-rw-r--r-- | spec/models/legacy_diff_discussion_spec.rb | 22 | ||||
-rw-r--r-- | spec/models/merge_request_spec.rb | 19 | ||||
-rw-r--r-- | spec/models/namespace_spec.rb | 8 | ||||
-rw-r--r-- | spec/models/network/graph_spec.rb | 21 | ||||
-rw-r--r-- | spec/models/note_spec.rb | 86 | ||||
-rw-r--r-- | spec/models/project_services/chat_message/pipeline_message_spec.rb | 15 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 47 | ||||
-rw-r--r-- | spec/models/repository_spec.rb | 36 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 12 |
19 files changed, 591 insertions, 75 deletions
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb index 7e8a1c8add7..f84c6b48173 100644 --- a/spec/models/blob_spec.rb +++ b/spec/models/blob_spec.rb @@ -35,8 +35,68 @@ describe Blob do end end + describe '#external_storage_error?' do + context 'if the blob is stored in LFS' do + let(:blob) { fake_blob(path: 'file.pdf', lfs: true) } + + context 'when the project has LFS enabled' do + it 'returns false' do + expect(blob.external_storage_error?).to be_falsey + end + end + + context 'when the project does not have LFS enabled' do + before do + project.lfs_enabled = false + end + + it 'returns true' do + expect(blob.external_storage_error?).to be_truthy + end + end + end + + context 'if the blob is not stored in LFS' do + let(:blob) { fake_blob(path: 'file.md') } + + it 'returns false' do + expect(blob.external_storage_error?).to be_falsey + end + end + end + + describe '#stored_externally?' do + context 'if the blob is stored in LFS' do + let(:blob) { fake_blob(path: 'file.pdf', lfs: true) } + + context 'when the project has LFS enabled' do + it 'returns true' do + expect(blob.stored_externally?).to be_truthy + end + end + + context 'when the project does not have LFS enabled' do + before do + project.lfs_enabled = false + end + + it 'returns false' do + expect(blob.stored_externally?).to be_falsey + end + end + end + + context 'if the blob is not stored in LFS' do + let(:blob) { fake_blob(path: 'file.md') } + + it 'returns false' do + expect(blob.stored_externally?).to be_falsey + end + end + end + describe '#raw_binary?' do - context 'if the blob is a valid LFS pointer' do + context 'if the blob is stored externally' do context 'if the extension has a rich viewer' do context 'if the viewer is binary' do it 'returns true' do @@ -56,15 +116,63 @@ describe Blob do end context "if the extension doesn't have a rich viewer" do - it 'returns true' do - blob = fake_blob(path: 'file.exe', lfs: true) + context 'if the extension has a text mime type' do + context 'if the extension is for a programming language' do + it 'returns false' do + blob = fake_blob(path: 'file.txt', lfs: true) - expect(blob.raw_binary?).to be_truthy + expect(blob.raw_binary?).to be_falsey + end + end + + context 'if the extension is not for a programming language' do + it 'returns false' do + blob = fake_blob(path: 'file.ics', lfs: true) + + expect(blob.raw_binary?).to be_falsey + end + end + end + + context 'if the extension has a binary mime type' do + context 'if the extension is for a programming language' do + it 'returns false' do + blob = fake_blob(path: 'file.rb', lfs: true) + + expect(blob.raw_binary?).to be_falsey + end + end + + context 'if the extension is not for a programming language' do + it 'returns true' do + blob = fake_blob(path: 'file.exe', lfs: true) + + expect(blob.raw_binary?).to be_truthy + end + end + end + + context 'if the extension has an unknown mime type' do + context 'if the extension is for a programming language' do + it 'returns false' do + blob = fake_blob(path: 'file.ini', lfs: true) + + expect(blob.raw_binary?).to be_falsey + end + end + + context 'if the extension is not for a programming language' do + it 'returns true' do + blob = fake_blob(path: 'file.wtf', lfs: true) + + expect(blob.raw_binary?).to be_truthy + end + end end end end - context 'if the blob is not an LFS pointer' do + context 'if the blob is not stored externally' do context 'if the blob is binary' do it 'returns true' do blob = fake_blob(path: 'file.pdf', binary: true) @@ -94,7 +202,7 @@ describe Blob do describe '#simple_viewer' do context 'when the blob is empty' do it 'returns an empty viewer' do - blob = fake_blob(data: '') + blob = fake_blob(data: '', size: 0) expect(blob.simple_viewer).to be_a(BlobViewer::Empty) end @@ -118,7 +226,7 @@ describe Blob do end describe '#rich_viewer' do - context 'when the blob is an invalid LFS pointer' do + context 'when the blob has an external storage error' do before do project.lfs_enabled = false end @@ -138,7 +246,7 @@ describe Blob do end end - context 'when the blob is a valid LFS pointer' do + context 'when the blob is stored externally' do it 'returns a matching viewer' do blob = fake_blob(path: 'file.pdf', lfs: true) diff --git a/spec/models/blob_viewer/base_spec.rb b/spec/models/blob_viewer/base_spec.rb index a3e598de56d..740ad9d275e 100644 --- a/spec/models/blob_viewer/base_spec.rb +++ b/spec/models/blob_viewer/base_spec.rb @@ -139,7 +139,7 @@ describe BlobViewer::Base, model: true do end end - context 'when the viewer is server side but the blob is stored in LFS' do + context 'when the viewer is server side but the blob is stored externally' do let(:project) { build(:empty_project, lfs_enabled: true) } let(:blob) { fake_blob(path: 'file.pdf', lfs: true) } @@ -148,8 +148,8 @@ describe BlobViewer::Base, model: true do allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) end - it 'return :server_side_but_stored_in_lfs' do - expect(viewer.render_error).to eq(:server_side_but_stored_in_lfs) + it 'return :server_side_but_stored_externally' do + expect(viewer.render_error).to eq(:server_side_but_stored_externally) end end end diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb new file mode 100644 index 00000000000..968593d7e9b --- /dev/null +++ b/spec/models/ci/artifact_blob_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe Ci::ArtifactBlob, models: true do + let(:build) { create(:ci_build, :artifacts) } + let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') } + + subject { described_class.new(entry) } + + describe '#id' do + it 'returns a hash of the path' do + expect(subject.id).to eq(Digest::SHA1.hexdigest(entry.path)) + end + end + + describe '#name' do + it 'returns the entry name' do + expect(subject.name).to eq(entry.name) + end + end + + describe '#path' do + it 'returns the entry path' do + expect(subject.path).to eq(entry.path) + end + end + + describe '#size' do + it 'returns the entry size' do + expect(subject.size).to eq(entry.metadata[:size]) + end + end + + describe '#mode' do + it 'returns the entry mode' do + expect(subject.mode).to eq(entry.metadata[:mode]) + end + end + + describe '#external_storage' do + it 'returns :build_artifact' do + expect(subject.external_storage).to eq(:build_artifact) + end + end +end diff --git a/spec/models/ci/trigger_schedule_spec.rb b/spec/models/ci/trigger_schedule_spec.rb index 75d21541cee..92447564d7c 100644 --- a/spec/models/ci/trigger_schedule_spec.rb +++ b/spec/models/ci/trigger_schedule_spec.rb @@ -73,4 +73,36 @@ describe Ci::TriggerSchedule, models: true do end end end + + describe '#real_next_run' do + subject do + Ci::TriggerSchedule.last.real_next_run(worker_cron: worker_cron, + worker_time_zone: worker_time_zone) + end + + context 'when GitLab time_zone is UTC' do + before do + allow(Time).to receive(:zone) + .and_return(ActiveSupport::TimeZone[worker_time_zone]) + end + + let(:worker_time_zone) { 'UTC' } + + context 'when cron_timezone is Eastern Time (US & Canada)' do + before do + create(:ci_trigger_schedule, :nightly, + cron_timezone: 'Eastern Time (US & Canada)') + end + + let(:worker_cron) { '0 1 2 3 *' } + + it 'returns the next time worker executes' do + expect(subject.min).to eq(0) + expect(subject.hour).to eq(1) + expect(subject.day).to eq(2) + expect(subject.month).to eq(3) + end + end + end + end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index ce31c8ed94c..08b2169fea7 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -212,7 +212,7 @@ eos end end - describe '#latest_pipeline' do + describe '#last_pipeline' do let!(:first_pipeline) do create(:ci_empty_pipeline, project: project, @@ -226,8 +226,8 @@ eos status: 'success') end - it 'returns latest pipeline' do - expect(commit.latest_pipeline).to eq second_pipeline + it 'returns last pipeline' do + expect(commit.last_pipeline).to eq second_pipeline end end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 4edafbc4e32..40bbb10eaac 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -170,6 +170,12 @@ describe CacheMarkdownField do is_expected.to be_truthy end + + it 'returns false if the markdown field is set but the html is not' do + thing.foo_html = nil + + is_expected.to be_falsy + end end describe '#refresh_markdown_cache!' do diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb index 48e7c0a822c..81f338745b1 100644 --- a/spec/models/diff_discussion_spec.rb +++ b/spec/models/diff_discussion_spec.rb @@ -1,19 +1,86 @@ require 'spec_helper' describe DiffDiscussion, model: true do - subject { described_class.new([first_note, second_note, third_note]) } + include RepoHelpers - let(:first_note) { create(:diff_note_on_merge_request) } - let(:merge_request) { first_note.noteable } - let(:project) { first_note.project } - let(:second_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, in_reply_to: first_note) } - let(:third_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, in_reply_to: first_note) } + subject { described_class.new([diff_note]) } + + let(:project) { create(:project) } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) } describe '#reply_attributes' do it 'includes position and original_position' do attributes = subject.reply_attributes - expect(attributes[:position]).to eq(first_note.position.to_json) - expect(attributes[:original_position]).to eq(first_note.original_position.to_json) + expect(attributes[:position]).to eq(diff_note.position.to_json) + expect(attributes[:original_position]).to eq(diff_note.original_position.to_json) + end + end + + describe '#merge_request_version_params' do + let(:merge_request) { create(:merge_request, source_project: project, target_project: project, importing: true) } + let!(:merge_request_diff1) { merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + let!(:merge_request_diff2) { merge_request.merge_request_diffs.create(head_commit_sha: nil) } + let!(:merge_request_diff3) { merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + + context 'when the discussion is active' do + it 'returns an empty hash, which will end up showing the latest version' do + expect(subject.merge_request_version_params).to eq({}) + end + end + + context 'when the discussion is on an older merge request version' do + let(:position) do + Gitlab::Diff::Position.new( + old_path: ".gitmodules", + new_path: ".gitmodules", + old_line: nil, + new_line: 4, + diff_refs: merge_request_diff1.diff_refs + ) + end + + let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, position: position) } + + before do + diff_note.position = diff_note.original_position + diff_note.save! + end + + it 'returns the diff ID for the version to show' do + expect(diff_id: merge_request_diff1.id) + end + end + + context 'when the discussion is on a comparison between merge request versions' do + let(:position) do + Gitlab::Diff::Position.new( + old_path: ".gitmodules", + new_path: ".gitmodules", + old_line: 4, + new_line: 4, + diff_refs: merge_request_diff3.compare_with(merge_request_diff1.head_commit_sha).diff_refs + ) + end + + let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, position: position) } + + it 'returns the diff ID and start sha of the versions to compare' do + expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff3.id, start_sha: merge_request_diff1.head_commit_sha) + end + end + + context 'when the discussion does not have a merge request version' do + let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, diff_refs: project.commit(sample_commit.id).diff_refs) } + + before do + diff_note.position = diff_note.original_position + diff_note.save! + end + + it 'returns nil' do + expect(subject.merge_request_version_params).to be_nil + end end end end diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb index f32b6b99b3d..ab4c51a87b0 100644 --- a/spec/models/diff_note_spec.rb +++ b/spec/models/diff_note_spec.rb @@ -155,23 +155,6 @@ describe DiffNote, models: true do end end - describe '#latest_merge_request_diff' do - context 'when active' do - it 'returns the current merge request diff' do - expect(subject.latest_merge_request_diff).to eq(merge_request.merge_request_diff) - end - end - - context 'when outdated' do - let!(:old_merge_request_diff) { merge_request.merge_request_diff } - let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: commit.diff_refs) } - - it 'returns the latest merge request diff that this diff note applied to' do - expect(subject.latest_merge_request_diff).to eq(old_merge_request_diff) - end - end - end - describe "creation" do describe "updating of position" do context "when noteable is a commit" do @@ -256,4 +239,39 @@ describe DiffNote, models: true do end end end + + describe '#created_at_diff?' do + let(:diff_refs) { project.commit(sample_commit.id).diff_refs } + let(:position) do + Gitlab::Diff::Position.new( + old_path: "files/ruby/popen.rb", + new_path: "files/ruby/popen.rb", + old_line: nil, + new_line: 14, + diff_refs: diff_refs + ) + end + + context "when noteable is a commit" do + subject { build(:diff_note_on_commit, project: project, position: position) } + + it "returns true" do + expect(subject.created_at_diff?(diff_refs)).to be true + end + end + + context "when noteable is a merge request" do + context "when the diff refs match the original one of the diff note" do + it "returns true" do + expect(subject.created_at_diff?(diff_refs)).to be true + end + end + + context "when the diff refs don't match the original one of the diff note" do + it "returns false" do + expect(subject.created_at_diff?(merge_request.diff_refs)).to be false + end + end + end + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 8ffde6f7fbb..a11805926cc 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -57,6 +57,32 @@ describe Group, models: true do it { is_expected.not_to validate_presence_of :owner } it { is_expected.to validate_presence_of :two_factor_grace_period } it { is_expected.to validate_numericality_of(:two_factor_grace_period).is_greater_than_or_equal_to(0) } + + describe 'path validation' do + it 'rejects paths reserved on the root namespace when the group has no parent' do + group = build(:group, path: 'api') + + expect(group).not_to be_valid + end + + it 'allows root paths when the group has a parent' do + group = build(:group, path: 'api', parent: create(:group)) + + expect(group).to be_valid + end + + it 'rejects any wildcard paths when not a top level group' do + group = build(:group, path: 'tree', parent: create(:group)) + + expect(group).not_to be_valid + end + + it 'rejects reserved group paths' do + group = build(:group, path: 'activity', parent: create(:group)) + + expect(group).not_to be_valid + end + end end describe '.visible_to_user' do diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 11befd4edfe..8748b98a4e3 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -291,6 +291,27 @@ describe Issue, models: true do end end + describe '#has_related_branch?' do + let(:issue) { create(:issue, title: "Blue Bell Knoll") } + subject { issue.has_related_branch? } + + context 'branch found' do + before do + allow(issue.project.repository).to receive(:branch_names).and_return(["iceblink-luck", issue.to_branch_name]) + end + + it { is_expected.to eq true } + end + + context 'branch not found' do + before do + allow(issue.project.repository).to receive(:branch_names).and_return(["lazy-calm"]) + end + + it { is_expected.to eq false } + end + end + it_behaves_like 'an editable mentionable' do subject { create(:issue, project: create(:project, :repository)) } diff --git a/spec/models/legacy_diff_discussion_spec.rb b/spec/models/legacy_diff_discussion_spec.rb index 153e757a0ef..6eb4a2aaf39 100644 --- a/spec/models/legacy_diff_discussion_spec.rb +++ b/spec/models/legacy_diff_discussion_spec.rb @@ -8,4 +8,26 @@ describe LegacyDiffDiscussion, models: true do expect(subject.reply_attributes[:line_code]).to eq(subject.line_code) end end + + describe '#merge_request_version_params' do + context 'when the discussion is active' do + before do + allow(subject).to receive(:active?).and_return(true) + end + + it 'returns an empty hash, which will end up showing the latest version' do + expect(subject.merge_request_version_params).to eq({}) + end + end + + context 'when the discussion is outdated' do + before do + allow(subject).to receive(:active?).and_return(false) + end + + it 'returns nil' do + expect(subject.merge_request_version_params).to be_nil + end + end + end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index be08b96641a..8b72125dd5d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1554,4 +1554,23 @@ describe MergeRequest, models: true do expect(subject.has_no_commits?).to be_truthy end end + + describe '#merge_request_diff_for' do + subject { create(:merge_request, importing: true) } + let!(:merge_request_diff1) { subject.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + let!(:merge_request_diff2) { subject.merge_request_diffs.create(head_commit_sha: nil) } + let!(:merge_request_diff3) { subject.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + + context 'with diff refs' do + it 'returns the diffs' do + expect(subject.merge_request_diff_for(merge_request_diff1.diff_refs)).to eq(merge_request_diff1) + end + end + + context 'with a commit SHA' do + it 'returns the diffs' do + expect(subject.merge_request_diff_for(merge_request_diff3.head_commit_sha)).to eq(merge_request_diff3) + end + end + end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index e406d0a16bd..8624616316c 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -34,6 +34,13 @@ describe Namespace, models: true do let(:group) { build(:group, :nested, path: 'tree') } it { expect(group).not_to be_valid } + + it 'rejects nested paths' do + parent = create(:group, :nested, path: 'environments') + namespace = build(:project, path: 'folders', namespace: parent) + + expect(namespace).not_to be_valid + end end context 'top-level group' do @@ -47,6 +54,7 @@ describe Namespace, models: true do describe "Respond to" do it { is_expected.to respond_to(:human_name) } it { is_expected.to respond_to(:to_param) } + it { is_expected.to respond_to(:has_parent?) } end describe '#to_param' do diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb index 46b36e11c23..0fe8a591a45 100644 --- a/spec/models/network/graph_spec.rb +++ b/spec/models/network/graph_spec.rb @@ -10,17 +10,17 @@ describe Network::Graph, models: true do expect(graph.notes).to eq( { note_on_commit.commit_id => 1 } ) end - describe "#commits" do + describe '#commits' do let(:graph) { described_class.new(project, 'refs/heads/master', project.repository.commit, nil) } - it "returns a list of commits" do + it 'returns a list of commits' do commits = graph.commits expect(commits).not_to be_empty expect(commits).to all( be_kind_of(Network::Commit) ) end - it "sorts the commits by commit date (descending)" do + it 'it the commits by commit date (descending)' do # Remove duplicate timestamps because they make it harder to # assert that the commits are sorted as expected. commits = graph.commits.uniq(&:date) @@ -29,5 +29,20 @@ describe Network::Graph, models: true do expect(commits).not_to be_empty expect(commits.map(&:id)).to eq(sorted_commits.map(&:id)) end + + it 'sorts children before parents for commits with the same timestamp' do + commits_by_time = graph.commits.group_by(&:date) + + commits_by_time.each do |time, commits| + commit_ids = commits.map(&:id) + + commits.each_with_index do |commit, index| + parent_indexes = commit.parent_ids.map { |parent_id| commit_ids.find_index(parent_id) }.compact + + # All parents of the current commit should appear after it + expect(parent_indexes).to all( be > index ) + end + end + end end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 557ea97b008..7a01cef9b4b 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -272,9 +272,9 @@ describe Note, models: true do Gitlab::Diff::Position.new( old_path: "files/ruby/popen.rb", new_path: "files/ruby/popen.rb", - old_line: 16, - new_line: 22, - diff_refs: merge_request.diff_refs + old_line: nil, + new_line: 13, + diff_refs: project.commit(sample_commit.id).diff_refs ) end @@ -288,26 +288,78 @@ describe Note, models: true do ) end - subject { merge_request.notes.grouped_diff_discussions } + context 'active diff discussions' do + subject { merge_request.notes.grouped_diff_discussions } - it "includes active discussions" do - discussions = subject.values.flatten + it "includes active discussions" do + discussions = subject.values.flatten - expect(discussions.count).to eq(2) - expect(discussions.map(&:id)).to eq([active_diff_note1.discussion_id, active_diff_note3.discussion_id]) - expect(discussions.all?(&:active?)).to be true + expect(discussions.count).to eq(2) + expect(discussions.map(&:id)).to eq([active_diff_note1.discussion_id, active_diff_note3.discussion_id]) + expect(discussions.all?(&:active?)).to be true - expect(discussions.first.notes).to eq([active_diff_note1, active_diff_note2]) - expect(discussions.last.notes).to eq([active_diff_note3]) - end + expect(discussions.first.notes).to eq([active_diff_note1, active_diff_note2]) + expect(discussions.last.notes).to eq([active_diff_note3]) + end - it "doesn't include outdated discussions" do - expect(subject.values.flatten.map(&:id)).not_to include(outdated_diff_note1.discussion_id) + it "doesn't include outdated discussions" do + expect(subject.values.flatten.map(&:id)).not_to include(outdated_diff_note1.discussion_id) + end + + it "groups the discussions by line code" 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 end - it "groups the discussions by line code" 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) + context 'diff discussions for older diff refs' do + subject { merge_request.notes.grouped_diff_discussions(diff_refs) } + + context 'for diff refs a discussion was created at' do + let(:diff_refs) { active_position2.diff_refs } + + it "includes discussions that were created then" do + discussions = subject.values.flatten + + expect(discussions.count).to eq(1) + + discussion = discussions.first + + expect(discussion.id).to eq(active_diff_note3.discussion_id) + expect(discussion.active?).to be true + expect(discussion.active?(diff_refs)).to be false + expect(discussion.created_at_diff?(diff_refs)).to be true + + expect(discussion.notes).to eq([active_diff_note3]) + end + + it "groups the discussions by original line code" do + expect(subject[active_diff_note3.original_line_code].first.id).to eq(active_diff_note3.discussion_id) + end + end + + context 'for diff refs a discussion was last active at' do + let(:diff_refs) { outdated_position.diff_refs } + + it "includes discussions that were last active" do + discussions = subject.values.flatten + + expect(discussions.count).to eq(1) + + discussion = discussions.first + + expect(discussion.id).to eq(outdated_diff_note1.discussion_id) + expect(discussion.active?).to be false + expect(discussion.active?(diff_refs)).to be true + expect(discussion.created_at_diff?(diff_refs)).to be true + + expect(discussion.notes).to eq([outdated_diff_note1, outdated_diff_note2]) + end + + it "groups the discussions by line code" do + expect(subject[outdated_diff_note1.line_code].first.id).to eq(outdated_diff_note1.discussion_id) + end + end end end diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb index ec5c6c5e0ed..e005be42b0d 100644 --- a/spec/models/project_services/chat_message/pipeline_message_spec.rb +++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb @@ -4,6 +4,7 @@ describe ChatMessage::PipelineMessage do subject { described_class.new(args) } let(:user) { { name: 'hacker' } } + let(:duration) { 7210 } let(:args) do { object_attributes: { @@ -26,7 +27,6 @@ describe ChatMessage::PipelineMessage do context 'pipeline succeeded' do let(:status) { 'success' } let(:color) { 'good' } - let(:duration) { 10 } let(:message) { build_message('passed') } it 'returns a message with information about succeeded build' do @@ -39,7 +39,6 @@ describe ChatMessage::PipelineMessage do context 'pipeline failed' do let(:status) { 'failed' } let(:color) { 'danger' } - let(:duration) { 10 } let(:message) { build_message } it 'returns a message with information about failed build' do @@ -64,7 +63,7 @@ describe ChatMessage::PipelineMessage do "<http://example.gitlab.com|project_name>:" \ " Pipeline <http://example.gitlab.com/pipelines/123|#123>" \ " of <http://example.gitlab.com/commits/develop|develop> branch" \ - " by #{name} #{status_text} in #{duration} #{'second'.pluralize(duration)}" + " by #{name} #{status_text} in 02:00:10" end end @@ -76,7 +75,6 @@ describe ChatMessage::PipelineMessage do context 'pipeline succeeded' do let(:status) { 'success' } let(:color) { 'good' } - let(:duration) { 10 } let(:message) { build_markdown_message('passed') } it 'returns a message with information about succeeded build' do @@ -85,7 +83,7 @@ describe ChatMessage::PipelineMessage do expect(subject.activity).to eq({ title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of [develop](http://example.gitlab.com/commits/develop) branch by hacker passed', subtitle: 'in [project_name](http://example.gitlab.com)', - text: 'in 10 seconds', + text: 'in 02:00:10', image: '' }) end @@ -94,7 +92,6 @@ describe ChatMessage::PipelineMessage do context 'pipeline failed' do let(:status) { 'failed' } let(:color) { 'danger' } - let(:duration) { 10 } let(:message) { build_markdown_message } it 'returns a message with information about failed build' do @@ -103,7 +100,7 @@ describe ChatMessage::PipelineMessage do expect(subject.activity).to eq({ title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of [develop](http://example.gitlab.com/commits/develop) branch by hacker failed', subtitle: 'in [project_name](http://example.gitlab.com)', - text: 'in 10 seconds', + text: 'in 02:00:10', image: '' }) end @@ -118,7 +115,7 @@ describe ChatMessage::PipelineMessage do expect(subject.activity).to eq({ title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of [develop](http://example.gitlab.com/commits/develop) branch by API failed', subtitle: 'in [project_name](http://example.gitlab.com)', - text: 'in 10 seconds', + text: 'in 02:00:10', image: '' }) end @@ -129,7 +126,7 @@ describe ChatMessage::PipelineMessage do "[project_name](http://example.gitlab.com):" \ " Pipeline [#123](http://example.gitlab.com/pipelines/123)" \ " of [develop](http://example.gitlab.com/commits/develop)" \ - " branch by #{name} #{status_text} in #{duration} #{'second'.pluralize(duration)}" + " branch by #{name} #{status_text} in 02:00:10" end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d9244657953..36ce3070a6e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -253,6 +253,34 @@ describe Project, models: true do expect(new_project.errors.full_messages.first).to eq('The project is still being deleted. Please try again later.') end end + + describe 'path validation' do + it 'allows paths reserved on the root namespace' do + project = build(:project, path: 'api') + + expect(project).to be_valid + end + + it 'rejects paths reserved on another level' do + project = build(:project, path: 'tree') + + expect(project).not_to be_valid + end + + it 'rejects nested paths' do + parent = create(:group, :nested, path: 'environments') + project = build(:project, path: 'folders', namespace: parent) + + expect(project).not_to be_valid + end + + it 'allows a reserved group name' do + parent = create(:group) + project = build(:project, path: 'avatar', namespace: parent) + + expect(project).to be_valid + end + end end describe 'default_scope' do @@ -1878,4 +1906,23 @@ describe Project, models: true do expect(project.pipeline_status).to be_loaded end end + + describe '#append_or_update_attribute' do + let(:project) { create(:project) } + + it 'shows full error updating an invalid MR' do + error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\ + ' Validate fork Source project is not a fork of the target project' + + expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }. + to raise_error(ActiveRecord::RecordNotSaved, error_message) + end + + it 'updates the project succesfully' do + merge_request = create(:merge_request, target_project: project, source_project: project) + + expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }. + not_to raise_error + end + end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index f6846cc1b2f..dd6514b3b50 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1098,21 +1098,33 @@ describe Repository, models: true do end describe '#merge' do - it 'merges the code and return the commit id' do + let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) } + + let(:commit_options) do + author = repository.user_to_committer(user) + { message: 'Test \r\n\r\n message', committer: author, author: author } + end + + it 'merges the code and returns the commit id' do expect(merge_commit).to be_present expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present end it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do - merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) - - merge_commit_id = repository.merge(user, - merge_request.diff_head_sha, - merge_request, - commit_options) + merge_commit_id = merge(repository, user, merge_request, commit_options) expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id) end + + it 'removes carriage returns from commit message' do + merge_commit_id = merge(repository, user, merge_request, commit_options) + + expect(repository.commit(merge_commit_id).message).to eq(commit_options[:message].delete("\r")) + end + + def merge(repository, user, merge_request, options = {}) + repository.merge(user, merge_request.diff_head_sha, merge_request, options) + end end describe '#revert' do @@ -1379,12 +1391,22 @@ describe Repository, models: true do describe '#branch_count' do it 'returns the number of branches' do expect(repository.branch_count).to be_an(Integer) + + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync + rugged_count = repository.raw_repository.rugged.branches.count + + expect(repository.branch_count).to eq(rugged_count) end end describe '#tag_count' do it 'returns the number of tags' do expect(repository.tag_count).to be_an(Integer) + + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync + rugged_count = repository.raw_repository.rugged.tags.count + + expect(repository.tag_count).to eq(rugged_count) end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0bcebc27598..1c2df4c9d97 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -97,6 +97,18 @@ describe User, models: true do expect(user.errors.values).to eq [['dashboard is a reserved name']] end + it 'allows child names' do + user = build(:user, username: 'avatar') + + expect(user).to be_valid + end + + it 'allows wildcard names' do + user = build(:user, username: 'blob') + + expect(user).to be_valid + end + it 'validates uniqueness' do expect(subject).to validate_uniqueness_of(:username).case_insensitive end |