merge_request_spec.rb 62.2 KB
Newer Older
D
Dmitriy Zaporozhets 已提交
1 2
require 'spec_helper'

3
describe MergeRequest do
4
  include RepoHelpers
B
Bob Van Landuyt 已提交
5
  include ProjectForksHelper
6

7 8
  subject { create(:merge_request) }

R
Robert Speicher 已提交
9
  describe 'associations' do
10 11
    it { is_expected.to belong_to(:target_project).class_name('Project') }
    it { is_expected.to belong_to(:source_project).class_name('Project') }
12
    it { is_expected.to belong_to(:merge_user).class_name("User") }
13
    it { is_expected.to belong_to(:assignee) }
14
    it { is_expected.to have_many(:merge_request_diffs) }
R
Robert Speicher 已提交
15 16
  end

17 18 19 20 21 22 23 24 25 26
  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

Z
Zeger-Jan van de Weg 已提交
27 28 29 30 31
  describe "act_as_paranoid" do
    it { is_expected.to have_db_column(:deleted_at) }
    it { is_expected.to have_db_index(:deleted_at) }
  end

32
  describe 'validation' do
33 34
    it { is_expected.to validate_presence_of(:target_branch) }
    it { is_expected.to validate_presence_of(:source_branch) }
Z
Zeger-Jan van de Weg 已提交
35

36
    context "Validation of merge user with Merge When Pipeline Succeeds" do
Z
Zeger-Jan van de Weg 已提交
37 38 39 40 41
      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
J
James Lopez 已提交
42
        subject.merge_when_pipeline_succeeds = true
Z
Zeger-Jan van de Weg 已提交
43 44 45 46
        expect(subject).not_to be_valid
      end

      it "is valid with merge user" do
J
James Lopez 已提交
47
        subject.merge_when_pipeline_succeeds = true
Z
Zeger-Jan van de Weg 已提交
48 49 50 51 52
        subject.merge_user = build(:user)

        expect(subject).to be_valid
      end
    end
53 54 55

    context 'for forks' do
      let(:project) { create(:project) }
B
Bob Van Landuyt 已提交
56 57
      let(:fork1) { fork_project(project) }
      let(:fork2) { fork_project(project) }
58 59 60 61 62 63 64 65

      it 'allows merge requests for sibling-forks' do
        subject.source_project = fork1
        subject.target_project = fork2

        expect(subject).to be_valid
      end
    end
D
Dmitriy Zaporozhets 已提交
66 67
  end

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  describe 'callbacks' do
    describe '#ensure_merge_request_metrics' do
      it 'creates metrics after saving' do
        merge_request = create(:merge_request)

        expect(merge_request.metrics).to be_persisted
        expect(MergeRequest::Metrics.count).to eq(1)
      end

      it 'does not duplicate metrics for a merge request' do
        merge_request = create(:merge_request)

        merge_request.mark_as_merged!

        expect(MergeRequest::Metrics.count).to eq(1)
      end
    end
  end

R
Robert Speicher 已提交
87
  describe 'respond to' do
88 89 90
    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?) }
91
    it { is_expected.to respond_to(:merge_params) }
J
James Lopez 已提交
92
    it { is_expected.to respond_to(:merge_when_pipeline_succeeds) }
93
  end
A
Andrey Kumanyaev 已提交
94

95 96 97 98 99 100
  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

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  describe '.set_latest_merge_request_diff_ids!' do
    def create_merge_request_with_diffs(source_branch, diffs: 2)
      params = {
        target_project: project,
        target_branch: 'master',
        source_project: project,
        source_branch: source_branch
      }

      create(:merge_request, params).tap do |mr|
        diffs.times { mr.merge_request_diffs.create }
      end
    end

    let(:project) { create(:project) }

    it 'sets IDs for merge requests, whether they are already set or not' do
      merge_requests = [
        create_merge_request_with_diffs('feature'),
        create_merge_request_with_diffs('feature-conflict'),
        create_merge_request_with_diffs('wip', diffs: 0),
        create_merge_request_with_diffs('csv')
      ]

      merge_requests.take(2).each do |merge_request|
        merge_request.update_column(:latest_merge_request_diff_id, nil)
      end

      expected = merge_requests.map do |merge_request|
        merge_request.merge_request_diffs.maximum(:id)
      end

      expect { project.merge_requests.set_latest_merge_request_diff_ids! }
        .to change { merge_requests.map { |mr| mr.reload.latest_merge_request_diff_id } }.to(expected)
    end
  end

138
  describe '#target_branch_sha' do
139
    let(:project) { create(:project, :repository) }
140

141
    subject { create(:merge_request, source_project: project, target_project: project) }
142

143
    context 'when the target branch does not exist' do
144
      before do
145
        project.repository.rm_branch(subject.author, subject.target_branch)
146
        subject.clear_memoized_shas
147
      end
148 149

      it 'returns nil' do
150
        expect(subject.target_branch_sha).to be_nil
151 152
      end
    end
153 154 155 156 157 158

    it 'returns memoized value' do
      subject.target_branch_sha = '8ffb3c15a5475e59ae909384297fede4badcb4c7'

      expect(subject.target_branch_sha).to eq '8ffb3c15a5475e59ae909384297fede4badcb4c7'
    end
159 160
  end

161 162 163 164 165
  describe '#card_attributes' do
    it 'includes the author name' do
      allow(subject).to receive(:author).and_return(double(name: 'Robert'))
      allow(subject).to receive(:assignee).and_return(nil)

166 167
      expect(subject.card_attributes)
        .to eq({ 'Author' => 'Robert', 'Assignee' => nil })
168 169 170 171 172 173
    end

    it 'includes the assignee name' do
      allow(subject).to receive(:author).and_return(double(name: 'Robert'))
      allow(subject).to receive(:assignee).and_return(double(name: 'Douwe'))

174 175
      expect(subject.card_attributes)
        .to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
176 177 178
    end
  end

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
  describe '#assignee_ids' do
    it 'returns an array of the assigned user id' do
      subject.assignee_id = 123

      expect(subject.assignee_ids).to eq([123])
    end
  end

  describe '#assignee_ids=' do
    it 'sets assignee_id to the last id in the array' do
      subject.assignee_ids = [123, 456]

      expect(subject.assignee_id).to eq(456)
    end
  end

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
  describe '#assignee_or_author?' do
    let(:user) { create(:user) }

    it 'returns true for a user that is assigned to a merge request' do
      subject.assignee = user

      expect(subject.assignee_or_author?(user)).to eq(true)
    end

    it 'returns true for a user that is the author of a merge request' do
      subject.author = user

      expect(subject.assignee_or_author?(user)).to eq(true)
    end

    it 'returns false for a user that is not the assignee or author' do
      expect(subject.assignee_or_author?(user)).to eq(false)
    end
  end

215 216
  describe '#cache_merge_request_closes_issues!' do
    before do
217
      subject.project.add_developer(subject.author)
218 219 220 221 222 223 224 225
      subject.target_branch = subject.project.default_branch
    end

    it 'caches closed issues' do
      issue  = create :issue, project: subject.project
      commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
      allow(subject).to receive(:commits).and_return([commit])

226
      expect { subject.cache_merge_request_closes_issues!(subject.author) }.to change(subject.merge_requests_closing_issues, :count).by(1)
227 228
    end

229 230 231 232
    context 'when both internal and external issue trackers are enabled' do
      before do
        subject.project.has_external_issue_tracker = true
        subject.project.save!
233
        create(:jira_service, project: subject.project)
234 235 236 237 238 239
      end

      it 'does not cache issues from external trackers' do
        issue  = ExternalIssue.new('JIRA-123', subject.project)
        commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
        allow(subject).to receive(:commits).and_return([commit])
240

241
        expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to raise_error
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
        expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count)
      end

      it 'caches an internal issue' do
        issue  = create(:issue, project: subject.project)
        commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
        allow(subject).to receive(:commits).and_return([commit])

        expect { subject.cache_merge_request_closes_issues!(subject.author) }
          .to change(subject.merge_requests_closing_issues, :count).by(1)
      end
    end

    context 'when only external issue tracker enabled' do
      before do
        subject.project.has_external_issue_tracker = true
        subject.project.issues_enabled = false
        subject.project.save!
      end

      it 'does not cache issues from external trackers' do
        issue  = ExternalIssue.new('JIRA-123', subject.project)
        commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
        allow(subject).to receive(:commits).and_return([commit])

        expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count)
      end

      it 'does not cache an internal issue' do
        issue  = create(:issue, project: subject.project)
        commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
        allow(subject).to receive(:commits).and_return([commit])

        expect { subject.cache_merge_request_closes_issues!(subject.author) }
          .not_to change(subject.merge_requests_closing_issues, :count)
      end
