merge_requests_spec.rb 16.6 KB
Newer Older
1 2
require "spec_helper"

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
3
describe API::API, api: true  do
4
  include ApiHelpers
5
  let(:user) { create(:user) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
6
  let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
7
  let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
8 9
  let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test") }
  let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test") }
10
  let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
11 12 13
  before {
    project.team << [user, :reporters]
  }
14 15 16 17

  describe "GET /projects/:id/merge_requests" do
    context "when unauthenticated" do
      it "should return authentication error" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
18
        get api("/projects/#{project.id}/merge_requests")
19 20 21 22 23
        response.status.should == 401
      end
    end

    context "when authenticated" do
24
      it "should return an array of all merge_requests" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
25
        get api("/projects/#{project.id}/merge_requests", user)
26 27
        response.status.should == 200
        json_response.should be_an Array
28
        json_response.length.should == 3
29 30
        json_response.first['title'].should == merge_request.title
      end
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
      it "should return an array of all merge_requests" do
        get api("/projects/#{project.id}/merge_requests?state", user)
        response.status.should == 200
        json_response.should be_an Array
        json_response.length.should == 3
        json_response.first['title'].should == merge_request.title
      end
      it "should return an array of open merge_requests" do
        get api("/projects/#{project.id}/merge_requests?state=opened", user)
        response.status.should == 200
        json_response.should be_an Array
        json_response.length.should == 1
        json_response.first['title'].should == merge_request.title
      end
      it "should return an array of closed merge_requests" do
        get api("/projects/#{project.id}/merge_requests?state=closed", user)
        response.status.should == 200
        json_response.should be_an Array
        json_response.length.should == 2
        json_response.first['title'].should == merge_request_closed.title
        json_response.second['title'].should == merge_request_merged.title
      end
      it "should return an array of merged merge_requests" do
        get api("/projects/#{project.id}/merge_requests?state=merged", user)
        response.status.should == 200
        json_response.should be_an Array
        json_response.length.should == 1
        json_response.first['title'].should == merge_request_merged.title
      end
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

      context "with ordering" do
        before do
          @mr_later = mr_with_later_created_and_updated_at_time
          @mr_earlier = mr_with_earlier_created_and_updated_at_time
        end

        it "should return an array of merge_requests in ascending order" do
          get api("/projects/#{project.id}/merge_requests?sort=asc", user)
          response.status.should == 200
          json_response.should be_an Array
          json_response.length.should == 3
          json_response.first['id'].should == @mr_earlier.id
          json_response.last['id'].should == @mr_later.id
        end
        it "should return an array of merge_requests in descending order" do
          get api("/projects/#{project.id}/merge_requests?sort=desc", user)
          response.status.should == 200
          json_response.should be_an Array
          json_response.length.should == 3
          json_response.first['id'].should == @mr_later.id
          json_response.last['id'].should == @mr_earlier.id
        end
        it "should return an array of merge_requests ordered by updated_at" do
          get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user)
          response.status.should == 200
          json_response.should be_an Array
          json_response.length.should == 3
          json_response.first['id'].should == @mr_earlier.id
          json_response.last['id'].should == @mr_later.id
        end
        it "should return an array of merge_requests ordered by created_at" do
          get api("/projects/#{project.id}/merge_requests?sort=created_at", user)
          response.status.should == 200
          json_response.should be_an Array
          json_response.length.should == 3
          json_response.first['id'].should == @mr_earlier.id
          json_response.last['id'].should == @mr_later.id
        end
99
      end
100 101 102 103 104
    end
  end

  describe "GET /projects/:id/merge_request/:merge_request_id" do
    it "should return merge_request" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
105
      get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user)
106 107
      response.status.should == 200
      json_response['title'].should == merge_request.title
108
      json_response['iid'].should == merge_request.iid
109
    end
110 111

    it "should return a 404 error if merge_request_id not found" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
112
      get api("/projects/#{project.id}/merge_request/999", user)
113 114
      response.status.should == 404
    end
115 116
  end

117 118 119 120 121 122 123 124 125 126 127 128 129
  describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do
    it 'should return the change information of the merge_request' do
      get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user)
      expect(response.status).to eq 200
      expect(json_response['changes'].size).to eq(merge_request.diffs.size)
    end

    it 'returns a 404 when merge_request_id not found' do
      get api("/projects/#{project.id}/merge_request/999/changes", user)
      expect(response.status).to eq(404)
    end
  end

130
  describe "POST /projects/:id/merge_requests" do
131 132
    context 'between branches projects' do
      it "should return merge_request" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
