merge_request_spec.rb 12.5 KB
Newer Older
D
Dmitriy Zaporozhets 已提交
1 2 3 4
# == Schema Information
#
# Table name: merge_requests
#
S
Stan Hu 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#  id                        :integer          not null, primary key
#  target_branch             :string(255)      not null
#  source_branch             :string(255)      not null
#  source_project_id         :integer          not null
#  author_id                 :integer
#  assignee_id               :integer
#  title                     :string(255)
#  created_at                :datetime
#  updated_at                :datetime
#  milestone_id              :integer
#  state                     :string(255)
#  merge_status              :string(255)
#  target_project_id         :integer          not null
#  iid                       :integer
#  description               :text
#  position                  :integer          default(0)
#  locked_at                 :datetime
#  updated_by_id             :integer
#  merge_error               :string(255)
#  merge_params              :text
#  merge_when_build_succeeds :boolean          default(FALSE), not null
#  merge_user_id             :integer
27
#  merge_commit_sha          :string
D
Dmitriy Zaporozhets 已提交
28 29
#

D
Dmitriy Zaporozhets 已提交
30 31
require 'spec_helper'

D
Douwe Maan 已提交
32
describe MergeRequest, models: true do
33 34
  subject { create(:merge_request) }

R
Robert Speicher 已提交
35 36 37
  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') }
38
    it { is_expected.to belong_to(:merge_user).class_name("User") }
R
Robert Speicher 已提交
39 40 41
    it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) }
  end

42 43 44 45 46 47 48 49 50 51 52
  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 'validation' do
53 54
    it { is_expected.to validate_presence_of(:target_branch) }
    it { is_expected.to validate_presence_of(:source_branch) }
Z
Zeger-Jan van de Weg 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

    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
D
Dmitriy Zaporozhets 已提交
73 74
  end

R
Robert Speicher 已提交
75
  describe 'respond to' do
76 77 78
    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?) }
79 80
    it { is_expected.to respond_to(:merge_params) }
    it { is_expected.to respond_to(:merge_when_build_succeeds) }
81
  end
A
Andrey Kumanyaev 已提交
82

83 84 85 86 87 88
  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

89 90 91 92 93 94 95 96 97
  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
98
  end
99 100

  describe "#mr_and_commit_notes" do
101
    let!(:merge_request) { create(:merge_request) }
102 103

    before do
104
      allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] }
D
Dmitriy Zaporozhets 已提交
105 106
      create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.project)
      create(:note, noteable: merge_request, project: merge_request.project)
107 108 109
    end

    it "should include notes for commits" do
110 111
      expect(merge_request.commits).not_to be_empty
      expect(merge_request.mr_and_commit_notes.count).to eq(2)
112
    end
113 114 115 116 117 118

    it "should include notes for commits from target project as well" do
      create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.target_project)
      expect(merge_request.commits).not_to be_empty
      expect(merge_request.mr_and_commit_notes.count).to eq(3)
    end
119
  end
120 121 122

  describe '#is_being_reassigned?' do
    it 'returns true if the merge_request assignee has changed' do
123
      subject.assignee = create(:user)
124
      expect(subject.is_being_reassigned?).to be_truthy
125 126
    end
    it 'returns false if the merge request assignee has not changed' do
127
      expect(subject.is_being_reassigned?).to be_falsey
128 129
    end
  end
I
Izaak Alpert 已提交
130 131 132

  describe '#for_fork?' do
    it 'returns true if the merge request is for a fork' do
D
Dmitriy Zaporozhets 已提交
133 134
      subject.source_project = create(:project, namespace: create(:group))
      subject.target_project = create(:project, namespace: create(:group))
I
Izaak Alpert 已提交
135

136
      expect(subject.for_fork?).to be_truthy
I
Izaak Alpert 已提交
137
    end
D
Dmitriy Zaporozhets 已提交
138

I
Izaak Alpert 已提交
139
    it 'returns false if is not for a fork' do
140
      expect(subject.for_fork?).to be_falsey
I
Izaak Alpert 已提交
141 142 143
    end
  end

144 145 146
  describe 'detection of issues to be closed' do
    let(:issue0) { create :issue, project: subject.project }
    let(:issue1) { create :issue, project: subject.project }
147 148 149 150

    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}") }
151 152

    before do
153
      allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
154 155 156
    end

    it 'accesses the set of issues that will be closed on acceptance' do
157 158
      allow(subject.project).to receive(:default_branch).
        and_return(subject.target_branch)
159