278 279 280
    end
  end

281
  describe '#source_branch_sha' do
282
    let(:last_branch_commit) { subject.source_project.repository.commit(Gitlab::Git::BRANCH_REF_PREFIX + subject.source_branch) }
283 284 285 286

    context 'with diffs' do
      subject { create(:merge_request, :with_diffs) }
      it 'returns the sha of the source branch last commit' do
287
        expect(subject.source_branch_sha).to eq(last_branch_commit.sha)
288 289 290
      end
    end

291 292 293
    context 'without diffs' do
      subject { create(:merge_request, :without_diffs) }
      it 'returns the sha of the source branch last commit' do
294
        expect(subject.source_branch_sha).to eq(last_branch_commit.sha)
295
      end
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

      context 'when there is a tag name matching the branch name' do
        let(:tag_name) { subject.source_branch }

        it 'returns the sha of the source branch last commit' do
          subject.source_project.repository.add_tag(subject.author,
                                                    tag_name,
                                                    subject.target_branch_sha,
                                                    'Add a tag')

          expect(subject.source_branch_sha).to eq(last_branch_commit.sha)

          subject.source_project.repository.rm_tag(subject.author, tag_name)
        end
      end
311 312
    end

313 314 315
    context 'when the merge request is being created' do
      subject { build(:merge_request, source_branch: nil, compare_commits: []) }
      it 'returns nil' do
316
        expect(subject.source_branch_sha).to be_nil
317 318
      end
    end
319 320 321 322 323 324

    it 'returns memoized value' do
      subject.source_branch_sha = '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b'

      expect(subject.source_branch_sha).to eq '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b'
    end
325 326
  end

327
  describe '#to_reference' do
328
    let(:project) { build(:project, name: 'sample-project') }
329 330
    let(:merge_request) { build(:merge_request, target_project: project, iid: 1) }

331
    it 'returns a String reference to the object' do
332
      expect(merge_request.to_reference).to eq "!1"
333 334 335
    end

    it 'supports a cross-project reference' do
336
      another_project = build(:project, name: 'another-project', namespace: project.namespace)
337
      expect(merge_request.to_reference(another_project)).to eq "sample-project!1"
338
    end
339 340

    it 'returns a String reference with the full path' do
341
      expect(merge_request.to_reference(full: true)).to eq(project.full_path + '!1')
342
    end
343
  end
344

345
  describe '#raw_diffs' do
S
Sean McGivern 已提交
346 347 348 349 350 351 352
    let(:merge_request) { build(:merge_request) }
    let(:options) { { paths: ['a/b', 'b/a', 'c/*'] } }

    context 'when there are MR diffs' do
      it 'delegates to the MR diffs' do
        merge_request.merge_request_diff = MergeRequestDiff.new

353
        expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(options)
S
Sean McGivern 已提交
354

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
        merge_request.raw_diffs(options)
      end
    end

    context 'when there are no MR diffs' do
      it 'delegates to the compare object' do
        merge_request.compare = double(:compare)

        expect(merge_request.compare).to receive(:raw_diffs).with(options)

        merge_request.raw_diffs(options)
      end
    end
  end

S
Sean McGivern 已提交
370 371 372 373 374 375
  describe '#diffs' do
    let(:merge_request) { build(:merge_request) }
    let(:options) { { paths: ['a/b', 'b/a', 'c/*'] } }

    context 'when there are MR diffs' do
      it 'delegates to the MR diffs' do
376
        merge_request.save
S
Sean McGivern 已提交
377

378
        expect(merge_request.merge_request_diff).to receive(:raw_diffs).with(hash_including(options))
S
Sean McGivern 已提交
379 380 381 382 383 384

        merge_request.diffs(options)
      end
    end

    context 'when there are no MR diffs' do
385
      it 'delegates to the compare object, setting expanded: true' do
S
Sean McGivern 已提交
386 387
        merge_request.compare = double(:compare)

388
        expect(merge_request.compare).to receive(:diffs).with(options.merge(expanded: true))
S
Sean McGivern 已提交
389 390 391 392 393 394

        merge_request.diffs(options)
      end
    end
  end

S
Sean McGivern 已提交
395 396 397 398 399 400
  describe '#diff_size' do
    let(:merge_request) do
      build(:merge_request, source_branch: 'expand-collapse-files', target_branch: 'master')
    end

    context 'when there are MR diffs' do
401
      it 'returns the correct count' do
S
Sean McGivern 已提交
402
        merge_request.save
403 404

        expect(merge_request.diff_size).to eq('105')
S
Sean McGivern 已提交
405 406
      end

407 408 409 410 411
      it 'returns the correct overflow count' do
        allow(Commit).to receive(:max_diff_options).and_return(max_files: 2)
        merge_request.save

        expect(merge_request.diff_size).to eq('2+')
S
Sean McGivern 已提交
412 413 414
      end

      it 'does not perform highlighting' do
415 416
        merge_request.save

S
Sean McGivern 已提交
417 418 419 420 421 422 423
        expect(Gitlab::Diff::Highlight).not_to receive(:new)

        merge_request.diff_size
      end
    end

    context 'when there are no MR diffs' do
424
      def set_compare(merge_request)
S
Sean McGivern 已提交
425 426 427 428 429 430 431 432 433 434
        merge_request.compare = CompareService.new(
          merge_request.source_project,
          merge_request.source_branch
        ).execute(
          merge_request.target_project,
          merge_request.target_branch
        )
      end

      it 'returns the correct count' do
435 436 437 438 439 440 441 442 443 444
        set_compare(merge_request)

        expect(merge_request.diff_size).to eq('105')
      end

      it 'returns the correct overflow count' do
        allow(Commit).to receive(:max_diff_options).and_return(max_files: 2)
        set_compare(merge_request)

        expect(merge_request.diff_size).to eq('2+')
S
Sean McGivern 已提交
445 446 447
      end

      it 'does not perform highlighting' do
448 449
        set_compare(merge_request)

S
Sean McGivern 已提交
450 451 452 453 454 455 456
        expect(Gitlab::Diff::Highlight).not_to receive(:new)

        merge_request.diff_size
      end
    end
  end

457
  describe "#related_notes" do
458
    let!(:merge_request) { create(:merge_request) }
459 460

    before do
461
      allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] }
462 463
      create(:note_on_commit, commit_id: merge_request.commits.first.id,
                              project: merge_request.project)
D
Dmitriy Zaporozhets 已提交
464
      create(:note, noteable: merge_request, project: merge_request.project)
465 466
    end

467
    it "includes notes for commits" do
468
      expect(merge_request.commits).not_to be_empty
469
      expect(merge_request.related_notes.count).to eq(2)
470
    end
471

472
    it "includes notes for commits from target project as well" do
473 474 475
      create(:note_on_commit, commit_id: merge_request.commits.first.id,
                              project: merge_request.target_project)

476
      expect(merge_request.commits).not_to be_empty
477
      expect(merge_request.related_notes.count).to eq(3)
478
    end
479
  end
480

I
Izaak Alpert 已提交
481 482
  describe '#for_fork?' do
    it 'returns true if the merge request is for a fork' do
483 484
      subject.source_project = build_stubbed(:project, namespace: create(:group))
      subject.target_project = build_stubbed(:project, namespace: create(:group))
I
Izaak Alpert 已提交
485

486
      expect(subject.for_fork?).to be_truthy
I
Izaak Alpert 已提交
487
    end
D
Dmitriy Zaporozhets 已提交
488

I
Izaak Alpert 已提交
489
    it 'returns false if is not for a fork' do
490
      expect(subject.for_fork?).to be_falsey
I
Izaak Alpert 已提交
491 492 493
    end
  end

494
  describe '#closes_issues' do
495 496
    let(:issue0) { create :issue, project: subject.project }
    let(:issue1) { create :issue, project: subject.project }
497 498 499 500

    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}") }
501 502

    before do
503
      subject.project.add_developer(subject.author)
504
      allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
505 506
    end