133
        post api("/projects/#{project.id}/merge_requests", user),
134 135 136 137 138
             title: 'Test merge_request',
             source_branch: 'stable',
             target_branch: 'master',
             author: user,
             labels: 'label, label2'
139 140
        response.status.should == 201
        json_response['title'].should == 'Test merge_request'
141
        json_response['labels'].should == ['label', 'label2']
142
      end
143

144
      it "should return 422 when source_branch equals target_branch" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
145
        post api("/projects/#{project.id}/merge_requests", user),
146
        title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
147 148
        response.status.should == 422
      end
149

150
      it "should return 400 when source_branch is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
151
        post api("/projects/#{project.id}/merge_requests", user),
152
        title: "Test merge_request", target_branch: "master", author: user
153 154
        response.status.should == 400
      end
155

156
      it "should return 400 when target_branch is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
157
        post api("/projects/#{project.id}/merge_requests", user),
158
        title: "Test merge_request", source_branch: "stable", author: user
159 160 161 162
        response.status.should == 400
      end

      it "should return 400 when title is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
163
        post api("/projects/#{project.id}/merge_requests", user),
164
        target_branch: 'master', source_branch: 'stable'
165 166
        response.status.should == 400
      end
167

Robert Schilling's avatar
Robert Schilling committed
168
      it 'should return 400 on invalid label names' do
169 170 171 172 173 174
        post api("/projects/#{project.id}/merge_requests", user),
             title: 'Test merge_request',
             source_branch: 'stable',
             target_branch: 'master',
             author: user,
             labels: 'label, ?'
Robert Schilling's avatar
Robert Schilling committed
175 176 177
        response.status.should == 400
        json_response['message']['labels']['?']['title'].should ==
          ['is invalid']
178
      end
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

      context 'with existing MR' do
        before do
          post api("/projects/#{project.id}/merge_requests", user),
               title: 'Test merge_request',
               source_branch: 'stable',
               target_branch: 'master',
               author: user
          @mr = MergeRequest.all.last
        end

        it 'should return 409 when MR already exists for source/target' do
          expect do
            post api("/projects/#{project.id}/merge_requests", user),
                 title: 'New test merge_request',
                 source_branch: 'stable',
                 target_branch: 'master',
                 author: user
          end.to change { MergeRequest.count }.by(0)
          response.status.should == 409
        end
      end
201
    end
202

203
    context 'forked projects' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
204 205 206
      let!(:user2) { create(:user) }
      let!(:fork_project) { create(:project, forked_from_project: project,  namespace: user2.namespace, creator_id: user2.id) }
      let!(:unrelated_project) { create(:project,  namespace: create(:user).namespace, creator_id: user2.id) }
207 208 209 210 211 212

      before :each do |each|
        fork_project.team << [user2, :reporters]
      end

      it "should return merge_request" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
213
        post api("/projects/#{fork_project.id}/merge_requests", user2),
214
        title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
215 216
        response.status.should == 201
        json_response['title'].should == 'Test merge_request'
217
        json_response['description'].should == 'Test description for Test merge_request'
218 219 220
      end

      it "should not return 422 when source_branch equals target_branch" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
221
        project.id.should_not == fork_project.id
222 223
        fork_project.forked?.should be_true
        fork_project.forked_from_project.should == project
Hiroyuki Sato's avatar
Hiroyuki Sato committed
224
        post api("/projects/#{fork_project.id}/merge_requests", user2),
225
        title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id
226 227 228 229 230
        response.status.should == 201
        json_response['title'].should == 'Test merge_request'
      end

      it "should return 400 when source_branch is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
231
        post api("/projects/#{fork_project.id}/merge_requests", user2),
232
        title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
233 234 235 236
        response.status.should == 400
      end

      it "should return 400 when target_branch is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
237
        post api("/projects/#{fork_project.id}/merge_requests", user2),
238
        title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
239 240 241 242
        response.status.should == 400
      end

      it "should return 400 when title is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
243
        post api("/projects/#{fork_project.id}/merge_requests", user2),
244
        target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id
245 246 247
        response.status.should == 400
      end

248 249 250 251 252 253 254 255 256 257
      context 'when target_branch is specified' do
        it 'should return 422 if not a forked project' do
          post api("/projects/#{project.id}/merge_requests", user),
               title: 'Test merge_request',
               target_branch: 'master',
               source_branch: 'stable',
               author: user,
               target_project_id: fork_project.id
          response.status.should == 422
        end
Izaak Alpert's avatar
Izaak Alpert committed
258

