summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
authorLin Jen-Shin <godfat@godfat.org>2016-12-14 19:24:31 +0800
committerLin Jen-Shin <godfat@godfat.org>2016-12-14 19:24:31 +0800
commit367024f1707ebbf986e5f25ac208f24e35746389 (patch)
tree8ff45ed585720aa4eb2f5a6c772a2a20f89b946f /spec/models
parent101cde38cf6d5506ea37c5f912fb4c37af50c541 (diff)
parent3a90612660ab90225907ec6d79032905885c2507 (diff)
downloadgitlab-ce-367024f1707ebbf986e5f25ac208f24e35746389.tar.gz
Merge remote-tracking branch 'upstream/master' into show-commit-status-from-latest-pipeline
* upstream/master: (557 commits) Fix wrong error message expectation in API::Commits spec Move admin settings spinach feature to rspec Encode when migrating ProcessCommitWorker jobs Prevent overflow with vertical scroll when we have space to show content Make rubocop happy API: Ability to cherry-pick a commit Be smarter when finding a sudoed user in API::Helpers Backport hooks on group policies for the EE-specific implementation API: Ability to get group's project in simple representation Add AddLowerPathIndexToRoutes to setup_postgresql.rake For single line git commit messages, the close quote should be on the same line as the open quote added border-radius and padding to labels Allow all alphanumeric characters in file names (!8002) Add failing test for #20190 Don't allow blank MR titles in API Replace static fixture for awards_handler_spec (!7661) Crontab typo '* */6' -> '0 */6' (4x/day not 1x-per-min-for-1h 4x/day) Fix test Tweak style and add back wording Clean up commit copy to clipboard and make consistent ...
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/build_spec.rb4
-rw-r--r--spec/models/ci/pipeline_spec.rb281
-rw-r--r--spec/models/ci/stage_spec.rb133
-rw-r--r--spec/models/ci/variable_spec.rb7
-rw-r--r--spec/models/commit_range_spec.rb23
-rw-r--r--spec/models/commit_spec.rb42
-rw-r--r--spec/models/commit_status_spec.rb45
-rw-r--r--spec/models/concerns/has_status_spec.rb98
-rw-r--r--spec/models/concerns/issuable_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb71
-rw-r--r--spec/models/cycle_analytics/code_spec.rb2
-rw-r--r--spec/models/cycle_analytics/issue_spec.rb2
-rw-r--r--spec/models/cycle_analytics/plan_spec.rb2
-rw-r--r--spec/models/cycle_analytics/production_spec.rb2
-rw-r--r--spec/models/cycle_analytics/review_spec.rb2
-rw-r--r--spec/models/cycle_analytics/staging_spec.rb2
-rw-r--r--spec/models/cycle_analytics/summary_spec.rb2
-rw-r--r--spec/models/cycle_analytics/test_spec.rb2
-rw-r--r--spec/models/discussion_spec.rb28
-rw-r--r--spec/models/environment_spec.rb4
-rw-r--r--spec/models/event_spec.rb18
-rw-r--r--spec/models/group_label_spec.rb10
-rw-r--r--spec/models/group_spec.rb7
-rw-r--r--spec/models/issue_spec.rb10
-rw-r--r--spec/models/key_spec.rb8
-rw-r--r--spec/models/merge_request_diff_spec.rb34
-rw-r--r--spec/models/merge_request_spec.rb102
-rw-r--r--spec/models/milestone_spec.rb14
-rw-r--r--spec/models/namespace_spec.rb21
-rw-r--r--spec/models/note_spec.rb2
-rw-r--r--spec/models/project_label_spec.rb4
-rw-r--r--spec/models/project_services/jira_service_spec.rb40
-rw-r--r--spec/models/project_services/pipeline_email_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb154
-rw-r--r--spec/models/repository_spec.rb75
-rw-r--r--spec/models/route_spec.rb29
-rw-r--r--spec/models/snippet_spec.rb51
-rw-r--r--spec/models/user_spec.rb57
38 files changed, 1101 insertions, 291 deletions
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index ef07f2275b1..d4970e38f7c 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -730,8 +730,8 @@ describe Ci::Build, models: true do
pipeline2 = create(:ci_pipeline, project: project)
@build2 = create(:ci_build, pipeline: pipeline2)
- commits = [double(id: pipeline.sha), double(id: pipeline2.sha)]
- allow(@merge_request).to receive(:commits).and_return(commits)
+ allow(@merge_request).to receive(:commits_sha).
+ and_return([pipeline.sha, pipeline2.sha])
allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index ea022e03608..8158e71dd55 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Ci::Pipeline, models: true do
+ include EmailHelpers
+
let(:project) { FactoryGirl.create :empty_project }
let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, status: 'created', project: project }
@@ -18,8 +20,6 @@ describe Ci::Pipeline, models: true do
it { is_expected.to respond_to :git_author_email }
it { is_expected.to respond_to :short_sha }
- it { is_expected.to delegate_method(:stages).to(:statuses) }
-
describe '#valid_commit_sha' do
context 'commit.sha can not start with 00000000' do
before do
@@ -123,16 +123,55 @@ describe Ci::Pipeline, models: true do
end
describe '#stages' do
- let(:pipeline2) { FactoryGirl.create :ci_pipeline, project: project }
- subject { CommitStatus.where(pipeline: [pipeline, pipeline2]).stages }
-
before do
- FactoryGirl.create :ci_build, pipeline: pipeline2, stage: 'test', stage_idx: 1
- FactoryGirl.create :ci_build, pipeline: pipeline, stage: 'build', stage_idx: 0
+ create(:commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success')
+ create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed')
+ create(:commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running')
+ create(:commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success')
+ end
+
+ subject { pipeline.stages }
+
+ context 'stages list' do
+ it 'returns ordered list of stages' do
+ expect(subject.map(&:name)).to eq(%w[build test deploy])
+ end
+ end
+
+ it 'returns a valid number of stages' do
+ expect(pipeline.stages_count).to eq(3)
end
- it 'return all stages' do
- is_expected.to eq(%w(build test))
+ it 'returns a valid names of stages' do
+ expect(pipeline.stages_name).to eq(['build', 'test', 'deploy'])
+ end
+
+ context 'stages with statuses' do
+ let(:statuses) do
+ subject.map do |stage|
+ [stage.name, stage.status]
+ end
+ end
+
+ it 'returns list of stages with statuses' do
+ expect(statuses).to eq([['build', 'failed'],
+ ['test', 'success'],
+ ['deploy', 'running']
+ ])
+ end
+
+ context 'when build is retried' do
+ before do
+ create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success')
+ end
+
+ it 'ignores the previous state' do
+ expect(statuses).to eq([['build', 'success'],
+ ['test', 'success'],
+ ['deploy', 'running']
+ ])
+ end
+ end
end
end
@@ -402,6 +441,230 @@ describe Ci::Pipeline, models: true do
end
end
+ describe '#detailed_status' do
+ context 'when pipeline is created' do
+ let(:pipeline) { create(:ci_pipeline, status: :created) }
+
+ it 'returns detailed status for created pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'created'
+ end
+ end
+
+ context 'when pipeline is pending' do
+ let(:pipeline) { create(:ci_pipeline, status: :pending) }
+
+ it 'returns detailed status for pending pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'pending'
+ end
+ end
+
+ context 'when pipeline is running' do
+ let(:pipeline) { create(:ci_pipeline, status: :running) }
+
+ it 'returns detailed status for running pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'running'
+ end
+ end
+
+ context 'when pipeline is successful' do
+ let(:pipeline) { create(:ci_pipeline, status: :success) }
+
+ it 'returns detailed status for successful pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'passed'
+ end
+ end
+
+ context 'when pipeline is failed' do
+ let(:pipeline) { create(:ci_pipeline, status: :failed) }
+
+ it 'returns detailed status for failed pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'failed'
+ end
+ end
+
+ context 'when pipeline is canceled' do
+ let(:pipeline) { create(:ci_pipeline, status: :canceled) }
+
+ it 'returns detailed status for canceled pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'canceled'
+ end
+ end
+
+ context 'when pipeline is skipped' do
+ let(:pipeline) { create(:ci_pipeline, status: :skipped) }
+
+ it 'returns detailed status for skipped pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'skipped'
+ end
+ end
+
+ context 'when pipeline is successful but with warnings' do
+ let(:pipeline) { create(:ci_pipeline, status: :success) }
+
+ before do
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline)
+ end
+
+ it 'retruns detailed status for successful pipeline with warnings' do
+ expect(pipeline.detailed_status.label).to eq 'passed with warnings'
+ end
+ end
+ end
+
+ describe '#cancelable?' do
+ %i[created running pending].each do |status0|
+ context "when there is a build #{status0}" do
+ before do
+ create(:ci_build, status0, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ context "when there is an external job #{status0}" do
+ before do
+ create(:generic_commit_status, status0, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ %i[success failed canceled].each do |status1|
+ context "when there are generic_commit_status jobs for #{status0} and #{status1}" do
+ before do
+ create(:generic_commit_status, status0, pipeline: pipeline)
+ create(:generic_commit_status, status1, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ context "when there are generic_commit_status and ci_build jobs for #{status0} and #{status1}" do
+ before do
+ create(:generic_commit_status, status0, pipeline: pipeline)
+ create(:ci_build, status1, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ context "when there are ci_build jobs for #{status0} and #{status1}" do
+ before do
+ create(:ci_build, status0, pipeline: pipeline)
+ create(:ci_build, status1, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+ end
+ end
+
+ %i[success failed canceled].each do |status|
+ context "when there is a build #{status}" do
+ before do
+ create(:ci_build, status, pipeline: pipeline)
+ end
+
+ it 'is not cancelable' do
+ expect(pipeline.cancelable?).to be_falsey
+ end
+ end
+
+ context "when there is an external job #{status}" do
+ before do
+ create(:generic_commit_status, status, pipeline: pipeline)
+ end
+
+ it 'is not cancelable' do
+ expect(pipeline.cancelable?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe '#cancel_running' do
+ let(:latest_status) { pipeline.statuses.pluck(:status) }
+
+ context 'when there is a running external job and created build' do
+ before do
+ create(:ci_build, :running, pipeline: pipeline)
+ create(:generic_commit_status, :running, pipeline: pipeline)
+
+ pipeline.cancel_running
+ end
+
+ it 'cancels both jobs' do
+ expect(latest_status).to contain_exactly('canceled', 'canceled')
+ end
+ end
+
+ context 'when builds are in different stages' do
+ before do
+ create(:ci_build, :running, stage_idx: 0, pipeline: pipeline)
+ create(:ci_build, :running, stage_idx: 1, pipeline: pipeline)
+
+ pipeline.cancel_running
+ end
+
+ it 'cancels both jobs' do
+ expect(latest_status).to contain_exactly('canceled', 'canceled')
+ end
+ end
+ end
+
+ describe '#retry_failed' do
+ let(:latest_status) { pipeline.statuses.latest.pluck(:status) }
+
+ context 'when there is a failed build and failed external status' do
+ before do
+ create(:ci_build, :failed, name: 'build', pipeline: pipeline)
+ create(:generic_commit_status, :failed, name: 'jenkins', pipeline: pipeline)
+
+ pipeline.retry_failed(create(:user))
+ end
+
+ it 'retries only build' do
+ expect(latest_status).to contain_exactly('pending', 'failed')
+ end
+ end
+
+ context 'when builds are in different stages' do
+ before do
+ create(:ci_build, :failed, name: 'build', stage_idx: 0, pipeline: pipeline)
+ create(:ci_build, :failed, name: 'jenkins', stage_idx: 1, pipeline: pipeline)
+
+ pipeline.retry_failed(create(:user))
+ end
+
+ it 'retries both builds' do
+ expect(latest_status).to contain_exactly('pending', 'pending')
+ end
+ end
+
+ context 'when there are canceled and failed' do
+ before do
+ create(:ci_build, :failed, name: 'build', stage_idx: 0, pipeline: pipeline)
+ create(:ci_build, :canceled, name: 'jenkins', stage_idx: 1, pipeline: pipeline)
+
+ pipeline.retry_failed(create(:user))
+ end
+
+ it 'retries both builds' do
+ expect(latest_status).to contain_exactly('pending', 'pending')
+ end
+ end
+ end
+
describe '#execute_hooks' do
let!(:build_a) { create_build('a', 0) }
let!(:build_b) { create_build('b', 1) }
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
new file mode 100644
index 00000000000..f232761dba2
--- /dev/null
+++ b/spec/models/ci/stage_spec.rb
@@ -0,0 +1,133 @@
+require 'spec_helper'
+
+describe Ci::Stage, models: true do
+ let(:stage) { build(:ci_stage) }
+ let(:pipeline) { stage.pipeline }
+ let(:stage_name) { stage.name }
+
+ describe '#expectations' do
+ subject { stage }
+
+ it { is_expected.to include_module(StaticModel) }
+
+ it { is_expected.to respond_to(:pipeline) }
+ it { is_expected.to respond_to(:name) }
+
+ it { is_expected.to delegate_method(:project).to(:pipeline) }
+ end
+
+ describe '#statuses' do
+ let!(:stage_build) { create_job(:ci_build) }
+ let!(:commit_status) { create_job(:commit_status) }
+ let!(:other_build) { create_job(:ci_build, stage: 'other stage') }
+
+ subject { stage.statuses }
+
+ it "returns only matching statuses" do
+ is_expected.to contain_exactly(stage_build, commit_status)
+ end
+ end
+
+ describe '#builds' do
+ let!(:stage_build) { create_job(:ci_build) }
+ let!(:commit_status) { create_job(:commit_status) }
+
+ subject { stage.builds }
+
+ it "returns only builds" do
+ is_expected.to contain_exactly(stage_build)
+ end
+ end
+
+ describe '#status' do
+ subject { stage.status }
+
+ context 'if status is already defined' do
+ let(:stage) { build(:ci_stage, status: 'success') }
+
+ it "returns defined status" do
+ is_expected.to eq('success')
+ end
+ end
+
+ context 'if status has to be calculated' do
+ let!(:stage_build) { create_job(:ci_build, status: :failed) }
+
+ it "returns status of a build" do
+ is_expected.to eq('failed')
+ end
+
+ context 'and builds are retried' do
+ let!(:new_build) { create_job(:ci_build, status: :success) }
+
+ it "returns status of latest build" do
+ is_expected.to eq('success')
+ end
+ end
+ end
+ end
+
+ describe '#detailed_status' do
+ subject { stage.detailed_status }
+
+ context 'when build is created' do
+ let!(:stage_build) { create_job(:ci_build, status: :created) }
+
+ it 'returns detailed status for created stage' do
+ expect(subject.text).to eq 'created'
+ end
+ end
+
+ context 'when build is pending' do
+ let!(:stage_build) { create_job(:ci_build, status: :pending) }
+
+ it 'returns detailed status for pending stage' do
+ expect(subject.text).to eq 'pending'
+ end
+ end
+
+ context 'when build is running' do
+ let!(:stage_build) { create_job(:ci_build, status: :running) }
+
+ it 'returns detailed status for running stage' do
+ expect(subject.text).to eq 'running'
+ end
+ end
+
+ context 'when build is successful' do
+ let!(:stage_build) { create_job(:ci_build, status: :success) }
+
+ it 'returns detailed status for successful stage' do
+ expect(subject.text).to eq 'passed'
+ end
+ end
+
+ context 'when build is failed' do
+ let!(:stage_build) { create_job(:ci_build, status: :failed) }
+
+ it 'returns detailed status for failed stage' do
+ expect(subject.text).to eq 'failed'
+ end
+ end
+
+ context 'when build is canceled' do
+ let!(:stage_build) { create_job(:ci_build, status: :canceled) }
+
+ it 'returns detailed status for canceled stage' do
+ expect(subject.text).to eq 'canceled'
+ end
+ end
+
+ context 'when build is skipped' do
+ let!(:stage_build) { create_job(:ci_build, status: :skipped) }
+
+ it 'returns detailed status for skipped stage' do
+ expect(subject.text).to eq 'skipped'
+ end
+ end
+ end
+
+ def create_job(type, status: 'success', stage: stage_name)
+ create(type, pipeline: pipeline, stage: stage, status: status)
+ end
+end
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 4e7833c3162..bee9f714849 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -5,6 +5,13 @@ describe Ci::Variable, models: true do
let(:secret_value) { 'secret' }
+ it { is_expected.to validate_presence_of(:key) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:gl_project_id) }
+ it { is_expected.to validate_length_of(:key).is_at_most(255) }
+ it { is_expected.to allow_value('foo').for(:key) }
+ it { is_expected.not_to allow_value('foo bar').for(:key) }
+ it { is_expected.not_to allow_value('foo/bar').for(:key) }
+
before :each do
subject.value = secret_value
end
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index c41359b55a3..30782ca75a0 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -45,7 +45,7 @@ describe CommitRange, models: true do
end
describe '#to_reference' do
- let(:cross) { create(:project) }
+ let(:cross) { create(:empty_project, namespace: project.namespace) }
it 'returns a String reference to the object' do
expect(range.to_reference).to eq "#{full_sha_from}...#{full_sha_to}"
@@ -56,12 +56,12 @@ describe CommitRange, models: true do
end
it 'supports a cross-project reference' do
- expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{full_sha_from}...#{full_sha_to}"
+ expect(range.to_reference(cross)).to eq "#{project.path}@#{full_sha_from}...#{full_sha_to}"
end
end
describe '#reference_link_text' do
- let(:cross) { create(:project) }
+ let(:cross) { create(:empty_project, namespace: project.namespace) }
it 'returns a String reference to the object' do
expect(range.reference_link_text).to eq "#{sha_from}...#{sha_to}"
@@ -72,7 +72,7 @@ describe CommitRange, models: true do
end
it 'supports a cross-project reference' do
- expect(range.reference_link_text(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}"
+ expect(range.reference_link_text(cross)).to eq "#{project.path}@#{sha_from}...#{sha_to}"
end
end
@@ -137,26 +137,25 @@ describe CommitRange, models: true do
end
describe '#has_been_reverted?' do
- it 'returns true if the commit has been reverted' do
- issue = create(:issue)
+ let(:issue) { create(:issue) }
+ let(:user) { issue.author }
+ it 'returns true if the commit has been reverted' do
create(:note_on_issue,
noteable: issue,
system: true,
- note: commit1.revert_description,
+ note: commit1.revert_description(user),
project: issue.project)
expect_any_instance_of(Commit).to receive(:reverts_commit?).
- with(commit1).
+ with(commit1, user).
and_return(true)
- expect(commit1.has_been_reverted?(nil, issue)).to eq(true)
+ expect(commit1.has_been_reverted?(user, issue)).to eq(true)
end
it 'returns false a commit has not been reverted' do
- issue = create(:issue)
-
- expect(commit1.has_been_reverted?(nil, issue)).to eq(false)
+ expect(commit1.has_been_reverted?(user, issue)).to eq(false)
end
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 62b77ef86c5..b81fab0372a 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -34,24 +34,30 @@ describe Commit, models: true do
end
describe '#to_reference' do
+ let(:project) { create(:project, path: 'sample-project') }
+ let(:commit) { project.commit }
+
it 'returns a String reference to the object' do
expect(commit.to_reference).to eq commit.id
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(commit.to_reference(another_project)).to eq "sample-project@#{commit.id}"
end
end
describe '#reference_link_text' do
+ let(:project) { create(:project, path: 'sample-project') }
+ let(:commit) { project.commit }
+
it 'returns a String reference to the object' do
expect(commit.reference_link_text).to eq commit.short_id
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(commit.reference_link_text(cross)).to eq "#{project.to_reference}@#{commit.short_id}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(commit.reference_link_text(another_project)).to eq "sample-project@#{commit.short_id}"
end
end
@@ -173,25 +179,26 @@ eos
describe '#reverts_commit?' do
let(:another_commit) { double(:commit, revert_description: "This reverts commit #{commit.sha}") }
+ let(:user) { commit.author }
- it { expect(commit.reverts_commit?(another_commit)).to be_falsy }
+ it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy }
context 'commit has no description' do
before { allow(commit).to receive(:description?).and_return(false) }
- it { expect(commit.reverts_commit?(another_commit)).to be_falsy }
+ it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy }
end
context "another_commit's description does not revert commit" do
before { allow(commit).to receive(:description).and_return("Foo Bar") }
- it { expect(commit.reverts_commit?(another_commit)).to be_falsy }
+ it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy }
end
context "another_commit's description reverts commit" do
before { allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") }
- it { expect(commit.reverts_commit?(another_commit)).to be_truthy }
+ it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy }
end
context "another_commit's description reverts merged merge request" do
@@ -201,7 +208,7 @@ eos
allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar")
end
- it { expect(commit.reverts_commit?(another_commit)).to be_truthy }
+ it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy }
end
end
@@ -297,4 +304,21 @@ eos
expect(commit.uri_type('this/path/doesnt/exist')).to be_nil
end
end
+
+ describe '.from_hash' do
+ let(:new_commit) { described_class.from_hash(commit.to_hash, project) }
+
+ it 'returns a Commit' do
+ expect(new_commit).to be_an_instance_of(described_class)
+ end
+
+ it 'wraps a Gitlab::Git::Commit' do
+ expect(new_commit.raw).to be_an_instance_of(Gitlab::Git::Commit)
+ end
+
+ it 'stores the correct commit fields' do
+ expect(new_commit.id).to eq(commit.id)
+ expect(new_commit.message).to eq(commit.message)
+ end
+ end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 80c2a1bc7a9..1ec08c2a9d0 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -175,7 +175,7 @@ describe CommitStatus, models: true do
end
it 'returns statuses without what we want to ignore' do
- is_expected.to eq(statuses.values_at(1, 2, 4, 5, 6, 8, 9))
+ is_expected.to eq(statuses.values_at(0, 1, 2, 3, 4, 5, 6, 8, 9))
end
end
@@ -200,49 +200,6 @@ describe CommitStatus, models: true do
end
end
- describe '#stages' do
- before do
- create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success'
- create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed'
- create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running'
- create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success'
- end
-
- context 'stages list' do
- subject { CommitStatus.where(pipeline: pipeline).stages }
-
- it 'returns ordered list of stages' do
- is_expected.to eq(%w[build test deploy])
- end
- end
-
- context 'stages with statuses' do
- subject { CommitStatus.where(pipeline: pipeline).latest.stages_status }
-
- it 'returns list of stages with statuses' do
- is_expected.to eq({
- 'build' => 'failed',
- 'test' => 'success',
- 'deploy' => 'running'
- })
- end
-
- context 'when build is retried' do
- before do
- create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success'
- end
-
- it 'ignores a previous state' do
- is_expected.to eq({
- 'build' => 'success',
- 'test' => 'success',
- 'deploy' => 'running'
- })
- end
- end
- end
- end
-
describe '#commit' do
it 'returns commit pipeline has been created for' do
expect(commit_status.commit).to eq project.commit
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 87bffbdc54e..4d0f51fe82a 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -48,7 +48,7 @@ describe HasStatus do
[create(type, status: :failed, allow_failure: true)]
end
- it { is_expected.to eq 'success' }
+ it { is_expected.to eq 'skipped' }
end
context 'success and canceled' do
@@ -123,4 +123,100 @@ describe HasStatus do
it_behaves_like 'build status summary'
end
end
+
+ context 'for scope with one status' do
+ shared_examples 'having a job' do |status|
+ %i[ci_build generic_commit_status].each do |type|
+ context "when it's #{status} #{type} job" do
+ let!(:job) { create(type, status) }
+
+ describe ".#{status}" do
+ it 'contains the job' do
+ expect(CommitStatus.public_send(status).all).
+ to contain_exactly(job)
+ end
+ end
+
+ describe '.relevant' do
+ if status == :created
+ it 'contains nothing' do
+ expect(CommitStatus.relevant.all).to be_empty
+ end
+ else
+ it 'contains the job' do
+ expect(CommitStatus.relevant.all).to contain_exactly(job)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ %i[created running pending success
+ failed canceled skipped].each do |status|
+ it_behaves_like 'having a job', status
+ end
+ end
+
+ context 'for scope with more statuses' do
+ shared_examples 'containing the job' do |status|
+ %i[ci_build generic_commit_status].each do |type|
+ context "when it's #{status} #{type} job" do
+ let!(:job) { create(type, status) }
+
+ it 'contains the job' do
+ is_expected.to contain_exactly(job)
+ end
+ end
+ end
+ end
+
+ shared_examples 'not containing the job' do |status|
+ %i[ci_build generic_commit_status].each do |type|
+ context "when it's #{status} #{type} job" do
+ let!(:job) { create(type, status) }
+
+ it 'contains nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+ end
+
+ describe '.running_or_pending' do
+ subject { CommitStatus.running_or_pending }
+
+ %i[running pending].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[created failed success].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
+
+ describe '.finished' do
+ subject { CommitStatus.finished }
+
+ %i[success failed canceled].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[created running pending].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
+
+ describe '.cancelable' do
+ subject { CommitStatus.cancelable }
+
+ %i[running pending created].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[failed success skipped canceled].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 6f84bffe046..4fa06a8c60a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -35,7 +35,7 @@ describe Issue, "Issuable" do
it { is_expected.to validate_presence_of(:iid) }
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to validate_length_of(:title).is_at_least(0).is_at_most(255) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
end
describe "Scope" do
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
new file mode 100644
index 00000000000..b556135532f
--- /dev/null
+++ b/spec/models/concerns/routable_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe Group, 'Routable' do
+ let!(:group) { create(:group) }
+
+ describe 'Validations' do
+ it { is_expected.to validate_presence_of(:route) }
+ end
+
+ describe 'Associations' do
+ it { is_expected.to have_one(:route).dependent(:destroy) }
+ end
+
+ describe 'Callbacks' do
+ it 'creates route record on create' do
+ expect(group.route.path).to eq(group.path)
+ end
+
+ it 'updates route record on path change' do
+ group.update_attributes(path: 'wow')
+
+ expect(group.route.path).to eq('wow')
+ end
+
+ it 'ensure route path uniqueness across different objects' do
+ create(:group, parent: group, path: 'xyz')
+ duplicate = build(:project, namespace: group, path: 'xyz')
+
+ expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Route path has already been taken, Route is invalid')
+ end
+ end
+
+ describe '.find_by_full_path' do
+ let!(:nested_group) { create(:group, parent: group) }
+
+ it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
+ it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
+ it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
+ it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
+ end
+
+ describe '.where_full_path_in' do
+ context 'without any paths' do
+ it 'returns an empty relation' do
+ expect(described_class.where_full_path_in([])).to eq([])
+ end
+ end
+
+ context 'without any valid paths' do
+ it 'returns an empty relation' do
+ expect(described_class.where_full_path_in(%w[unknown])).to eq([])
+ end
+ end
+
+ context 'with valid paths' do
+ let!(:nested_group) { create(:group, parent: group) }
+
+ it 'returns the projects matching the paths' do
+ result = described_class.where_full_path_in([group.to_param, nested_group.to_param])
+
+ expect(result).to contain_exactly(group, nested_group)
+ end
+
+ it 'returns projects regardless of the casing of paths' do
+ result = described_class.where_full_path_in([group.to_param.upcase, nested_group.to_param.upcase])
+
+ expect(result).to contain_exactly(group, nested_group)
+ end
+ end
+ end
+end
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index 7691d690db0..7771785ead3 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#code', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
context 'with deployment' do
generate_cycle_analytics_spec(
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index f649b44d367..5ed3d37f2fb 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#issue', models: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :issue,
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index 2cdefbeef21..baf3e3241a1 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#plan', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :plan,
diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb
index 1f5e5cab92d..21b9c6e7150 100644
--- a/spec/models/cycle_analytics/production_spec.rb
+++ b/spec/models/cycle_analytics/production_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#production', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :production,
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index 0ed080a42b1..158621d59a4 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#review', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :review,
diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb
index af1c4477ddb..dad653964b7 100644
--- a/spec/models/cycle_analytics/staging_spec.rb
+++ b/spec/models/cycle_analytics/staging_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#staging', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :staging,
diff --git a/spec/models/cycle_analytics/summary_spec.rb b/spec/models/cycle_analytics/summary_spec.rb
index 9d67bc82cba..725bc68b25f 100644
--- a/spec/models/cycle_analytics/summary_spec.rb
+++ b/spec/models/cycle_analytics/summary_spec.rb
@@ -4,7 +4,7 @@ describe CycleAnalytics::Summary, models: true do
let(:project) { create(:project) }
let(:from) { Time.now }
let(:user) { create(:user, :admin) }
- subject { described_class.new(project, from: from) }
+ subject { described_class.new(project, user, from: from) }
describe "#new_issues" do
it "finds the number of issues created after the 'from date'" do
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index 02ddfeed9c1..2313724e8f3 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#test', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :test,
diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb
index 0142706d140..bc32fadd391 100644
--- a/spec/models/discussion_spec.rb
+++ b/spec/models/discussion_spec.rb
@@ -521,6 +521,15 @@ describe Discussion, model: true do
end
end
+ describe "#first_note_to_resolve" do
+ it "returns the first not that still needs to be resolved" do
+ allow(first_note).to receive(:to_be_resolved?).and_return(false)
+ allow(second_note).to receive(:to_be_resolved?).and_return(true)
+
+ expect(subject.first_note_to_resolve).to eq(second_note)
+ end
+ end
+
describe "#collapsed?" do
context "when a diff discussion" do
before do
@@ -590,4 +599,23 @@ describe Discussion, model: true do
end
end
end
+
+ describe "#truncated_diff_lines" do
+ let(:truncated_lines) { subject.truncated_diff_lines }
+
+ context "when diff is greater than allowed number of truncated diff lines " do
+ it "returns fewer lines" do
+ expect(subject.diff_lines.count).to be > described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
+
+ expect(truncated_lines.count).to be <= described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
+ end
+ end
+
+ context "when some diff lines are meta" do
+ it "returns no meta lines" do
+ expect(subject.diff_lines).to include(be_meta)
+ expect(truncated_lines).not_to include(be_meta)
+ end
+ end
+ end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index d06665197db..c8170164898 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -13,9 +13,9 @@ describe Environment, models: true do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
- it { is_expected.to validate_length_of(:name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
- it { is_expected.to validate_length_of(:external_url).is_within(0..255) }
+ it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
# To circumvent a not null violation of the name column:
# https://github.com/thoughtbot/shoulda-matchers/issues/336
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index b684053cd02..f8660da031d 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -260,6 +260,24 @@ describe Event, models: true do
end
end
+ describe '#authored_by?' do
+ let(:event) { build(:event) }
+
+ it 'returns true when the event author and user are the same' do
+ expect(event.authored_by?(event.author)).to eq(true)
+ end
+
+ it 'returns false when passing nil as an argument' do
+ expect(event.authored_by?(nil)).to eq(false)
+ end
+
+ it 'returns false when the given user is not the author of the event' do
+ user = double(:user, id: -1)
+
+ expect(event.authored_by?(user)).to eq(false)
+ end
+ end
+
def create_event(project, user, attrs = {})
data = {
before: Gitlab::Git::BLANK_SHA,
diff --git a/spec/models/group_label_spec.rb b/spec/models/group_label_spec.rb
index 2369658bf78..668aa6fb357 100644
--- a/spec/models/group_label_spec.rb
+++ b/spec/models/group_label_spec.rb
@@ -37,6 +37,16 @@ describe GroupLabel, models: true do
end
end
+ context 'cross-project' do
+ let(:namespace) { build_stubbed(:namespace) }
+ let(:source_project) { build_stubbed(:empty_project, name: 'project-1', namespace: namespace) }
+ let(:target_project) { build_stubbed(:empty_project, name: 'project-2', namespace: namespace) }
+
+ it 'returns a String reference to the object' do
+ expect(label.to_reference(source_project, target_project)).to eq %(project-1~#{label.id})
+ end
+ end
+
context 'using invalid format' do
it 'raises error' do
expect { label.to_reference(format: :invalid) }
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 1613a586a2c..850b1a3cf1e 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -271,4 +271,11 @@ describe Group, models: true do
expect(group.web_url).to include("groups/#{group.name}")
end
end
+
+ describe 'nested group' do
+ subject { create(:group, :nested) }
+
+ it { is_expected.to be_valid }
+ it { expect(subject.parent).to be_kind_of(Group) }
+ end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 89e93dce8c5..24e216329a9 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -43,14 +43,16 @@ describe Issue, models: true do
end
describe '#to_reference' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:issue) { build(:issue, iid: 1, project: project) }
+
it 'returns a String reference to the object' do
- expect(subject.to_reference).to eq "##{subject.iid}"
+ expect(issue.to_reference).to eq "#1"
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(subject.to_reference(cross)).
- to eq "#{subject.project.to_reference}##{subject.iid}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(issue.to_reference(another_project)).to eq "sample-project#1"
end
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 90731f55470..2a33d819138 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -7,9 +7,13 @@ describe Key, models: true do
describe "Validation" do
it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
+
it { is_expected.to validate_presence_of(:key) }
- it { is_expected.to validate_length_of(:title).is_within(0..255) }
- it { is_expected.to validate_length_of(:key).is_within(0..5000) }
+ it { is_expected.to validate_length_of(:key).is_at_most(5000) }
+ it { is_expected.to allow_value('ssh-foo').for(:key) }
+ it { is_expected.to allow_value('ecdsa-foo').for(:key) }
+ it { is_expected.not_to allow_value('foo-bar').for(:key) }
end
describe "Methods" do
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index e5007424041..eb876d105da 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -77,24 +77,13 @@ describe MergeRequestDiff, models: true do
end
describe '#commits_sha' do
- shared_examples 'returning all commits SHA' do
- it 'returns all commits SHA' do
- commits_sha = subject.commits_sha
+ it 'returns all commits SHA using serialized commits' do
+ subject.st_commits = [
+ { id: 'sha1' },
+ { id: 'sha2' }
+ ]
- expect(commits_sha).to eq(subject.commits.map(&:sha))
- end
- end
-
- context 'when commits were loaded' do
- before do
- subject.commits
- end
-
- it_behaves_like 'returning all commits SHA'
- end
-
- context 'when commits were not loaded' do
- it_behaves_like 'returning all commits SHA'
+ expect(subject.commits_sha).to eq(['sha1', 'sha2'])
end
end
@@ -113,4 +102,15 @@ describe MergeRequestDiff, models: true do
expect(diffs.size).to eq(3)
end
end
+
+ describe '#commits_count' do
+ it 'returns number of commits using serialized commits' do
+ subject.st_commits = [
+ { id: 'sha1' },
+ { id: 'sha2' }
+ ]
+
+ expect(subject.commits_count).to eq 2
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 58ccd056328..8b730be91fd 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -31,7 +31,7 @@ describe MergeRequest, models: true do
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
- context "Validation of merge user with Merge When Build succeeds" do
+ context "Validation of merge user with Merge When Pipeline Succeeds" do
it "allows user to be nil when the feature is disabled" do
expect(subject).to be_valid
end
@@ -142,13 +142,16 @@ describe MergeRequest, models: true do
end
describe '#to_reference' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:merge_request) { build(:merge_request, target_project: project, iid: 1) }
+
it 'returns a String reference to the object' do
- expect(subject.to_reference).to eq "!#{subject.iid}"
+ expect(merge_request.to_reference).to eq "!1"
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(subject.to_reference(cross)).to eq "#{subject.source_project.to_reference}!#{subject.iid}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(merge_request.to_reference(another_project)).to eq "sample-project!1"
end
end
@@ -557,20 +560,17 @@ describe MergeRequest, models: true do
end
describe '#commits_sha' do
- let(:commit0) { double('commit0', sha: 'sha1') }
- let(:commit1) { double('commit1', sha: 'sha2') }
- let(:commit2) { double('commit2', sha: 'sha3') }
-
before do
- allow(subject.merge_request_diff).to receive(:commits).and_return([commit0, commit1, commit2])
+ allow(subject.merge_request_diff).to receive(:commits_sha).
+ and_return(['sha1'])
end
- it 'returns sha of commits' do
- expect(subject.commits_sha).to contain_exactly('sha1', 'sha2', 'sha3')
+ it 'delegates to merge request diff' do
+ expect(subject.commits_sha).to eq ['sha1']
end
end
- describe '#pipeline' do
+ describe '#head_pipeline' do
describe 'when the source project exists' do
it 'returns the latest pipeline' do
pipeline = double(:ci_pipeline, ref: 'master')
@@ -581,7 +581,7 @@ describe MergeRequest, models: true do
with('master', '123abc').
and_return(pipeline)
- expect(subject.pipeline).to eq(pipeline)
+ expect(subject.head_pipeline).to eq(pipeline)
end
end
@@ -589,7 +589,7 @@ describe MergeRequest, models: true do
it 'returns nil' do
allow(subject).to receive(:source_project).and_return(nil)
- expect(subject.pipeline).to be_nil
+ expect(subject.head_pipeline).to be_nil
end
end
end
@@ -857,7 +857,7 @@ describe MergeRequest, models: true do
context 'and a failed pipeline is associated' do
before do
pipeline.update(status: 'failed')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_falsey }
@@ -866,7 +866,7 @@ describe MergeRequest, models: true do
context 'and a successful pipeline is associated' do
before do
pipeline.update(status: 'success')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -875,7 +875,7 @@ describe MergeRequest, models: true do
context 'and a skipped pipeline is associated' do
before do
pipeline.update(status: 'skipped')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -883,7 +883,7 @@ describe MergeRequest, models: true do
context 'when no pipeline is associated' do
before do
- allow(subject).to receive(:pipeline) { nil }
+ allow(subject).to receive(:head_pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -896,7 +896,7 @@ describe MergeRequest, models: true do
context 'and a failed pipeline is associated' do
before do
pipeline.statuses << create(:commit_status, status: 'failed', project: project)
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -904,7 +904,7 @@ describe MergeRequest, models: true do
context 'when no pipeline is associated' do
before do
- allow(subject).to receive(:pipeline) { nil }
+ allow(subject).to receive(:head_pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -1127,6 +1127,46 @@ describe MergeRequest, models: true do
allow(subject).to receive(:diff_discussions).and_return([first_discussion, second_discussion, third_discussion])
end
+ describe '#resolvable_discussions' do
+ before do
+ allow(first_discussion).to receive(:to_be_resolved?).and_return(true)
+ allow(second_discussion).to receive(:to_be_resolved?).and_return(false)
+ allow(third_discussion).to receive(:to_be_resolved?).and_return(false)
+ end
+
+ it 'includes only discussions that need to be resolved' do
+ expect(subject.resolvable_discussions).to eq([first_discussion])
+ end
+ end
+
+ describe '#discussions_can_be_resolved_by? user' do
+ let(:user) { build(:user) }
+
+ context 'all discussions can be resolved by the user' do
+ before do
+ allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(third_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ end
+
+ it 'allows a user to resolve the discussions' do
+ expect(subject.discussions_can_be_resolved_by?(user)).to be(true)
+ end
+ end
+
+ context 'one discussion cannot be resolved by the user' do
+ before do
+ allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(third_discussion).to receive(:can_resolve?).with(user).and_return(false)
+ end
+
+ it 'allows a user to resolve the discussions' do
+ expect(subject.discussions_can_be_resolved_by?(user)).to be(false)
+ end
+ end
+ end
+
describe "#discussions_resolvable?" do
context "when all discussions are unresolvable" do
before do
@@ -1440,4 +1480,26 @@ describe MergeRequest, models: true do
end
end
end
+
+ describe '#has_commits?' do
+ before do
+ allow(subject.merge_request_diff).to receive(:commits_count).
+ and_return(2)
+ end
+
+ it 'returns true when merge request diff has commits' do
+ expect(subject.has_commits?).to be_truthy
+ end
+ end
+
+ describe '#has_no_commits?' do
+ before do
+ allow(subject.merge_request_diff).to receive(:commits_count).
+ and_return(0)
+ end
+
+ it 'returns true when merge request diff has 0 commits' do
+ expect(subject.has_no_commits?).to be_truthy
+ end
+ end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index a4bfe851dfb..0cc2efae5f9 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -246,4 +246,18 @@ describe Milestone, models: true do
end
end
end
+
+ describe '#to_reference' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:milestone) { build(:milestone, iid: 1, project: project) }
+
+ it 'returns a String reference to the object' do
+ expect(milestone.to_reference).to eq "%1"
+ end
+
+ it 'supports a cross-project reference' do
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(milestone.to_reference(another_project)).to eq "sample-project%1"
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 431b3e4435f..7f82e85563b 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -4,11 +4,18 @@ describe Namespace, models: true do
let!(:namespace) { create(:namespace) }
it { is_expected.to have_many :projects }
- it { is_expected.to validate_presence_of :name }
+
+ it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name) }
- it { is_expected.to validate_presence_of :path }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
+
+ it { is_expected.to validate_length_of(:description).is_at_most(255) }
+
+ it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path) }
- it { is_expected.to validate_presence_of :owner }
+ it { is_expected.to validate_length_of(:path).is_at_most(255) }
+
+ it { is_expected.to validate_presence_of(:owner) }
describe "Mass assignment" do
end
@@ -117,4 +124,12 @@ describe Namespace, models: true do
expect(Namespace.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name")
end
end
+
+ describe '#full_path' do
+ let(:group) { create(:group) }
+ let(:nested_group) { create(:group, parent: group) }
+
+ it { expect(group.full_path).to eq(group.path) }
+ it { expect(nested_group.full_path).to eq("#{group.path}/#{nested_group.path}") }
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index e6b6e7c0634..17a15b12dcb 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -223,7 +223,7 @@ describe Note, models: true do
let(:note) do
create :note,
noteable: ext_issue, project: ext_proj,
- note: "Mentioned in issue #{private_issue.to_reference(ext_proj)}",
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
system: true
end
diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb
index 18c9d449ee5..4d538cac007 100644
--- a/spec/models/project_label_spec.rb
+++ b/spec/models/project_label_spec.rb
@@ -105,14 +105,14 @@ describe ProjectLabel, models: true do
context 'using name' do
it 'returns cross reference with label name' do
expect(label.to_reference(project, format: :name))
- .to eq %Q(#{label.project.to_reference}~"#{label.name}")
+ .to eq %Q(#{label.project.path_with_namespace}~"#{label.name}")
end
end
context 'using id' do
it 'returns cross reference with label id' do
expect(label.to_reference(project, format: :id))
- .to eq %Q(#{label.project.to_reference}~#{label.id})
+ .to eq %Q(#{label.project.path_with_namespace}~#{label.id})
end
end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index f5da967cd14..862e3a72a73 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -68,7 +68,7 @@ describe JiraService, models: true do
end
end
- describe "Execute" do
+ describe '#close_issue' do
let(:custom_base_url) { 'http://custom_url' }
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -101,12 +101,10 @@ describe JiraService, models: true do
@jira_service.save
project_issues_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123'
- @project_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/project/GitLabProject'
@transitions_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions'
@comment_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/comment'
@remote_link_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/remotelink'
- WebMock.stub_request(:get, @project_url)
WebMock.stub_request(:get, project_issues_url)
WebMock.stub_request(:post, @transitions_url)
WebMock.stub_request(:post, @comment_url)
@@ -114,7 +112,7 @@ describe JiraService, models: true do
end
it "calls JIRA API" do
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /Issue solved with/
@@ -124,7 +122,7 @@ describe JiraService, models: true do
# Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
# for more information
it "creates Remote Link reference in JIRA for comment" do
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
# Creates comment
expect(WebMock).to have_requested(:post, @comment_url)
@@ -146,7 +144,7 @@ describe JiraService, models: true do
it "does not send comment or remote links to issues already closed" do
allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(true)
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).not_to have_requested(:post, @comment_url)
expect(WebMock).not_to have_requested(:post, @remote_link_url)
@@ -155,7 +153,7 @@ describe JiraService, models: true do
it "references the GitLab commit/merge request" do
stub_config_setting(base_url: custom_base_url)
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
@@ -170,7 +168,7 @@ describe JiraService, models: true do
{ script_name: '/gitlab' }
end
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
@@ -178,19 +176,33 @@ describe JiraService, models: true do
end
it "calls the api with jira_issue_transition_id" do
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @transitions_url).with(
body: /custom-id/
).once
end
+ end
- context "when testing" do
- it "tries to get jira project" do
- @jira_service.execute(nil)
+ describe '#test_settings' do
+ let(:jira_service) do
+ described_class.new(
+ url: 'http://jira.example.com',
+ username: 'gitlab_jira_username',
+ password: 'gitlab_jira_password',
+ project_key: 'GitLabProject'
+ )
+ end
+ let(:project_url) { 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/project/GitLabProject' }
- expect(WebMock).to have_requested(:get, @project_url)
- end
+ before do
+ WebMock.stub_request(:get, project_url)
+ end
+
+ it 'tries to get JIRA project' do
+ jira_service.test_settings
+
+ expect(WebMock).to have_requested(:get, project_url)
end
end
diff --git a/spec/models/project_services/pipeline_email_service_spec.rb b/spec/models/project_services/pipeline_email_service_spec.rb
index 4f56bceda44..7c8824485f5 100644
--- a/spec/models/project_services/pipeline_email_service_spec.rb
+++ b/spec/models/project_services/pipeline_email_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe PipelinesEmailService do
+ include EmailHelpers
+
let(:pipeline) do
create(:ci_pipeline, project: project, sha: project.commit('master').sha)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index da38254d1bc..21ff238841e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -131,14 +131,18 @@ describe Project, models: true do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
- it { is_expected.to validate_length_of(:name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
- it { is_expected.to validate_length_of(:path).is_within(0..255) }
- it { is_expected.to validate_length_of(:description).is_within(0..2000) }
+ it { is_expected.to validate_length_of(:path).is_at_most(255) }
+
+ it { is_expected.to validate_length_of(:description).is_at_most(2000) }
+
it { is_expected.to validate_presence_of(:creator) }
+
it { is_expected.to validate_presence_of(:namespace) }
+
it { is_expected.to validate_presence_of(:repository_storage) }
it 'does not allow new projects beyond user limits' do
@@ -258,10 +262,70 @@ describe Project, models: true do
end
describe '#to_reference' do
- let(:project) { create(:empty_project) }
+ let(:owner) { create(:user, name: 'Gitlab') }
+ let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
+ let(:project) { create(:empty_project, path: 'sample-project', namespace: namespace) }
+
+ context 'when nil argument' do
+ it 'returns nil' do
+ expect(project.to_reference).to be_nil
+ end
+ end
+
+ context 'when same project argument' do
+ it 'returns nil' do
+ expect(project.to_reference(project)).to be_nil
+ end
+ end
+
+ context 'when cross namespace project argument' do
+ let(:another_namespace_project) { create(:empty_project, name: 'another-project') }
+
+ it 'returns complete path to the project' do
+ expect(project.to_reference(another_namespace_project)).to eq 'sample-namespace/sample-project'
+ end
+ end
+
+ context 'when same namespace / cross-project argument' do
+ let(:another_project) { create(:empty_project, namespace: namespace) }
+
+ it 'returns complete path to the project' do
+ expect(project.to_reference(another_project)).to eq 'sample-project'
+ end
+ end
+ end
+
+ describe '#to_human_reference' do
+ let(:owner) { create(:user, name: 'Gitlab') }
+ let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
+ let(:project) { create(:empty_project, name: 'Sample project', namespace: namespace) }
+
+ context 'when nil argument' do
+ it 'returns nil' do
+ expect(project.to_human_reference).to be_nil
+ end
+ end
- it 'returns a String reference to the object' do
- expect(project.to_reference).to eq project.path_with_namespace
+ context 'when same project argument' do
+ it 'returns nil' do
+ expect(project.to_human_reference(project)).to be_nil
+ end
+ end
+
+ context 'when cross namespace project argument' do
+ let(:another_namespace_project) { create(:empty_project, name: 'another-project') }
+
+ it 'returns complete name with namespace of the project' do
+ expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project'
+ end
+ end
+
+ context 'when same namespace / cross-project argument' do
+ let(:another_project) { create(:empty_project, namespace: namespace) }
+
+ it 'returns name of the project' do
+ expect(project.to_human_reference(another_project)).to eq 'Sample project'
+ end
end
end
@@ -361,10 +425,15 @@ describe Project, models: true do
describe '#get_issue' do
let(:project) { create(:empty_project) }
let!(:issue) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :developer]
+ end
context 'with default issues tracker' do
it 'returns an issue' do
- expect(project.get_issue(issue.iid)).to eq issue
+ expect(project.get_issue(issue.iid, user)).to eq issue
end
it 'returns count of open issues' do
@@ -372,7 +441,12 @@ describe Project, models: true do
end
it 'returns nil when no issue found' do
- expect(project.get_issue(999)).to be_nil
+ expect(project.get_issue(999, user)).to be_nil
+ end
+
+ it "returns nil when user doesn't have access" do
+ user = create(:user)
+ expect(project.get_issue(issue.iid, user)).to eq nil
end
end
@@ -382,7 +456,7 @@ describe Project, models: true do
end
it 'returns an ExternalIssue' do
- issue = project.get_issue('FOO-1234')
+ issue = project.get_issue('FOO-1234', user)
expect(issue).to be_kind_of(ExternalIssue)
expect(issue.iid).to eq 'FOO-1234'
expect(issue.project).to eq project
@@ -404,35 +478,6 @@ describe Project, models: true do
end
end
- describe '.find_with_namespace' do
- context 'with namespace' do
- before do
- @group = create :group, name: 'gitlab'
- @project = create(:project, name: 'gitlabhq', namespace: @group)
- end
-
- it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) }
- it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) }
- it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil }
- end
-
- context 'when multiple projects using a similar name exist' do
- let(:group) { create(:group, name: 'gitlab') }
-
- let!(:project1) do
- create(:empty_project, name: 'gitlab1', path: 'gitlab', namespace: group)
- end
-
- let!(:project2) do
- create(:empty_project, name: 'gitlab2', path: 'GITLAB', namespace: group)
- end
-
- it 'returns the row where the path matches literally' do
- expect(Project.find_with_namespace('gitlab/GITLAB')).to eq(project2)
- end
- end
- end
-
describe '#to_param' do
context 'with namespace' do
before do
@@ -1474,39 +1519,6 @@ describe Project, models: true do
end
end
- describe '.where_paths_in' do
- context 'without any paths' do
- it 'returns an empty relation' do
- expect(Project.where_paths_in([])).to eq([])
- end
- end
-
- context 'without any valid paths' do
- it 'returns an empty relation' do
- expect(Project.where_paths_in(%w[foo])).to eq([])
- end
- end
-
- context 'with valid paths' do
- let!(:project1) { create(:project) }
- let!(:project2) { create(:project) }
-
- it 'returns the projects matching the paths' do
- projects = Project.where_paths_in([project1.path_with_namespace,
- project2.path_with_namespace])
-
- expect(projects).to contain_exactly(project1, project2)
- end
-
- it 'returns projects regardless of the casing of paths' do
- projects = Project.where_paths_in([project1.path_with_namespace.upcase,
- project2.path_with_namespace.upcase])
-
- expect(projects).to contain_exactly(project1, project2)
- end
- end
- end
-
describe 'change_head' do
let(:project) { create(:project) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 04afb8ebc98..b5a42edd192 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -768,7 +768,6 @@ describe Repository, models: true do
expect(repository).not_to receive(:expire_root_ref_cache)
expect(repository).not_to receive(:expire_emptiness_caches)
expect(repository).to receive(:expire_branches_cache)
- expect(repository).to receive(:expire_has_visible_content_cache)
repository.update_branch_with_hooks(user, 'new-feature') { new_rev }
end
@@ -786,7 +785,6 @@ describe Repository, models: true do
expect(empty_repository).to receive(:expire_root_ref_cache)
expect(empty_repository).to receive(:expire_emptiness_caches)
expect(empty_repository).to receive(:expire_branches_cache)
- expect(empty_repository).to receive(:expire_has_visible_content_cache)
empty_repository.commit_file(user, 'CHANGELOG', 'Changelog!',
'Updates file content', 'master', false)
@@ -829,15 +827,6 @@ describe Repository, models: true do
expect(subject).to eq(true)
end
-
- it 'caches the output' do
- expect(repository).to receive(:branch_count).
- once.
- and_return(3)
-
- repository.has_visible_content?
- repository.has_visible_content?
- end
end
end
@@ -918,20 +907,6 @@ describe Repository, models: true do
end
end
- describe '#expire_has_visible_content_cache' do
- it 'expires the visible content cache' do
- repository.has_visible_content?
-
- expect(repository).to receive(:branch_count).
- once.
- and_return(0)
-
- repository.expire_has_visible_content_cache
-
- expect(repository.has_visible_content?).to eq(false)
- end
- end
-
describe '#expire_branch_cache' do
# This method is private but we need it for testing purposes. Sadly there's
# no other proper way of testing caching operations.
@@ -967,7 +942,6 @@ describe Repository, models: true do
allow(repository).to receive(:empty?).and_return(true)
expect(cache).to receive(:expire).with(:empty?)
- expect(repository).to receive(:expire_has_visible_content_cache)
repository.expire_emptiness_caches
end
@@ -976,7 +950,6 @@ describe Repository, models: true do
allow(repository).to receive(:empty?).and_return(false)
expect(cache).not_to receive(:expire).with(:empty?)
- expect(repository).not_to receive(:expire_has_visible_content_cache)
repository.expire_emptiness_caches
end
@@ -1203,16 +1176,16 @@ describe Repository, models: true do
end
describe '#after_create_branch' do
- it 'flushes the visible content cache' do
- expect(repository).to receive(:expire_has_visible_content_cache)
+ it 'expires the branch caches' do
+ expect(repository).to receive(:expire_branches_cache)
repository.after_create_branch
end
end
describe '#after_remove_branch' do
- it 'flushes the visible content cache' do
- expect(repository).to receive(:expire_has_visible_content_cache)
+ it 'expires the branch caches' do
+ expect(repository).to receive(:expire_branches_cache)
repository.after_remove_branch
end
@@ -1303,32 +1276,36 @@ describe Repository, models: true do
repository.add_tag(user, '8.5', 'master', 'foo')
end
- it 'does not create a tag when a pre-hook fails' do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
-
- expect do
- repository.add_tag(user, '8.5', 'master', 'foo')
- end.to raise_error(GitHooksService::PreReceiveError)
+ it 'returns a Gitlab::Git::Tag object' do
+ tag = repository.add_tag(user, '8.5', 'master', 'foo')
- repository.expire_tags_cache
- expect(repository.find_tag('8.5')).to be_nil
+ expect(tag).to be_a(Gitlab::Git::Tag)
end
- it 'passes tag SHA to hooks' do
- spy = GitHooksService.new
- allow(GitHooksService).to receive(:new).and_return(spy)
- allow(spy).to receive(:execute).and_call_original
+ it 'passes commit SHA to pre-receive and update hooks,\
+ and tag SHA to post-receive hook' do
+ pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', repository.path_to_repo)
+ update_hook = Gitlab::Git::Hook.new('update', repository.path_to_repo)
+ post_receive_hook = Gitlab::Git::Hook.new('post-receive', repository.path_to_repo)
- tag = repository.add_tag(user, '8.5', 'master', 'foo')
+ allow(Gitlab::Git::Hook).to receive(:new).
+ and_return(pre_receive_hook, update_hook, post_receive_hook)
- expect(spy).to have_received(:execute).
- with(anything, anything, anything, tag.target, anything)
- end
+ allow(pre_receive_hook).to receive(:trigger).and_call_original
+ allow(update_hook).to receive(:trigger).and_call_original
+ allow(post_receive_hook).to receive(:trigger).and_call_original
- it 'returns a Gitlab::Git::Tag object' do
tag = repository.add_tag(user, '8.5', 'master', 'foo')
- expect(tag).to be_a(Gitlab::Git::Tag)
+ commit_sha = repository.commit('master').id
+ tag_sha = tag.target
+
+ expect(pre_receive_hook).to have_received(:trigger).
+ with(anything, anything, commit_sha, anything)
+ expect(update_hook).to have_received(:trigger).
+ with(anything, anything, commit_sha, anything)
+ expect(post_receive_hook).to have_received(:trigger).
+ with(anything, anything, tag_sha, anything)
end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
new file mode 100644
index 00000000000..6f491fdf9a0
--- /dev/null
+++ b/spec/models/route_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe Route, models: true do
+ let!(:group) { create(:group) }
+ let!(:route) { group.route }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:source) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:source) }
+ it { is_expected.to validate_presence_of(:path) }
+ it { is_expected.to validate_uniqueness_of(:path) }
+ end
+
+ describe '#rename_children' do
+ let!(:nested_group) { create(:group, path: "test", parent: group) }
+ let!(:deep_nested_group) { create(:group, path: "foo", parent: nested_group) }
+
+ it "updates children routes with new path" do
+ route.update_attributes(path: 'bar')
+
+ expect(described_class.exists?(path: 'bar')).to be_truthy
+ expect(described_class.exists?(path: 'bar/test')).to be_truthy
+ expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy
+ end
+ end
+end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index f62f6bacbaa..7425a897769 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -23,9 +23,9 @@ describe Snippet, models: true do
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to validate_length_of(:title).is_within(0..255) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
- it { is_expected.to validate_length_of(:file_name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:file_name).is_at_most(255) }
it { is_expected.to validate_presence_of(:content) }
@@ -33,16 +33,51 @@ describe Snippet, models: true do
end
describe '#to_reference' do
+ context 'when snippet belongs to a project' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:snippet) { build(:snippet, id: 1, project: project) }
+
+ it 'returns a String reference to the object' do
+ expect(snippet.to_reference).to eq "$1"
+ end
+
+ it 'supports a cross-project reference' do
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(snippet.to_reference(another_project)).to eq "sample-project$1"
+ end
+ end
+
+ context 'when snippet does not belong to a project' do
+ let(:snippet) { build(:snippet, id: 1, project: nil) }
+
+ it 'returns a String reference to the object' do
+ expect(snippet.to_reference).to eq "$1"
+ end
+
+ it 'still returns shortest reference when project arg present' do
+ another_project = build(:project, name: 'another-project')
+ expect(snippet.to_reference(another_project)).to eq "$1"
+ end
+ end
+ end
+
+ describe '#file_name' do
let(:project) { create(:empty_project) }
- let(:snippet) { create(:snippet, project: project) }
- it 'returns a String reference to the object' do
- expect(snippet.to_reference).to eq "$#{snippet.id}"
+ context 'file_name is nil' do
+ let(:snippet) { create(:snippet, project: project, file_name: nil) }
+
+ it 'returns an empty string' do
+ expect(snippet.file_name).to eq ''
+ end
end
- it 'supports a cross-project reference' do
- cross = double('project')
- expect(snippet.to_reference(cross)).to eq "#{project.to_reference}$#{snippet.id}"
+ context 'file_name is not nil' do
+ let(:snippet) { create(:snippet, project: project, file_name: 'foo.txt') }
+
+ it 'returns the file_name' do
+ expect(snippet.file_name).to eq 'foo.txt'
+ end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 91826e5884d..8b20ee81614 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -79,7 +79,7 @@ describe User, models: true do
it { is_expected.to allow_value(0).for(:projects_limit) }
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
- it { is_expected.to validate_length_of(:bio).is_within(0..255) }
+ it { is_expected.to validate_length_of(:bio).is_at_most(255) }
it_behaves_like 'an object with email-formated attributes', :email do
subject { build(:user) }
@@ -575,6 +575,23 @@ describe User, models: true do
end
end
end
+
+ describe '#require_ssh_key?' do
+ protocol_and_expectation = {
+ 'http' => false,
+ 'ssh' => true,
+ '' => true,
+ }
+
+ protocol_and_expectation.each do |protocol, expected|
+ it "has correct require_ssh_key?" do
+ stub_application_setting(enabled_git_access_protocol: protocol)
+ user = build(:user)
+
+ expect(user.require_ssh_key?).to eq(expected)
+ end
+ end
+ end
end
describe '.find_by_any_email' do
@@ -710,17 +727,6 @@ describe User, models: true do
end
end
- describe 'by_username_or_id' do
- let(:user1) { create(:user, username: 'foo') }
-
- it "gets the correct user" do
- expect(User.by_username_or_id(user1.id)).to eq(user1)
- expect(User.by_username_or_id('foo')).to eq(user1)
- expect(User.by_username_or_id(-1)).to be_nil
- expect(User.by_username_or_id('bar')).to be_nil
- end
- end
-
describe '.find_by_ssh_key_id' do
context 'using an existing SSH key ID' do
let(:user) { create(:user) }
@@ -1349,4 +1355,31 @@ describe User, models: true do
expect(projects).to be_empty
end
end
+
+ describe '#refresh_authorized_projects', redis: true do
+ let(:project1) { create(:empty_project) }
+ let(:project2) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ project1.team << [user, :reporter]
+ project2.team << [user, :guest]
+
+ user.project_authorizations.delete_all
+ user.refresh_authorized_projects
+ end
+
+ it 'refreshes the list of authorized projects' do
+ expect(user.project_authorizations.count).to eq(2)
+ end
+
+ it 'sets the authorized_projects_populated column' do
+ expect(user.authorized_projects_populated).to eq(true)
+ end
+
+ it 'stores the correct access levels' do
+ expect(user.project_authorizations.where(access_level: Gitlab::Access::GUEST).exists?).to eq(true)
+ expect(user.project_authorizations.where(access_level: Gitlab::Access::REPORTER).exists?).to eq(true)
+ end
+ end
end