507
    it 'accesses the set of issues that will be closed on acceptance' do
508 509
      allow(subject.project).to receive(:default_branch)
        .and_return(subject.target_branch)
510

511
      closed = subject.closes_issues
512

513 514
      expect(closed).to include(issue0, issue1)
    end
515

516 517 518
    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'
519

520
      expect(subject.closes_issues).to be_empty
521
    end
522
  end
523

524
  describe '#issues_mentioned_but_not_closing' do
525 526 527 528
    let(:closing_issue) { create :issue, project: subject.project }
    let(:mentioned_issue) { create :issue, project: subject.project }

    let(:commit) { double('commit', safe_message: "Fixes #{closing_issue.to_reference}") }
529

530
    it 'detects issues mentioned in description but not closed' do
531
      subject.project.add_developer(subject.author)
532
      subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}"
533

534
      allow(subject).to receive(:commits).and_return([commit])
535 536
      allow(subject.project).to receive(:default_branch)
        .and_return(subject.target_branch)
537

538
      expect(subject.issues_mentioned_but_not_closing(subject.author)).to match_array([mentioned_issue])
539
    end
540 541 542

    context 'when the project has an external issue tracker' do
      before do
543
        subject.project.add_developer(subject.author)
544 545 546 547 548 549 550 551 552 553 554 555 556
        commit = double(:commit, safe_message: 'Fixes TEST-3')

        create(:jira_service, project: subject.project)

        allow(subject).to receive(:commits).and_return([commit])
        allow(subject).to receive(:description).and_return('Is related to TEST-2 and TEST-3')
        allow(subject.project).to receive(:default_branch).and_return(subject.target_branch)
      end

      it 'detects issues mentioned in description but not closed' do
        expect(subject.issues_mentioned_but_not_closing(subject.author).map(&:to_s)).to match_array(['TEST-2'])
      end
    end
557 558
  end

559
  describe "#work_in_progress?" do
560 561 562
    ['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}"
563
        expect(subject.work_in_progress?).to eq true
564
      end
T
Ted Hogan 已提交
565 566
    end

567 568
    it "doesn't detect WIP for words starting with WIP" do
      subject.title = "Wipwap #{subject.title}"
569
      expect(subject.work_in_progress?).to eq false
570 571
    end

572 573
    it "doesn't detect WIP for words containing with WIP" do
      subject.title = "WupWipwap #{subject.title}"
574
      expect(subject.work_in_progress?).to eq false
575 576
    end

577
    it "doesn't detect WIP by default" do
578
      expect(subject.work_in_progress?).to eq false
579 580 581
    end
  end

T
Thomas Balthazar 已提交
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
  describe "#wipless_title" do
    ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
      it "removes the '#{wip_prefix}' prefix" do
        wipless_title = subject.title
        subject.title = "#{wip_prefix}#{subject.title}"

        expect(subject.wipless_title).to eq wipless_title
      end

      it "is satisfies the #work_in_progress? method" do
        subject.title = "#{wip_prefix}#{subject.title}"
        subject.title = subject.wipless_title

        expect(subject.work_in_progress?).to eq false
      end
    end
  end

  describe "#wip_title" do
    it "adds the WIP: prefix to the title" do
      wip_title = "WIP: #{subject.title}"

      expect(subject.wip_title).to eq wip_title
605
    end
T
Thomas Balthazar 已提交
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

    it "does not add the WIP: prefix multiple times" do
      wip_title = "WIP: #{subject.title}"
      subject.title = subject.wip_title
      subject.title = subject.wip_title

      expect(subject.wip_title).to eq wip_title
    end

    it "is satisfies the #work_in_progress? method" do
      subject.title = subject.wip_title

      expect(subject.work_in_progress?).to eq true
    end
  end

Z
Zeger-Jan van de Weg 已提交
622
  describe '#can_remove_source_branch?' do
623 624
    set(:user) { create(:user) }
    set(:merge_request) { create(:merge_request, :simple) }
625

626
    subject { merge_request }
627

628 629
    before do
      subject.source_project.add_master(user)
Z
Zeger-Jan van de Weg 已提交
630
    end
631

Z
Zeger-Jan van de Weg 已提交
632
    it "can't be removed when its a protected branch" do
633
      allow(ProtectedBranch).to receive(:protected?).and_return(true)
634

635 636 637
      expect(subject.can_remove_source_branch?(user)).to be_falsey
    end

638
    it "can't remove a root ref" do
639
      subject.update(source_branch: 'master', target_branch: 'feature')
640 641 642 643

      expect(subject.can_remove_source_branch?(user)).to be_falsey
    end

Z
Zeger-Jan van de Weg 已提交
644
    it "is unable to remove the source branch for a project the user cannot push to" do
645 646
      user2 = create(:user)

Z
Zeger-Jan van de Weg 已提交
647 648 649
      expect(subject.can_remove_source_branch?(user2)).to be_falsey
    end

650
    it "can be removed if the last commit is the head of the source branch" do
651
      allow(subject).to receive(:source_branch_head).and_return(subject.diff_head_commit)
652

Z
Zeger-Jan van de Weg 已提交
653
      expect(subject.can_remove_source_branch?(user)).to be_truthy
654
    end
655 656

    it "cannot be removed if the last commit is not also the head of the source branch" do
657
      subject.clear_memoized_shas
658 659
      subject.source_branch = "lfs"

660 661
      expect(subject.can_remove_source_branch?(user)).to be_falsey
    end
662 663
  end

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
  describe '#merge_commit_message' do
    it 'includes merge information as the title' do
      request = build(:merge_request, source_branch: 'source', target_branch: 'target')

      expect(request.merge_commit_message)
        .to match("Merge branch 'source' into 'target'\n\n")
    end

    it 'includes its title in the body' do
      request = build(:merge_request, title: 'Remove all technical debt')

      expect(request.merge_commit_message)
        .to match("Remove all technical debt\n\n")
    end

679
    it 'includes its closed issues in the body' do
680
      issue = create(:issue, project: subject.project)
681

682
      subject.project.add_developer(subject.author)
683
      subject.description = "This issue Closes #{issue.to_reference}"
684

685 686
      allow(subject.project).to receive(:default_branch)
        .and_return(subject.target_branch)
687 688

      expect(subject.merge_commit_message)
689
        .to match("Closes #{issue.to_reference}")
690 691 692 693 694 695
    end

    it 'includes its reference in the body' do
      request = build_stubbed(:merge_request)

      expect(request.merge_commit_message)
696
        .to match("See merge request #{request.to_reference(full: true)}")
697 698 699 700 701 702 703
    end

    it 'excludes multiple linebreak runs when description is blank' do
      request = build(:merge_request, title: 'Title', description: nil)

      expect(request.merge_commit_message).not_to match("Title\n\n\n\n")
    end
704 705 706 707 708 709 710 711 712 713 714 715 716 717

    it 'includes its description in the body' do
      request = build(:merge_request, description: 'By removing all code')

      expect(request.merge_commit_message(include_description: true))
        .to match("By removing all code\n\n")
    end

    it 'does not includes its description in the body' do
      request = build(:merge_request, description: 'By removing all code')

      expect(request.merge_commit_message)
        .not_to match("By removing all code\n\n")
    end
718 719
  end

J
James Lopez 已提交
720
  describe "#reset_merge_when_pipeline_succeeds" do
721
    let(:merge_if_green) do
J
James Lopez 已提交
722
      create :merge_request, merge_when_pipeline_succeeds: true, merge_user: create(:user),
723 724
                             merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" }
    end
Z
Zeger-Jan van de Weg 已提交
725

726
    it "sets the item to false" do
J
James Lopez 已提交
727
      merge_if_green.reset_merge_when_pipeline_succeeds
728 729
      merge_if_green.reload

J
James Lopez 已提交
730
      expect(merge_if_green.merge_when_pipeline_succeeds).to be_falsey
731 732
      expect(merge_if_green.merge_params["should_remove_source_branch"]).to be_nil
      expect(merge_if_green.merge_params["commit_message"]).to be_nil
733 734 735
    end
  end

736
  describe '#hook_attrs' do
737 738 739 740 741 742 743 744
    it 'delegates to Gitlab::HookData::MergeRequestBuilder#build' do
      builder = double

      expect(Gitlab::HookData::MergeRequestBuilder)
        .to receive(:new).with(subject).and_return(builder)
      expect(builder).to receive(:build)

      subject.hook_attrs
745
    end
746 747 748
  end

  describe '#diverged_commits_count' do
749
    let(:project)      { create(:project, :repository) }
B
Bob Van Landuyt 已提交
750
    let(:forked_project) { fork_project(project, nil, repository: true) }
751

752
    context 'when the target branch does not exist anymore' do
753 754 755 756
      subject { create(:merge_request, source_project: project, target_project: project) }

      before do
        project.repository.raw_repository.delete_branch(subject.target_branch)
757
        subject.clear_memoized_shas
758
      end
759 760

      it 'does not crash' do
761
        expect { subject.diverged_commits_count }.not_to raise_error
762 763 764 765 766 767 768
      end

      it 'returns 0' do
        expect(subject.diverged_commits_count).to eq(0)
      end
    end

769 770 771 772
    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
773
        expect(subject.diverged_commits_count).to eq(29)
774 775 776 777
      end
    end

    context 'diverged on fork' do
B
Bob Van Landuyt 已提交
778
      subject(:merge_request_fork_with_divergence) { create(:merge_request, :diverged, source_project: forked_project, target_project: project) }
779 780

      it 'counts commits that are on target branch but not on source branch' do
781
        expect(subject.diverged_commits_count).to eq(29)
782 783 784 785
      end
    end

    context 'rebased on fork' do
B
Bob Van Landuyt 已提交
786
      subject(:merge_request_rebased) { create(:merge_request, :rebased, source_project: forked_project, target_project: project) }
787 788 789 790 791 792 793

      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
794
      before do
795 796 797 798
        allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new)
      end

      it 'caches the output' do