160 161 162
      closed = subject.closes_issues

      expect(closed).to include(issue0, issue1)
163 164 165
    end

    it 'only lists issues as to be closed if it targets the default branch' do
166
      allow(subject.project).to receive(:default_branch).and_return('master')
167 168
      subject.target_branch = 'something-else'

169
      expect(subject.closes_issues).to be_empty
170
    end
171 172 173

    it 'detects issues mentioned in the description' do
      issue2 = create(:issue, project: subject.project)
174
      subject.description = "Closes #{issue2.to_reference}"
175 176
      allow(subject.project).to receive(:default_branch).
        and_return(subject.target_branch)
177

178
      expect(subject.closes_issues).to include(issue2)
179
    end
180 181
  end

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
  describe "#work_in_progress?" do
    it "detects the 'WIP ' prefix" do
      subject.title = "WIP #{subject.title}"
      expect(subject).to be_work_in_progress
    end

    it "detects the 'WIP: ' prefix" do
      subject.title = "WIP: #{subject.title}"
      expect(subject).to be_work_in_progress
    end

    it "detects the '[WIP] ' prefix" do
      subject.title = "[WIP] #{subject.title}"
      expect(subject).to be_work_in_progress
    end

T
Ted Hogan 已提交
198 199 200 201 202
    it "detects the '[WIP]' prefix" do
      subject.title = "[WIP]#{subject.title}"
      expect(subject).to be_work_in_progress
    end

203 204 205 206 207 208 209 210 211 212
    it "doesn't detect WIP for words starting with WIP" do
      subject.title = "Wipwap #{subject.title}"
      expect(subject).not_to be_work_in_progress
    end

    it "doesn't detect WIP by default" do
      expect(subject).not_to be_work_in_progress
    end
  end

Z
Zeger-Jan van de Weg 已提交
213
  describe '#can_remove_source_branch?' do
Z
Zeger-Jan van de Weg 已提交
214 215
    let(:user) { create(:user) }
    let(:user2) { create(:user) }
216 217 218 219

    before do
      subject.source_project.team << [user, :master]

Z
Zeger-Jan van de Weg 已提交
220 221 222 223
      subject.source_branch = "feature"
      subject.target_branch = "master"
      subject.save!
    end
224

Z
Zeger-Jan van de Weg 已提交
225 226
    it "can't be removed when its a protected branch" do
      allow(subject.source_project).to receive(:protected_branch?).and_return(true)
227 228 229 230
      expect(subject.can_remove_source_branch?(user)).to be_falsey
    end

    it "cant remove a root ref" do
Z
Zeger-Jan van de Weg 已提交
231 232
      subject.source_branch = "master"
      subject.target_branch = "feature"
233 234 235 236

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

Z
Zeger-Jan van de Weg 已提交
237 238 239 240
    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

241 242 243
    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)

Z
Zeger-Jan van de Weg 已提交
244
      expect(subject.can_remove_source_branch?(user)).to be_truthy
245
    end
246 247 248 249

    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
250 251
  end

252
  describe "#reset_merge_when_build_succeeds" do
Z
Zeger-Jan van de Weg 已提交
253 254
    let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user) }

255 256 257 258 259 260 261 262
    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
    end
  end

263
  describe "#hook_attrs" do
264 265 266 267 268 269 270 271 272 273 274
    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

275
    it "has all the required keys" do
276 277 278 279
      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)
280
    end
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
  end

  describe '#diverged_commits_count' do
    let(:project)      { create(:project) }
    let(:fork_project) { create(:project, forked_from_project: project) }

    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
345 346
  end

347
  it_behaves_like 'an editable mentionable' do
348
    subject { create(:merge_request) }
349

350 351
    let(:backref_text) { "merge request #{subject.to_reference}" }
    let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
352
  end
V
Vinnie Okada 已提交
353 354

  it_behaves_like 'a Taskable' do
355
    subject { create :merge_request, :simple }
V
Vinnie Okada 已提交
356
  end
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381

  describe '#ci_commit' do
    describe 'when the source project exists' do
      it 'returns the latest commit' do
        commit    = double(:commit, id: '123abc')
        ci_commit = double(:ci_commit)

        allow(subject).to receive(:last_commit).and_return(commit)

        expect(subject.source_project).to receive(:ci_commit).
          with('123abc').
          and_return(ci_commit)

        expect(subject.ci_commit).to eq(ci_commit)
      end
    end

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

        expect(subject.ci_commit).to be_nil
      end
    end
  end
D
Dmitriy Zaporozhets 已提交
382
end