Newer
Older
subject { create(:merge_request) }
describe 'associations' do
it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') }
it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') }
it { is_expected.to belong_to(:merge_user).class_name("User") }
it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) }
end
describe 'modules' do
subject { described_class }
it { is_expected.to include_module(InternalId) }
it { is_expected.to include_module(Issuable) }
it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Sortable) }
it { is_expected.to include_module(Taskable) }
end
describe "act_as_paranoid" do
it { is_expected.to have_db_column(:deleted_at) }
it { is_expected.to have_db_index(:deleted_at) }
end
describe 'validation' 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
it "allows user to be nil when the feature is disabled" do
expect(subject).to be_valid
end
it "is invalid without merge user" do
subject.merge_when_build_succeeds = true
expect(subject).not_to be_valid
end
it "is valid with merge user" do
subject.merge_when_build_succeeds = true
subject.merge_user = build(:user)
expect(subject).to be_valid
end
end
it { is_expected.to respond_to(:unchecked?) }
it { is_expected.to respond_to(:can_be_merged?) }
it { is_expected.to respond_to(:cannot_be_merged?) }
it { is_expected.to respond_to(:merge_params) }
it { is_expected.to respond_to(:merge_when_build_succeeds) }
describe '.in_projects' do
it 'returns the merge requests for a set of projects' do
expect(described_class.in_projects(Project.all)).to eq([subject])
end
end
describe '#target_sha' do
context 'when the target branch does not exist anymore' do
Douglas Barbosa Alexandre
committed
let(:project) { create(:project) }
subject { create(:merge_request, source_project: project, target_project: project) }
before do
project.repository.raw_repository.delete_branch(subject.target_branch)
end
it 'returns nil' do
expect(subject.target_sha).to be_nil
end
end
end
describe '#source_sha' do
let(:last_branch_commit) { subject.source_project.repository.commit(subject.source_branch) }
context 'with diffs' do
subject { create(:merge_request, :with_diffs) }
it 'returns the sha of the source branch last commit' do
expect(subject.source_sha).to eq(last_branch_commit.sha)
end
end
context 'without diffs' do
subject { create(:merge_request, :without_diffs) }
it 'returns the sha of the source branch last commit' do
expect(subject.source_sha).to eq(last_branch_commit.sha)
end
end
context 'when the merge request is being created' do
subject { build(:merge_request, source_branch: nil, compare_commits: []) }
it 'returns nil' do
expect(subject.source_sha).to be_nil
end
end
end
describe '#to_reference' do
it 'returns a String reference to the object' do
expect(subject.to_reference).to eq "!#{subject.iid}"
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}"
end
describe "#mr_and_commit_notes" do
let!(:merge_request) { create(:merge_request) }
allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] }
create(:note_on_commit, commit_id: merge_request.commits.first.id,
project: merge_request.project)
create(:note, noteable: merge_request, project: merge_request.project)
end
it "should include notes for commits" do
expect(merge_request.commits).not_to be_empty
expect(merge_request.mr_and_commit_notes.count).to eq(2)

Valery Sizov
committed
it "should include notes for commits from target project as well" do
create(:note_on_commit, commit_id: merge_request.commits.first.id,
project: merge_request.target_project)