799 800 801
        expect(subject).to receive(:compute_diverged_commits_count)
          .once
          .and_return(2)
802 803 804 805 806 807

        subject.diverged_commits_count
        subject.diverged_commits_count
      end

      it 'invalidates the cache when the source sha changes' do
808 809 810
        expect(subject).to receive(:compute_diverged_commits_count)
          .twice
          .and_return(2)
811 812

        subject.diverged_commits_count
813
        allow(subject).to receive(:source_branch_sha).and_return('123abc')
814 815 816 817
        subject.diverged_commits_count
      end

      it 'invalidates the cache when the target sha changes' do
818 819 820
        expect(subject).to receive(:compute_diverged_commits_count)
          .twice
          .and_return(2)
821 822

        subject.diverged_commits_count
823
        allow(subject).to receive(:target_branch_sha).and_return('123abc')
824 825 826
        subject.diverged_commits_count
      end
    end
827 828
  end

829
  it_behaves_like 'an editable mentionable' do
830
    subject { create(:merge_request, :simple) }
831

832
    let(:backref_text) { "merge request #{subject.to_reference}" }
833
    let(:set_mentionable_text) { ->(txt) { subject.description = txt } }
834
  end
V
Vinnie Okada 已提交
835 836

  it_behaves_like 'a Taskable' do
837
    subject { create :merge_request, :simple }
V
Vinnie Okada 已提交
838
  end
839

840
  describe '#commit_shas' do
841
    before do
842
      allow(subject.merge_request_diff).to receive(:commit_shas)
843
        .and_return(['sha1'])
844 845
    end

846
    it 'delegates to merge request diff' do
847
      expect(subject.commit_shas).to eq ['sha1']
848 849 850
    end
  end

851
  context 'head pipeline' do
852 853 854
    before do
      allow(subject).to receive(:diff_head_sha).and_return('lastsha')
    end
855

856 857 858
    describe '#head_pipeline' do
      it 'returns nil for MR without head_pipeline_id' do
        subject.update_attribute(:head_pipeline_id, nil)
859

860 861
        expect(subject.head_pipeline).to be_nil
      end
862 863 864 865 866 867 868 869

      context 'when the source project does not exist' do
        it 'returns nil' do
          allow(subject).to receive(:source_project).and_return(nil)

          expect(subject.head_pipeline).to be_nil
        end
      end
870 871
    end

872
    describe '#actual_head_pipeline' do
873 874 875
      it 'returns nil for MR with old pipeline' do
        pipeline = create(:ci_empty_pipeline, sha: 'notlatestsha')
        subject.update_attribute(:head_pipeline_id, pipeline.id)
876

877
        expect(subject.actual_head_pipeline).to be_nil
878
      end
879

880 881 882
      it 'returns the pipeline for MR with recent pipeline' do
        pipeline = create(:ci_empty_pipeline, sha: 'lastsha')
        subject.update_attribute(:head_pipeline_id, pipeline.id)
883

884 885
        expect(subject.actual_head_pipeline).to eq(subject.head_pipeline)
        expect(subject.actual_head_pipeline).to eq(pipeline)
886
      end
887 888 889 890 891 892

      it 'returns nil when source project does not exist' do
        allow(subject).to receive(:source_project).and_return(nil)

        expect(subject.actual_head_pipeline).to be_nil
      end
893 894
    end
  end
Y
Yorick Peterse 已提交
895

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
  describe '#has_ci?' do
    let(:merge_request) { build_stubbed(:merge_request) }

    context 'has ci' do
      it 'returns true if MR has head_pipeline_id and commits' do
        allow(merge_request).to receive_message_chain(:source_project, :ci_service) { nil }
        allow(merge_request).to receive(:head_pipeline_id) { double }
        allow(merge_request).to receive(:has_no_commits?) { false }

        expect(merge_request.has_ci?).to be(true)
      end

      it 'returns true if MR has any pipeline and commits' do
        allow(merge_request).to receive_message_chain(:source_project, :ci_service) { nil }
        allow(merge_request).to receive(:head_pipeline_id) { nil }
        allow(merge_request).to receive(:has_no_commits?) { false }
        allow(merge_request).to receive(:all_pipelines) { [double] }

        expect(merge_request.has_ci?).to be(true)
      end

      it 'returns true if MR has CI service and commits' do
        allow(merge_request).to receive_message_chain(:source_project, :ci_service) { double }
        allow(merge_request).to receive(:head_pipeline_id) { nil }
        allow(merge_request).to receive(:has_no_commits?) { false }
        allow(merge_request).to receive(:all_pipelines) { [] }

        expect(merge_request.has_ci?).to be(true)
      end
    end

    context 'has no ci' do
      it 'returns false if MR has no CI service nor pipeline, and no commits' do
        allow(merge_request).to receive_message_chain(:source_project, :ci_service) { nil }
        allow(merge_request).to receive(:head_pipeline_id) { nil }
        allow(merge_request).to receive(:all_pipelines) { [] }
        allow(merge_request).to receive(:has_no_commits?) { true }

        expect(merge_request.has_ci?).to be(false)
      end
    end
  end

939
  describe '#all_pipelines' do
940
    shared_examples 'returning pipelines with proper ordering' do
941
      let!(:all_pipelines) do
942
        subject.all_commit_shas.map do |sha|
943 944 945 946
          create(:ci_empty_pipeline,
                 project: subject.source_project,
                 sha: sha,
                 ref: subject.source_branch)
947 948 949 950 951
        end
      end

      it 'returns all pipelines' do
        expect(subject.all_pipelines).not_to be_empty
952
        expect(subject.all_pipelines).to eq(all_pipelines.reverse)
953
      end
954 955
    end

956 957 958 959 960 961
    context 'with single merge_request_diffs' do
      it_behaves_like 'returning pipelines with proper ordering'
    end

    context 'with multiple irrelevant merge_request_diffs' do
      before do
962
        subject.update(target_branch: 'v1.0.0')
963 964 965
      end

      it_behaves_like 'returning pipelines with proper ordering'
966
    end
967 968

    context 'with unsaved merge request' do
969
      subject { build(:merge_request) }
970 971 972

      let!(:pipeline) do
        create(:ci_empty_pipeline,
973
               project: subject.project,
974 975
               sha: subject.diff_head_sha,
               ref: subject.source_branch)
976
      end
977 978 979 980 981

      it 'returns pipelines from diff_head_sha' do
        expect(subject.all_pipelines).to contain_exactly(pipeline)
      end
    end
982 983
  end

984
  describe '#all_commit_shas' do
985
    context 'when merge request is persisted' do
986
      let(:all_commit_shas) do
987 988
        subject.merge_request_diffs.flat_map(&:commits).map(&:sha).uniq
      end
989