259 260 261 262 263 264 265 266 267
        it 'should return 422 if targeting a different fork' do
          post api("/projects/#{fork_project.id}/merge_requests", user2),
               title: 'Test merge_request',
               target_branch: 'master',
               source_branch: 'stable',
               author: user2,
               target_project_id: unrelated_project.id
          response.status.should == 422
        end
Izaak Alpert's avatar
Izaak Alpert committed
268 269 270
      end

      it "should return 201 when target_branch is specified and for the same project" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
271
        post api("/projects/#{fork_project.id}/merge_requests", user2),
272
        title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id
Izaak Alpert's avatar
Izaak Alpert committed
273 274
        response.status.should == 201
      end
275
    end
276 277
  end

278 279
  describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do
    it "should return merge_request" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
280
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close"
281 282 283 284 285
      response.status.should == 200
      json_response['state'].should == 'closed'
    end
  end

286 287 288 289
  describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
    it "should return merge_request in case of success" do
      MergeRequest.any_instance.stub(can_be_merged?: true, automerge!: true)
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
290
      response.status.should == 200
291 292 293 294 295 296 297
    end

    it "should return 405 if branch can't be merged" do
      MergeRequest.any_instance.stub(can_be_merged?: false)
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
      response.status.should == 405
      json_response['message'].should == 'Branch cannot be merged'
298
    end
299 300 301 302 303

    it "should return 405 if merge_request is not open" do
      merge_request.close
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
      response.status.should == 405
304
      json_response['message'].should == '405 Method Not Allowed'
305 306 307 308 309 310 311 312 313
    end

    it "should return 401 if user has no permissions to merge" do
      user2 = create(:user)
      project.team << [user2, :reporter]
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user2)
      response.status.should == 401
      json_response['message'].should == '401 Unauthorized'
    end
314 315
  end

316 317
  describe "PUT /projects/:id/merge_request/:merge_request_id" do
    it "should return merge_request" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
318
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title"
319
      response.status.should == 200
320
      json_response['title'].should == 'New title'
321
    end
322

323 324 325 326 327 328
    it "should return merge_request" do
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description"
      response.status.should == 200
      json_response['description'].should == 'New description'
    end

329
    it "should return 422 when source_branch and target_branch are renamed the same" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
330
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
331
      source_branch: "master", target_branch: "master"
332 333 334 335
      response.status.should == 422
    end

    it "should return merge_request with renamed target_branch" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
336
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki"
337
      response.status.should == 200
338
      json_response['target_branch'].should == 'wiki'
339
    end
340

Robert Schilling's avatar
Robert Schilling committed
341
    it 'should return 400 on invalid label names' do
342 343 344 345
      put api("/projects/#{project.id}/merge_request/#{merge_request.id}",
              user),
          title: 'new issue',
          labels: 'label, ?'
Robert Schilling's avatar
Robert Schilling committed
346 347
      response.status.should == 400
      json_response['message']['labels']['?']['title'].should == ['is invalid']
348
    end
349 350 351 352
  end

  describe "POST /projects/:id/merge_request/:merge_request_id/comments" do
    it "should return comment" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
353
      post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment"
354
      response.status.should == 201
355
      json_response['note'].should == 'My comment'
356
    end
357 358

    it "should return 400 if note is missing" do
Hiroyuki Sato's avatar
Hiroyuki Sato committed
359
      post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
360 361
      response.status.should == 400
    end
362 363

    it "should return 404 if note is attached to non existent merge request" do
364 365
      post api("/projects/#{project.id}/merge_request/404/comments", user),
           note: 'My comment'
366 367
      response.status.should == 404
    end
368
  end
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

  describe "GET :id/merge_request/:merge_request_id/comments" do
    it "should return merge_request comments" do
      get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
      response.status.should == 200
      json_response.should be_an Array
      json_response.length.should == 1
      json_response.first['note'].should == "a comment on a MR"
      json_response.first['author']['id'].should == user.id
    end

    it "should return a 404 error if merge_request_id not found" do
      get api("/projects/#{project.id}/merge_request/999/comments", user)
      response.status.should == 404
    end
  end
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

  def mr_with_later_created_and_updated_at_time
    merge_request
    merge_request.created_at += 1.hour
    merge_request.updated_at += 30.minutes
    merge_request.save
    merge_request
  end

  def mr_with_earlier_created_and_updated_at_time
    merge_request_closed
    merge_request_closed.created_at -= 1.hour
    merge_request_closed.updated_at -= 30.minutes
    merge_request_closed.save
    merge_request_closed
  end
401
end