Valery Sizov
committed
expect(merge_request.commits).not_to be_empty
expect(merge_request.mr_and_commit_notes.count).to eq(3)
end
describe '#is_being_reassigned?' do
it 'returns true if the merge_request assignee has changed' do
subject.assignee = create(:user)
expect(subject.is_being_reassigned?).to be_truthy
end
it 'returns false if the merge request assignee has not changed' do
expect(subject.is_being_reassigned?).to be_falsey
describe '#for_fork?' do
it 'returns true if the merge request is for a fork' do
subject.source_project = create(:project, namespace: create(:group))
subject.target_project = create(:project, namespace: create(:group))
expect(subject.for_fork?).to be_truthy
it 'returns false if is not for a fork' do
expect(subject.for_fork?).to be_falsey
describe 'detection of issues to be closed' do
let(:issue0) { create :issue, project: subject.project }
let(:issue1) { create :issue, project: subject.project }
let(:commit0) { double('commit0', safe_message: "Fixes #{issue0.to_reference}") }
let(:commit1) { double('commit1', safe_message: "Fixes #{issue0.to_reference}") }
let(:commit2) { double('commit2', safe_message: "Fixes #{issue1.to_reference}") }
subject.project.team << [subject.author, :developer]
allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
end
it 'accesses the set of issues that will be closed on acceptance' do
allow(subject.project).to receive(:default_branch).
and_return(subject.target_branch)
closed = subject.closes_issues
expect(closed).to include(issue0, issue1)
end
it 'only lists issues as to be closed if it targets the default branch' do
allow(subject.project).to receive(:default_branch).and_return('master')
subject.target_branch = 'something-else'
expect(subject.closes_issues).to be_empty
it 'detects issues mentioned in the description' do
issue2 = create(:issue, project: subject.project)
subject.description = "Closes #{issue2.to_reference}"
allow(subject.project).to receive(:default_branch).
and_return(subject.target_branch)
expect(subject.closes_issues).to include(issue2)
describe "#work_in_progress?" do
['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
it "detects the '#{wip_prefix}' prefix" do
subject.title = "#{wip_prefix}#{subject.title}"
expect(subject.work_in_progress?).to eq true
it "doesn't detect WIP for words starting with WIP" do
subject.title = "Wipwap #{subject.title}"
expect(subject.work_in_progress?).to eq false
end
it "doesn't detect WIP for words containing with WIP" do
subject.title = "WupWipwap #{subject.title}"
expect(subject.work_in_progress?).to eq false
it "doesn't detect WIP by default" do
expect(subject.work_in_progress?).to eq false
end
end
let(:user) { create(:user) }
let(:user2) { create(:user) }
before do
subject.source_project.team << [user, :master]
subject.source_branch = "feature"
subject.target_branch = "master"
subject.save!
end
it "can't be removed when its a protected branch" do
allow(subject.source_project).to receive(:protected_branch?).and_return(true)
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
it "cant remove a root ref" do
subject.source_branch = "master"
subject.target_branch = "feature"
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
it "is unable to remove the source branch for a project the user cannot push to" do
expect(subject.can_remove_source_branch?(user2)).to be_falsey
end
it "can be removed if the last commit is the head of the source branch" do
allow(subject.source_project).to receive(:commit).and_return(subject.last_commit)
expect(subject.can_remove_source_branch?(user)).to be_truthy
it "cannot be removed if the last commit is not also the head of the source branch" do
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
describe "#reset_merge_when_build_succeeds" do
let(:merge_if_green) do
create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user),
merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" }
end
it "sets the item to false" do
merge_if_green.reset_merge_when_build_succeeds
merge_if_green.reload
expect(merge_if_green.merge_when_build_succeeds).to be_falsey
expect(merge_if_green.merge_params["should_remove_source_branch"]).to be_nil
expect(merge_if_green.merge_params["commit_message"]).to be_nil
Kirill Zaitsev
committed
let(:attrs_hash) { subject.hook_attrs.to_h }
[:source, :target].each do |key|
describe "#{key} key" do
include_examples 'project hook data', project_key: key do
let(:data) { attrs_hash }
let(:project) { subject.send("#{key}_project") }
end
end
end
it "has all the required keys" do
Kirill Zaitsev
committed
expect(attrs_hash).to include(:source)
expect(attrs_hash).to include(:target)
expect(attrs_hash).to include(:last_commit)
expect(attrs_hash).to include(:work_in_progress)
end
describe '#diverged_commits_count' do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
context 'when the target branch does not exist anymore' do
Douglas Barbosa Alexandre
committed
subject { create(:merge_request, source_project: project, target_project: project) }
before do
project.repository.raw_repository.delete_branch(subject.target_branch)
subject.reload
end
it 'does not crash' do
expect{ subject.diverged_commits_count }.not_to raise_error
end
it 'returns 0' do
expect(subject.diverged_commits_count).to eq(0)
end
end
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
context 'diverged on same repository' do
subject(:merge_request_with_divergence) { create(:merge_request, :diverged, source_project: project, target_project: project) }
it 'counts commits that are on target branch but not on source branch' do
expect(subject.diverged_commits_count).to eq(5)
end
end
context 'diverged on fork' do
subject(:merge_request_fork_with_divergence) { create(:merge_request, :diverged, source_project: fork_project, target_project: project) }
it 'counts commits that are on target branch but not on source branch' do
expect(subject.diverged_commits_count).to eq(5)
end
end
context 'rebased on fork' do
subject(:merge_request_rebased) { create(:merge_request, :rebased, source_project: fork_project, target_project: project) }
it 'counts commits that are on target branch but not on source branch' do
expect(subject.diverged_commits_count).to eq(0)
end
end
describe 'caching' do
before(:example) do
allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new)
end
it 'caches the output' do
expect(subject).to receive(:compute_diverged_commits_count).
once.
and_return(2)
subject.diverged_commits_count
subject.diverged_commits_count
end
it 'invalidates the cache when the source sha changes' do
expect(subject).to receive(:compute_diverged_commits_count).
twice.
and_return(2)
subject.diverged_commits_count
allow(subject).to receive(:source_sha).and_return('123abc')
subject.diverged_commits_count
end
it 'invalidates the cache when the target sha changes' do
expect(subject).to receive(:compute_diverged_commits_count).
twice.
and_return(2)
subject.diverged_commits_count
allow(subject).to receive(:target_sha).and_return('123abc')
subject.diverged_commits_count
end
end
it_behaves_like 'an editable mentionable' do
subject { create(:merge_request) }
let(:backref_text) { "merge request #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
subject { create :merge_request, :simple }
describe '#pipeline' do
describe 'when the source project exists' do
it 'returns the latest commit' do
commit = double(:commit, id: '123abc')
pipeline = double(:ci_pipeline, ref: 'master')
allow(subject).to receive(:last_commit).and_return(commit)
expect(subject.source_project).to receive(:pipeline).
and_return(pipeline)
expect(subject.pipeline).to eq(pipeline)
end
end
describe 'when the source project does not exist' do
it 'returns nil' do
allow(subject).to receive(:source_project).and_return(nil)
Kamil Trzcinski
committed
expect(subject.pipeline).to be_nil
describe '#participants' do
let(:project) { create(:project, :public) }
let(:mr) do
create(:merge_request, source_project: project, target_project: project)
end
let!(:note1) do
create(:note_on_merge_request, noteable: mr, project: project, note: 'a')
end
let!(:note2) do
create(:note_on_merge_request, noteable: mr, project: project, note: 'b')
end
it 'includes the merge request author' do
expect(mr.participants).to include(mr.author)
end
it 'includes the authors of the notes' do
expect(mr.participants).to include(note1.author, note2.author)
end
end
describe 'cached counts' do
it 'updates when assignees change' do
user1 = create(:user)
user2 = create(:user)
mr = create(:merge_request, assignee: user1)
expect(user1.assigned_open_merge_request_count).to eq(1)
expect(user2.assigned_open_merge_request_count).to eq(0)
mr.assignee = user2
mr.save
expect(user1.assigned_open_merge_request_count).to eq(0)
expect(user2.assigned_open_merge_request_count).to eq(1)
end
end