990
      shared_examples 'returning all SHA' do
991
        it 'returns all SHAs from all merge_request_diffs' do
992
          expect(subject.merge_request_diffs.size).to eq(2)
993
          expect(subject.all_commit_shas).to match_array(all_commit_shas)
994
        end
995 996
      end

997 998
      context 'with a completely different branch' do
        before do
999
          subject.update(target_branch: 'csv')
1000 1001 1002
        end

        it_behaves_like 'returning all SHA'
1003 1004
      end

1005 1006
      context 'with a branch having no difference' do
        before do
1007
          subject.update(target_branch: 'branch-merged')
1008 1009 1010 1011 1012
          subject.reload # make sure commits were not cached
        end

        it_behaves_like 'returning all SHA'
      end
1013 1014
    end

1015 1016 1017 1018 1019 1020 1021 1022 1023
    context 'when merge request is not persisted' do
      context 'when compare commits are set in the service' do
        let(:commit) { spy('commit') }

        subject do
          build(:merge_request, compare_commits: [commit, commit])
        end

        it 'returns commits from compare commits temporary data' do
1024
          expect(subject.all_commit_shas).to eq [commit, commit]
1025
        end
1026 1027
      end

1028 1029 1030 1031
      context 'when compare commits are not set in the service' do
        subject { build(:merge_request) }

        it 'returns array with diff head sha element only' do
1032
          expect(subject.all_commit_shas).to eq [subject.diff_head_sha]
1033 1034
        end
      end
1035 1036 1037
    end
  end

Y
Yorick Peterse 已提交
1038
  describe '#participants' do
1039
    let(:project) { create(:project, :public) }
Y
Yorick Peterse 已提交
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060

    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
J
Josh Frye 已提交
1061 1062 1063 1064 1065 1066

  describe 'cached counts' do
    it 'updates when assignees change' do
      user1 = create(:user)
      user2 = create(:user)
      mr = create(:merge_request, assignee: user1)
1067 1068
      mr.project.add_developer(user1)
      mr.project.add_developer(user2)
J
Josh Frye 已提交
1069

1070 1071
      expect(user1.assigned_open_merge_requests_count).to eq(1)
      expect(user2.assigned_open_merge_requests_count).to eq(0)
J
Josh Frye 已提交
1072 1073 1074 1075

      mr.assignee = user2
      mr.save

1076 1077
      expect(user1.assigned_open_merge_requests_count).to eq(0)
      expect(user2.assigned_open_merge_requests_count).to eq(1)
J
Josh Frye 已提交
1078 1079
    end
  end
1080

1081
  describe '#merge_async' do
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
    it 'enqueues MergeWorker job and updates merge_jid' do
      merge_request = create(:merge_request)
      user_id = double(:user_id)
      params = double(:params)
      merge_jid = 'hash-123'

      expect(MergeWorker).to receive(:perform_async).with(merge_request.id, user_id, params) do
        merge_jid
      end

1092
      merge_request.merge_async(user_id, params)
1093 1094 1095 1096 1097

      expect(merge_request.reload.merge_jid).to eq(merge_jid)
    end
  end

1098
  describe '#check_if_can_be_merged' do
1099
    let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) }
1100 1101 1102 1103

    subject { create(:merge_request, source_project: project, merge_status: :unchecked) }

    context 'when it is not broken and has no conflicts' do
1104
      before do
1105
        allow(subject).to receive(:broken?) { false }
1106
        allow(project.repository).to receive(:can_be_merged?).and_return(true)
1107
      end
1108

1109
      it 'is marked as mergeable' do
1110 1111 1112 1113 1114
        expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged')
      end
    end

    context 'when broken' do
1115 1116 1117
      before do
        allow(subject).to receive(:broken?) { true }
      end
1118 1119 1120 1121 1122 1123 1124 1125 1126

      it 'becomes unmergeable' do
        expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
      end
    end

    context 'when it has conflicts' do
      before do
        allow(subject).to receive(:broken?) { false }
1127
        allow(project.repository).to receive(:can_be_merged?).and_return(false)
1128 1129 1130 1131 1132 1133 1134 1135 1136
      end

      it 'becomes unmergeable' do
        expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
      end
    end
  end

  describe '#mergeable?' do
1137
    let(:project) { create(:project) }
1138 1139 1140

    subject { create(:merge_request, source_project: project) }

1141 1142
    it 'returns false if #mergeable_state? is false' do
      expect(subject).to receive(:mergeable_state?) { false }
1143

1144
      expect(subject.mergeable?).to be_falsey
1145 1146
    end

1147
    it 'return true if #mergeable_state? is true and the MR #can_be_merged? is true' do
1148 1149
      allow(subject).to receive(:mergeable_state?) { true }
      expect(subject).to receive(:check_if_can_be_merged)
1150
      expect(subject).to receive(:can_be_merged?) { true }
1151 1152 1153 1154 1155 1156

      expect(subject.mergeable?).to be_truthy
    end
  end

  describe '#mergeable_state?' do
1157
    let(:project) { create(:project, :repository) }
1158 1159 1160

    subject { create(:merge_request, source_project: project) }

1161
    it 'checks if merge request can be merged' do
1162
      allow(subject).to receive(:mergeable_ci_state?) { true }
1163 1164 1165 1166 1167 1168
      expect(subject).to receive(:check_if_can_be_merged)

      subject.mergeable?
    end

    context 'when not open' do
1169 1170 1171
      before do
        subject.close
      end
1172 1173

      it 'returns false' do
1174
        expect(subject.mergeable_state?).to be_falsey
1175 1176 1177 1178
      end
    end

    context 'when working in progress' do
1179 1180 1181
      before do
        subject.title = 'WIP MR'
      end
1182 1183

      it 'returns false' do
1184
        expect(subject.mergeable_state?).to be_falsey
1185 1186 1187 1188
      end
    end

    context 'when broken' do
1189 1190 1191
      before do
        allow(subject).to receive(:broken?) { true }
      end
1192 1193

      it 'returns false' do
1194
        expect(subject.mergeable_state?).to be_falsey
1195 1196 1197 1198
      end
    end

    context 'when failed' do
R
Rémy Coutable 已提交
1199
      context 'when #mergeable_ci_state? is false' do
1200
        before do
1201
          allow(subject).to receive(:mergeable_ci_state?) { false }
1202 1203 1204 1205
        end

        it 'returns false' do
          expect(subject.mergeable_state?).to be_falsey
1206 1207
        end
      end
1208

R
Rémy Coutable 已提交
1209
      context 'when #mergeable_discussions_state? is false' do
1210 1211 1212 1213 1214 1215 1216 1217
        before do
          allow(subject).to receive(:mergeable_discussions_state?) { false }
        end

        it 'returns false' do
          expect(subject.mergeable_state?).to be_falsey
        end
      end
1218 1219 1220
    end
  end

1221
  describe '#mergeable_ci_state?' do
1222
    let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) }
R
Rémy Coutable 已提交
1223
    let(:pipeline) { create(:ci_empty_pipeline) }
1224 1225 1226

    subject { build(:merge_request, target_project: project) }

1227
    context 'when it is only allowed to merge when build is green' do
R
Rémy Coutable 已提交
1228
      context 'and a failed pipeline is associated' do
1229
        before do
1230
          pipeline.update(status: 'failed', sha: subject.diff_head_sha)
1231
          allow(subject).to receive(:head_pipeline) { pipeline }
1232
        end
1233

1234
        it { expect(subject.mergeable_ci_state?).to be_falsey }
1235 1236
      end

1237 1238
      context 'and a successful pipeline is associated' do
        before do
1239
          pipeline.update(status: 'success', sha: subject.diff_head_sha)
1240
          allow(subject).to receive(:head_pipeline) { pipeline }
1241 1242 1243 1244 1245 1246 1247
        end

        it { expect(subject.mergeable_ci_state?).to be_truthy }
      end

      context 'and a skipped pipeline is associated' do
        before do
1248
          pipeline.update(status: 'skipped', sha: subject.diff_head_sha)
1249
          allow(subject).to receive(:head_pipeline) { pipeline }
1250 1251 1252 1253 1254
        end

        it { expect(subject.mergeable_ci_state?).to be_truthy }
      end

R
Rémy Coutable 已提交
1255
      context 'when no pipeline is associated' do
1256
        before do
1257
          allow(subject).to receive(:head_pipeline) { nil }
1258 1259 1260
        end

        it { expect(subject.mergeable_ci_state?).to be_truthy }
1261 1262 1263
      end
    end

1264
    context 'when merges are not restricted to green builds' do
1265
      subject { build(:merge_request, target_project: build(:project, only_allow_merge_if_pipeline_succeeds: false)) }
1266

R
Rémy Coutable 已提交
1267
      context 'and a failed pipeline is associated' do
1268
        before do
R
Rémy Coutable 已提交
1269
          pipeline.statuses << create(:commit_status, status: 'failed', project: project)
1270
          allow(subject).to receive(:head_pipeline) { pipeline }
1271 1272 1273 1274 1275
        end

        it { expect(subject.mergeable_ci_state?).to be_truthy }
      end

R
Rémy Coutable 已提交
1276
      context 'when no pipeline is associated' do
1277
        before do
1278
          allow(subject).to receive(:head_pipeline) { nil }
1279 1280 1281
        end

        it { expect(subject.mergeable_ci_state?).to be_truthy }
1282 1283 1284
      end
    end
  end
1285

1286
  describe '#mergeable_discussions_state?' do
R
Rémy Coutable 已提交
1287
    let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) }
1288

R
Rémy Coutable 已提交
1289
    context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do
1290
      let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) }
1291

R
Rémy Coutable 已提交
1292
      context 'with all discussions resolved' do
1293
        before do
R
Rémy Coutable 已提交
1294
          merge_request.discussions.each { |d| d.resolve!(merge_request.author) }
1295 1296 1297
        end

        it 'returns true' do
R
Rémy Coutable 已提交
1298
          expect(merge_request.mergeable_discussions_state?).to be_truthy
1299 1300 1301
        end
      end

R
Rémy Coutable 已提交
1302
      context 'with unresolved discussions' do
1303
        before do
R
Rémy Coutable 已提交
1304
          merge_request.discussions.each(&:unresolve!)
1305 1306 1307
        end

        it 'returns false' do
R
Rémy Coutable 已提交
1308
          expect(merge_request.mergeable_discussions_state?).to be_falsey
1309 1310
        end
      end
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320

      context 'with no discussions' do
        before do
          merge_request.notes.destroy_all
        end

        it 'returns true' do
          expect(merge_request.mergeable_discussions_state?).to be_truthy
        end
      end
1321 1322
    end

R
Rémy Coutable 已提交
1323
    context 'when project.only_allow_merge_if_all_discussions_are_resolved == false' do
1324
      let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: false) }
1325

R
Rémy Coutable 已提交
1326
      context 'with unresolved discussions' do
1327
        before do
R
Rémy Coutable 已提交
1328
          merge_request.discussions.each(&:unresolve!)
1329 1330 1331
        end

        it 'returns true' do
R
Rémy Coutable 已提交
1332
          expect(merge_request.mergeable_discussions_state?).to be_truthy
1333 1334 1335 1336 1337
        end
      end
    end
  end

D
Douwe Maan 已提交
1338
  describe "#environments_for" do
1339
    let(:project)       { create(:project, :repository) }
D
Douwe Maan 已提交
1340
    let(:user)          { project.creator }
Z
Z.J. van de Weg 已提交
1341 1342
    let(:merge_request) { create(:merge_request, source_project: project) }

D
Douwe Maan 已提交
1343 1344 1345 1346 1347
    before do
      merge_request.source_project.add_master(user)
      merge_request.target_project.add_master(user)
    end

1348 1349
    context 'with multiple environments' do
      let(:environments) { create_list(:environment, 3, project: project) }
1350

1351 1352 1353 1354 1355 1356
      before do
        create(:deployment, environment: environments.first, ref: 'master', sha: project.commit('master').id)
        create(:deployment, environment: environments.second, ref: 'feature', sha: project.commit('feature').id)
      end

      it 'selects deployed environments' do
D
Douwe Maan 已提交
1357
        expect(merge_request.environments_for(user)).to contain_exactly(environments.first)
1358 1359 1360 1361
      end
    end

    context 'with environments on source project' do
B
Bob Van Landuyt 已提交
1362
      let(:source_project) { fork_project(project, nil, repository: true) }
K
Kamil Trzcinski 已提交
1363

1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
      let(:merge_request) do
        create(:merge_request,
               source_project: source_project, source_branch: 'feature',
               target_project: project)
      end

      let(:source_environment) { create(:environment, project: source_project) }

      before do
        create(:deployment, environment: source_environment, ref: 'feature', sha: merge_request.diff_head_sha)
      end

      it 'selects deployed environments' do
D
Douwe Maan 已提交
1377
        expect(merge_request.environments_for(user)).to contain_exactly(source_environment)
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387
      end

      context 'with environments on target project' do
        let(:target_environment) { create(:environment, project: project) }

        before do
          create(:deployment, environment: target_environment, tag: true, sha: merge_request.diff_head_sha)
        end

        it 'selects deployed environments' do
D
Douwe Maan 已提交
1388
          expect(merge_request.environments_for(user)).to contain_exactly(source_environment, target_environment)
1389 1390
        end
      end
1391 1392 1393 1394 1395 1396 1397 1398
    end

    context 'without a diff_head_commit' do
      before do
        expect(merge_request).to receive(:diff_head_commit).and_return(nil)
      end

      it 'returns an empty array' do
D
Douwe Maan 已提交
1399
        expect(merge_request.environments_for(user)).to be_empty
1400
      end
Z
Z.J. van de Weg 已提交
1401 1402 1403
    end
  end

1404
  describe "#reload_diff" do
1405
    let(:discussion) { create(:diff_note_on_merge_request, project: subject.project, noteable: subject).to_discussion }
1406 1407
    let(:commit) { subject.project.commit(sample_commit.id) }

1408
    it "does not change existing merge request diff" do
1409
      expect(subject.merge_request_diff).not_to receive(:save_git_content)
1410 1411 1412
      subject.reload_diff
    end

1413 1414 1415 1416
    it "creates new merge request diff" do
      expect { subject.reload_diff }.to change { subject.merge_request_diffs.count }.by(1)
    end

1417 1418 1419 1420 1421 1422
    it "executs diff cache service" do
      expect_any_instance_of(MergeRequests::MergeRequestDiffCacheService).to receive(:execute).with(subject)

      subject.reload_diff
    end

1423 1424 1425 1426 1427
    it "calls update_diff_discussion_positions" do
      expect(subject).to receive(:update_diff_discussion_positions)

      subject.reload_diff
    end
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437

    context 'when using the after_update hook to update' do
      context 'when the branches are updated' do
        it 'uses the new heads to generate the diff' do
          expect { subject.update!(source_branch: subject.target_branch, target_branch: subject.source_branch) }
            .to change { subject.merge_request_diff.start_commit_sha }
            .and change { subject.merge_request_diff.head_commit_sha }
        end
      end
    end
1438
  end
1439

1440 1441 1442 1443 1444 1445
  describe '#update_diff_discussion_positions' do
    let(:discussion) { create(:diff_note_on_merge_request, project: subject.project, noteable: subject).to_discussion }
    let(:commit) { subject.project.commit(sample_commit.id) }
    let(:old_diff_refs) { subject.diff_refs }

    before do
1446
      # Update merge_request_diff so that #diff_refs will return commit.diff_refs
1447 1448 1449 1450 1451 1452
      allow(subject).to receive(:create_merge_request_diff) do
        subject.merge_request_diffs.create(
          base_commit_sha: commit.parent_id,
          start_commit_sha: commit.parent_id,
          head_commit_sha: commit.sha
        )
1453 1454

        subject.merge_request_diff(true)
1455
      end
1456
    end
1457

1458
    it "updates diff discussion positions" do
1459
      expect(Discussions::UpdateDiffPositionService).to receive(:new).with(
1460
        subject.project,
1461
        subject.author,
1462 1463
        old_diff_refs: old_diff_refs,
        new_diff_refs: commit.diff_refs,
1464
        paths: discussion.position.paths
1465 1466
      ).and_call_original

1467
      expect_any_instance_of(Discussions::UpdateDiffPositionService).to receive(:execute).with(discussion).and_call_original
1468 1469
      expect_any_instance_of(DiffNote).to receive(:save).once

1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
      subject.update_diff_discussion_positions(old_diff_refs: old_diff_refs,
                                               new_diff_refs: commit.diff_refs,
                                               current_user: subject.author)
    end

    context 'when resolve_outdated_diff_discussions is set' do
      before do
        discussion

        subject.project.update!(resolve_outdated_diff_discussions: true)
      end

      it 'calls MergeRequests::ResolvedDiscussionNotificationService' do
        expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService)
          .to receive(:execute).with(subject)

        subject.update_diff_discussion_positions(old_diff_refs: old_diff_refs,
                                                 new_diff_refs: commit.diff_refs,
                                                 current_user: subject.author)
      end
1490 1491
    end
  end
1492 1493 1494 1495 1496 1497 1498 1499 1500

  describe '#branch_merge_base_commit' do
    context 'source and target branch exist' do
      it { expect(subject.branch_merge_base_commit.sha).to eq('ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }
      it { expect(subject.branch_merge_base_commit).to be_a(Commit) }
    end

    context 'when the target branch does not exist' do
      before do
1501
        subject.project.repository.rm_branch(subject.author, subject.target_branch)
1502
        subject.clear_memoized_shas
1503 1504 1505 1506
      end

      it 'returns nil' do
        expect(subject.branch_merge_base_commit).to be_nil
1507 1508 1509 1510
      end
    end
  end

1511
  describe "#diff_refs" do
1512 1513 1514 1515 1516 1517 1518 1519
    context "with diffs" do
      subject { create(:merge_request, :with_diffs) }

      it "does not touch the repository" do
        subject # Instantiate the object

        expect_any_instance_of(Repository).not_to receive(:commit)

1520
        subject.diff_refs
1521 1522 1523 1524 1525 1526 1527 1528 1529
      end

      it "returns expected diff_refs" do
        expected_diff_refs = Gitlab::Diff::DiffRefs.new(
          base_sha:  subject.merge_request_diff.base_commit_sha,
          start_sha: subject.merge_request_diff.start_commit_sha,
          head_sha:  subject.merge_request_diff.head_commit_sha
        )

1530
        expect(subject.diff_refs).to eq(expected_diff_refs)
1531 1532 1533
      end
    end
  end
1534

1535
  describe "#source_project_missing?" do
1536
    let(:project)      { create(:project) }
B
Bob Van Landuyt 已提交
1537
    let(:forked_project) { fork_project(project) }
1538
    let(:user)         { create(:user) }
B
Bob Van Landuyt 已提交
1539
    let(:unlink_project) { Projects::UnlinkForkService.new(forked_project, user) }
1540

K
Katarzyna Kobierska 已提交
1541
    context "when the fork exists" do
1542 1543
      let(:merge_request) do
        create(:merge_request,
B
Bob Van Landuyt 已提交
1544
          source_project: forked_project,
1545 1546 1547
          target_project: project)
      end

1548
      it { expect(merge_request.source_project_missing?).to be_falsey }
1549 1550
    end

K
Katarzyna Kobierska 已提交
1551
    context "when the source project is the same as the target project" do
1552 1553
      let(:merge_request) { create(:merge_request, source_project: project) }

1554
      it { expect(merge_request.source_project_missing?).to be_falsey }
1555 1556
    end

K
Katarzyna Kobierska 已提交
1557
    context "when the fork does not exist" do
1558
      let!(:merge_request) do
1559
        create(:merge_request,
B
Bob Van Landuyt 已提交
1560
          source_project: forked_project,
1561 1562 1563
          target_project: project)
      end

K
Katarzyna Kobierska 已提交
1564
      it "returns true" do
1565 1566 1567
        unlink_project.execute
        merge_request.reload

1568
        expect(merge_request.source_project_missing?).to be_truthy
1569 1570 1571 1572
      end
    end
  end

1573
  describe '#merge_ongoing?' do
1574 1575 1576 1577 1578 1579
    it 'returns true when the merge request is locked' do
      merge_request = build_stubbed(:merge_request, state: :locked)

      expect(merge_request.merge_ongoing?).to be(true)
    end

1580
    it 'returns true when merge_id, MR is not merged and it has no running job' do
1581
      merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo')
1582
      allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { true }
1583 1584 1585

      expect(merge_request.merge_ongoing?).to be(true)
    end
1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600

    it 'returns false when merge_jid is nil' do
      merge_request = build_stubbed(:merge_request, state: :open, merge_jid: nil)

      expect(merge_request.merge_ongoing?).to be(false)
    end

    it 'returns false if MR is merged' do
      merge_request = build_stubbed(:merge_request, state: :merged, merge_jid: 'foo')

      expect(merge_request.merge_ongoing?).to be(false)
    end

    it 'returns false if there is no merge job running' do
      merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo')
1601
      allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { false }
1602 1603 1604

      expect(merge_request.merge_ongoing?).to be(false)
    end
1605 1606
  end

1607
  describe "#closed_without_fork?" do
1608
    let(:project)      { create(:project) }
B
Bob Van Landuyt 已提交
1609
    let(:forked_project) { fork_project(project) }
1610
    let(:user)         { create(:user) }
B
Bob Van Landuyt 已提交
1611
    let(:unlink_project) { Projects::UnlinkForkService.new(forked_project, user) }
1612

K
Katarzyna Kobierska 已提交
1613
    context "when the merge request is closed" do
1614 1615
      let(:closed_merge_request) do
        create(:closed_merge_request,
B
Bob Van Landuyt 已提交
1616
          source_project: forked_project,
1617 1618 1619
          target_project: project)
      end

K
Katarzyna Kobierska 已提交
1620
      it "returns false if the fork exist" do
1621 1622 1623
        expect(closed_merge_request.closed_without_fork?).to be_falsey
      end

K
Katarzyna Kobierska 已提交
1624
      it "returns true if the fork does not exist" do
1625 1626 1627 1628 1629 1630
        unlink_project.execute
        closed_merge_request.reload

        expect(closed_merge_request.closed_without_fork?).to be_truthy
      end
    end
K
Katarzyna Kobierska 已提交
1631

K
Katarzyna Kobierska 已提交
1632
    context "when the merge request is open" do
K
Katarzyna Kobierska 已提交
1633 1634
      let(:open_merge_request) do
        create(:merge_request,
B
Bob Van Landuyt 已提交
1635
          source_project: forked_project,
K
Katarzyna Kobierska 已提交
1636 1637 1638 1639 1640 1641 1642
          target_project: project)
      end

      it "returns false" do
        expect(open_merge_request.closed_without_fork?).to be_falsey
      end
    end
1643
  end
1644

1645
  describe '#reopenable?' do
K
Katarzyna Kobierska 已提交
1646 1647 1648
    context 'when the merge request is closed' do
      it 'returns true' do
        subject.close
K
Katarzyna Kobierska 已提交
1649

1650
        expect(subject.reopenable?).to be_truthy
K
Katarzyna Kobierska 已提交
1651 1652 1653
      end

      context 'forked project' do
B
Bob Van Landuyt 已提交
1654
        let(:project)      { create(:project, :public) }
K
Katarzyna Kobierska 已提交
1655
        let(:user)         { create(:user) }
B
Bob Van Landuyt 已提交
1656
        let(:forked_project) { fork_project(project, user) }
1657 1658

        let!(:merge_request) do
K
Katarzyna Kobierska 已提交
1659
          create(:closed_merge_request,
B
Bob Van Landuyt 已提交
1660
            source_project: forked_project,
K
Katarzyna Kobierska 已提交
1661 1662 1663 1664
            target_project: project)
        end

        it 'returns false if unforked' do
B
Bob Van Landuyt 已提交
1665
          Projects::UnlinkForkService.new(forked_project, user).execute
K
Katarzyna Kobierska 已提交
1666

1667
          expect(merge_request.reload.reopenable?).to be_falsey
K
Katarzyna Kobierska 已提交
1668 1669 1670
        end

        it 'returns false if the source project is deleted' do
B
Bob Van Landuyt 已提交
1671
          Projects::DestroyService.new(forked_project, user).execute
K
Katarzyna Kobierska 已提交
1672

1673
          expect(merge_request.reload.reopenable?).to be_falsey
K
Katarzyna Kobierska 已提交
1674 1675
        end

K
Katarzyna Kobierska 已提交
1676
        it 'returns false if the merge request is merged' do
K
Katarzyna Kobierska 已提交
1677 1678
          merge_request.update_attributes(state: 'merged')

1679
          expect(merge_request.reload.reopenable?).to be_falsey
K
Katarzyna Kobierska 已提交
1680 1681
        end
      end
K
Katarzyna Kobierska 已提交
1682 1683
    end

K
Katarzyna Kobierska 已提交
1684
    context 'when the merge request is opened' do
K
Katarzyna Kobierska 已提交
1685
      it 'returns false' do
1686
        expect(subject.reopenable?).to be_falsey
K
Katarzyna Kobierska 已提交
1687
      end
K
Katarzyna Kobierska 已提交
1688 1689
    end
  end
1690

1691
  describe '#mergeable_with_quick_action?' do
1692
    def create_pipeline(status)
F
Felipe Artur 已提交
1693
      pipeline = create(:ci_pipeline_with_one_job,
1694 1695 1696
        project: project,
        ref:     merge_request.source_branch,
        sha:     merge_request.diff_head_sha,
1697 1698
        status:  status,
        head_pipeline_of: merge_request)
F
Felipe Artur 已提交
1699 1700

      pipeline
1701 1702
    end

J
James Lopez 已提交
1703
    let(:project)       { create(:project, :public, :repository, only_allow_merge_if_pipeline_succeeds: true) }
1704 1705 1706 1707 1708 1709
    let(:developer)     { create(:user) }
    let(:user)          { create(:user) }
    let(:merge_request) { create(:merge_request, source_project: project) }
    let(:mr_sha)        { merge_request.diff_head_sha }

    before do
1710
      project.add_developer(developer)
1711 1712 1713 1714
    end

    context 'when autocomplete_precheck is set to true' do
      it 'is mergeable by developer' do
1715
        expect(merge_request.mergeable_with_quick_action?(developer, autocomplete_precheck: true)).to be_truthy
1716 1717 1718
      end

      it 'is not mergeable by normal user' do
1719
        expect(merge_request.mergeable_with_quick_action?(user, autocomplete_precheck: true)).to be_falsey
1720 1721 1722 1723 1724
      end
    end

    context 'when autocomplete_precheck is set to false' do
      it 'is mergeable by developer' do
1725
        expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
1726 1727 1728
      end

      it 'is not mergeable by normal user' do
1729
        expect(merge_request.mergeable_with_quick_action?(user, last_diff_sha: mr_sha)).to be_falsey
1730 1731 1732 1733 1734 1735 1736 1737
      end

      context 'closed MR'  do
        before do
          merge_request.update_attribute(:state, :closed)
        end

        it 'is not mergeable' do
1738
          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
1739 1740 1741 1742 1743 1744 1745 1746 1747
        end
      end

      context 'MR with WIP'  do
        before do
          merge_request.update_attribute(:title, 'WIP: some MR')
        end

        it 'is not mergeable' do
1748
          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
1749 1750 1751 1752 1753
        end
      end

      context 'sha differs from the MR diff_head_sha'  do
        it 'is not mergeable' do
1754
          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: 'some other sha')).to be_falsey
1755 1756 1757
        end
      end

J
Jarka Kadlecova 已提交
1758 1759
      context 'sha is not provided'  do
        it 'is not mergeable' do
1760
          expect(merge_request.mergeable_with_quick_action?(developer)).to be_falsey
J
Jarka Kadlecova 已提交
1761 1762 1763
        end
      end

1764 1765 1766 1767 1768 1769
      context 'with pipeline ok'  do
        before do
          create_pipeline(:success)
        end

        it 'is mergeable' do
1770
          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
1771 1772 1773 1774 1775 1776 1777 1778 1779
        end
      end

      context 'with failing pipeline'  do
        before do
          create_pipeline(:failed)
        end

        it 'is not mergeable' do
1780
          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
1781 1782 1783 1784 1785
        end
      end

      context 'with running pipeline'  do
        before do
F
Felipe Artur 已提交
1786
          create_pipeline(:running)
1787 1788 1789
        end

        it 'is mergeable' do
1790
          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
1791 1792 1793 1794 1795
        end
      end
    end
  end

1796 1797
  describe '#has_commits?' do
    before do
1798 1799
      allow(subject.merge_request_diff).to receive(:commits_count)
        .and_return(2)
1800 1801 1802 1803 1804 1805 1806 1807 1808
    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
1809 1810
      allow(subject.merge_request_diff).to receive(:commits_count)
        .and_return(0)
1811 1812 1813 1814 1815 1816
    end

    it 'returns true when merge request diff has 0 commits' do
      expect(subject.has_no_commits?).to be_truthy
    end
  end
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835

  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
1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867

  describe '#version_params_for' do
    subject { create(:merge_request, importing: true) }
    let(:project) { subject.project }
    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 'when the diff refs are for an older merge request version' do
      let(:diff_refs) { merge_request_diff1.diff_refs }

      it 'returns the diff ID for the version to show' do
        expect(subject.version_params_for(diff_refs)).to eq(diff_id: merge_request_diff1.id)
      end
    end

    context 'when the diff refs are for a comparison between merge request versions' do
      let(:diff_refs) { merge_request_diff3.compare_with(merge_request_diff1.head_commit_sha).diff_refs }

      it 'returns the diff ID and start sha of the versions to compare' do
        expect(subject.version_params_for(diff_refs)).to eq(diff_id: merge_request_diff3.id, start_sha: merge_request_diff1.head_commit_sha)
      end
    end

    context 'when the diff refs are not for a merge request version' do
      let(:diff_refs) { project.commit(sample_commit.id).diff_refs }

      it 'returns nil' do
        expect(subject.version_params_for(diff_refs)).to be_nil
      end
    end
  end
1868

1869
  describe '#fetch_ref!' do
M
micael.bergeron 已提交
1870
    it 'fetches the ref correctly' do
1871
      expect { subject.target_project.repository.delete_refs(subject.ref_path) }.not_to raise_error
1872

1873 1874
      subject.fetch_ref!
      expect(subject.target_project.repository.ref_exists?(subject.ref_path)).to be_truthy
1875 1876
    end
  end
1877 1878 1879 1880 1881 1882 1883 1884 1885

  describe 'removing a merge request' do
    it 'refreshes the number of open merge requests of the target project' do
      project = subject.target_project

      expect { subject.destroy }
        .to change { project.open_merge_requests_count }.from(1).to(0)
    end
  end
1886 1887 1888 1889

  it_behaves_like 'throttled touch' do
    subject { create(:merge_request, updated_at: 1.hour.ago) }
  end
1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905

  context 'state machine transitions' do
    describe '#unlock_mr' do
      subject { create(:merge_request, state: 'locked', merge_jid: 123) }

      it 'updates merge request head pipeline and sets merge_jid to nil' do
        pipeline = create(:ci_empty_pipeline, project: subject.project, ref: subject.source_branch, sha: subject.source_branch_sha)

        subject.unlock_mr

        subject.reload
        expect(subject.head_pipeline).to eq(pipeline)
        expect(subject.merge_jid).to be_nil
      end
    end
  end
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951

  describe '#should_be_rebased?' do
    let(:project) { create(:project, :repository) }

    it 'returns false for the same source and target branches' do
      merge_request = create(:merge_request, source_project: project, target_project: project)

      expect(merge_request.should_be_rebased?).to be_falsey
    end
  end

  describe '#rebase_in_progress?' do
    # Create merge request and project before we stub file calls
    before do
      subject
    end

    it 'returns true when there is a current rebase directory' do
      allow(File).to receive(:exist?).and_return(true)
      allow(File).to receive(:mtime).and_return(Time.now)

      expect(subject.rebase_in_progress?).to be_truthy
    end

    it 'returns false when there is no rebase directory' do
      allow(File).to receive(:exist?).and_return(false)

      expect(subject.rebase_in_progress?).to be_falsey
    end

    it 'returns false when the rebase directory has expired' do
      allow(File).to receive(:exist?).and_return(true)
      allow(File).to receive(:mtime).and_return(20.minutes.ago)

      expect(subject.rebase_in_progress?).to be_falsey
    end

    it 'returns false when the source project has been removed' do
      allow(subject).to receive(:source_project).and_return(nil)
      allow(File).to receive(:exist?).and_return(true)
      allow(File).to receive(:mtime).and_return(Time.now)

      expect(File).not_to have_received(:exist?)
      expect(subject.rebase_in_progress?).to be_falsey
    end
  end
D
Dmitriy Zaporozhets 已提交
1952
end