issues_spec.rb 47.8 KB
Newer Older
1 2
require 'spec_helper'

3
describe API::V3::Issues do
Rémy Coutable's avatar
Rémy Coutable committed
4 5 6 7 8 9 10
  set(:user)        { create(:user) }
  set(:user2)       { create(:user) }
  set(:non_member)  { create(:user) }
  set(:guest)       { create(:user) }
  set(:author)      { create(:author) }
  set(:assignee)    { create(:assignee) }
  set(:admin)       { create(:user, :admin) }
11
  let!(:project)    { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
12 13 14
  let!(:closed_issue) do
    create :closed_issue,
           author: user,
15
           assignees: [user],
16 17 18
           project: project,
           state: :closed,
           milestone: milestone,
19
           created_at: generate(:past_time),
20 21 22 23 24 25 26
           updated_at: 3.hours.ago
  end
  let!(:confidential_issue) do
    create :issue,
           :confidential,
           project: project,
           author: author,
27
           assignees: [assignee],
28
           created_at: generate(:past_time),
29 30 31 32 33
           updated_at: 2.hours.ago
  end
  let!(:issue) do
    create :issue,
           author: user,
34
           assignees: [user],
35 36
           project: project,
           milestone: milestone,
37
           created_at: generate(:past_time),
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
           updated_at: 1.hour.ago
  end
  let!(:label) do
    create(:label, title: 'label', color: '#FFAABB', project: project)
  end
  let!(:label_link) { create(:label_link, label: label, target: issue) }
  let!(:milestone) { create(:milestone, title: '1.0.0', project: project) }
  let!(:empty_milestone) do
    create(:milestone, title: '2.0.0', project: project)
  end
  let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }

  let(:no_milestone_title) { URI.escape(Milestone::None.title) }

  before do
53 54
    project.add_reporter(user)
    project.add_guest(guest)
55 56 57 58 59 60 61
  end

  describe "GET /issues" do
    context "when unauthenticated" do
      it "returns authentication error" do
        get v3_api("/issues")

62
        expect(response).to have_gitlab_http_status(401)
63 64 65 66 67 68 69
      end
    end

    context "when authenticated" do
      it "returns an array of issues" do
        get v3_api("/issues", user)

70
        expect(response).to have_gitlab_http_status(200)
71 72 73 74 75 76 77 78
        expect(json_response).to be_an Array
        expect(json_response.first['title']).to eq(issue.title)
        expect(json_response.last).to have_key('web_url')
      end

      it 'returns an array of closed issues' do
        get v3_api('/issues?state=closed', user)

79
        expect(response).to have_gitlab_http_status(200)
80 81 82 83 84 85 86 87
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['id']).to eq(closed_issue.id)
      end

      it 'returns an array of opened issues' do
        get v3_api('/issues?state=opened', user)

88
        expect(response).to have_gitlab_http_status(200)
89 90 91 92 93 94 95 96
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['id']).to eq(issue.id)
      end

      it 'returns an array of all issues' do
        get v3_api('/issues?state=all', user)

97
        expect(response).to have_gitlab_http_status(200)
98 99 100 101 102 103 104 105 106
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(2)
        expect(json_response.first['id']).to eq(issue.id)
        expect(json_response.second['id']).to eq(closed_issue.id)
      end

      it 'returns an array of labeled issues' do
        get v3_api("/issues?labels=#{label.title}", user)

107
        expect(response).to have_gitlab_http_status(200)
108 109 110 111 112 113 114 115
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['labels']).to eq([label.title])
      end

      it 'returns an array of labeled issues when at least one label matches' do
        get v3_api("/issues?labels=#{label.title},foo,bar", user)

116
        expect(response).to have_gitlab_http_status(200)
117 118 119 120 121 122 123 124
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['labels']).to eq([label.title])
      end

      it 'returns an empty array if no issue matches labels' do
        get v3_api('/issues?labels=foo,bar', user)

125
        expect(response).to have_gitlab_http_status(200)
126 127 128 129 130 131 132
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(0)
      end

      it 'returns an array of labeled issues matching given state' do
        get v3_api("/issues?labels=#{label.title}&state=opened", user)

133
        expect(response).to have_gitlab_http_status(200)
134 135 136 137 138 139 140 141 142
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['labels']).to eq([label.title])
        expect(json_response.first['state']).to eq('opened')
      end

      it 'returns an empty array if no issue matches labels and state filters' do
        get v3_api("/issues?labels=#{label.title}&state=closed", user)

143
        expect(response).to have_gitlab_http_status(200)
144 145 146 147 148 149 150
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(0)
      end

      it 'returns an empty array if no issue matches milestone' do
        get v3_api("/issues?milestone=#{empty_milestone.title}", user)

151
        expect(response).to have_gitlab_http_status(200)
152 153 154 155 156 157 158
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(0)
      end

      it 'returns an empty array if milestone does not exist' do
        get v3_api("/issues?milestone=foo", user)

159
        expect(response).to have_gitlab_http_status(200)
160 161 162 163 164 165 166
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(0)
      end

      it 'returns an array of issues in given milestone' do
        get v3_api("/issues?milestone=#{milestone.title}", user)

167
        expect(response).to have_gitlab_http_status(200)
168 169 170 171 172 173 174 175 176 177
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(2)
        expect(json_response.first['id']).to eq(issue.id)
        expect(json_response.second['id']).to eq(closed_issue.id)
      end

      it 'returns an array of issues matching state in milestone' do
        get v3_api("/issues?milestone=#{milestone.title}",  user),
          '&state=closed'

178
        expect(response).to have_gitlab_http_status(200)
179 180 181 182 183 184 185 186
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['id']).to eq(closed_issue.id)
      end

      it 'returns an array of issues with no milestone' do
        get v3_api("/issues?milestone=#{no_milestone_title}", author)

187
        expect(response).to have_gitlab_http_status(200)
188 189 190 191 192 193 194 195 196 197
        expect(json_response).to be_an Array
        expect(json_response.length).to eq(1)
        expect(json_response.first['id']).to eq(confidential_issue.id)
      end

      it 'sorts by created_at descending by default' do
        get v3_api('/issues', user)

        response_dates = json_response.map { |issue| issue['created_at'] }

198
        expect(response).to have_gitlab_http_status(200)
199 200 201 202 203 204 205 206 207
        expect(json_response).to be_an Array
        expect(response_dates).to eq(response_dates.sort.reverse)
      end

      it 'sorts ascending when requested' do
        get v3_api('/issues?sort=asc', user)

        response_dates = json_response.map { |issue| issue['created_at'] }

208
        expect(response).to have_gitlab_http_status(200)
209 210 211 212 213 214 215 216 217
        expect(json_response).to be_an Array
        expect(response_dates).to eq(response_dates.sort)
      end

      it 'sorts by updated_at descending when requested' do
        get v3_api('/issues?order_by=updated_at', user)

        response_dates = json_response.map { |issue| issue['updated_at'] }

218
        expect(response).to have_gitlab_http_status(200)
219 220 221 222 223 224 225 226 227
        expect(json_response).to be_an Array
        expect(response_dates).to eq(response_dates.sort.reverse)
      end

      it 'sorts by updated_at ascending when requested' do
        get v3_api('/issues?order_by=updated_at&sort=asc', user)

        response_dates = json_response.map { |issue| issue['updated_at'] }

228
        expect(response).to have_gitlab_http_status(200)
229 230 231
        expect(json_response).to be_an Array
        expect(response_dates).to eq(response_dates.sort)
      end
232 233 234 235

      it 'matches V3 response schema' do
        get v3_api('/issues', user)

236
        expect(response).to have_gitlab_http_status(200)
237 238
        expect(response).to match_response_schema('public_api/v3/issues')
      end
239 240 241 242 243
    end
  end

  describe "GET /groups/:id/issues" do
    let!(:group)            { create(:group) }
244
    let!(:group_project)    { create(:project, :public, creator_id: user.id, namespace: group) }
245 246 247
    let!(:group_closed_issue) do
      create :closed_issue,
             author: user,
248
             assignees: [user],
249 250 251 252 253 254 255 256 257 258
             project: group_project,
             state: :closed,
             milestone: group_milestone,
             updated_at: 3.hours.ago
    end
    let!(:group_confidential_issue) do
      create :issue,
             :confidential,
             project: group_project,
             author: author,
259
             assignees: [assignee],
260 261 262 263 264
             updated_at: 2.hours.ago
    end
    let!(:group_issue) do
      create :issue,
             author: user,
265
             assignees: [user],
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
             project: group_project,
             milestone: group_milestone,
             updated_at: 1.hour.ago
    end
    let!(:group_label) do
      create(:label, title: 'group_lbl', color: '#FFAABB', project: group_project)
    end
    let!(:group_label_link) { create(:label_link, label: group_label, target: group_issue) }
    let!(:group_milestone) { create(:milestone, title: '3.0.0', project: group_project) }
    let!(:group_empty_milestone) do
      create(:milestone, title: '4.0.0', project: group_project)
    end
    let!(:group_note) { create(:note_on_issue, author: user, project: group_project, noteable: group_issue) }

    before do
281
      group_project.add_reporter(user)
282 283 284
    end
    let(:base_url) { "/groups/#{group.id}/issues" }

285 286 287
    it 'returns all group issues (including opened and closed)' do
      get v3_api(base_url, admin)

288
      expect(response).to have_gitlab_http_status(200)
289 290 291 292
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(3)
    end

293
    it 'returns group issues without confidential issues for non project members' do
294
      get v3_api("#{base_url}?state=opened", non_member)
295

296
      expect(response).to have_gitlab_http_status(200)
297 298 299 300 301 302
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['title']).to eq(group_issue.title)
    end

    it 'returns group confidential issues for author' do
303
      get v3_api("#{base_url}?state=opened", author)
304

305
      expect(response).to have_gitlab_http_status(200)
306 307 308 309 310
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
    end

    it 'returns group confidential issues for assignee' do
311
      get v3_api("#{base_url}?state=opened", assignee)
312

313
      expect(response).to have_gitlab_http_status(200)
314 315 316 317 318
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
    end

    it 'returns group issues with confidential issues for project members' do
319
      get v3_api("#{base_url}?state=opened", user)
320

321
      expect(response).to have_gitlab_http_status(200)
322 323 324 325 326
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
    end

    it 'returns group confidential issues for admin' do
327
      get v3_api("#{base_url}?state=opened", admin)
328

329
      expect(response).to have_gitlab_http_status(200)
330 331 332 333 334 335 336
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
    end

    it 'returns an array of labeled group issues' do
      get v3_api("#{base_url}?labels=#{group_label.title}", user)

337
      expect(response).to have_gitlab_http_status(200)
338 339 340 341 342 343 344 345
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['labels']).to eq([group_label.title])
    end

    it 'returns an array of labeled group issues where all labels match' do
      get v3_api("#{base_url}?labels=#{group_label.title},foo,bar", user)

346
      expect(response).to have_gitlab_http_status(200)
347 348 349 350 351 352 353
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an empty array if no group issue matches labels' do
      get v3_api("#{base_url}?labels=foo,bar", user)

354
      expect(response).to have_gitlab_http_status(200)
355 356 357 358 359 360 361
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an empty array if no issue matches milestone' do
      get v3_api("#{base_url}?milestone=#{group_empty_milestone.title}", user)

362
      expect(response).to have_gitlab_http_status(200)
363 364 365 366 367 368 369
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an empty array if milestone does not exist' do
      get v3_api("#{base_url}?milestone=foo", user)

370
      expect(response).to have_gitlab_http_status(200)
371 372 373 374 375
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an array of issues in given milestone' do
376
      get v3_api("#{base_url}?state=opened&milestone=#{group_milestone.title}", user)
377

378
      expect(response).to have_gitlab_http_status(200)
379 380 381 382 383 384 385 386 387
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['id']).to eq(group_issue.id)
    end

    it 'returns an array of issues matching state in milestone' do
      get v3_api("#{base_url}?milestone=#{group_milestone.title}", user),
        '&state=closed'

388
      expect(response).to have_gitlab_http_status(200)
389 390 391 392 393 394 395 396
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['id']).to eq(group_closed_issue.id)
    end

    it 'returns an array of issues with no milestone' do
      get v3_api("#{base_url}?milestone=#{no_milestone_title}", user)

397
      expect(response).to have_gitlab_http_status(200)
398 399 400 401 402 403 404 405 406 407
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['id']).to eq(group_confidential_issue.id)
    end

    it 'sorts by created_at descending by default' do
      get v3_api(base_url, user)

      response_dates = json_response.map { |issue| issue['created_at'] }

408
      expect(response).to have_gitlab_http_status(200)
409 410 411 412 413 414 415 416 417
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort.reverse)
    end

    it 'sorts ascending when requested' do
      get v3_api("#{base_url}?sort=asc", user)

      response_dates = json_response.map { |issue| issue['created_at'] }

418
      expect(response).to have_gitlab_http_status(200)
419 420 421 422 423 424 425 426 427
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort)
    end

    it 'sorts by updated_at descending when requested' do
      get v3_api("#{base_url}?order_by=updated_at", user)

      response_dates = json_response.map { |issue| issue['updated_at'] }

428
      expect(response).to have_gitlab_http_status(200)
429 430 431 432 433 434 435 436 437
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort.reverse)
    end

    it 'sorts by updated_at ascending when requested' do
      get v3_api("#{base_url}?order_by=updated_at&sort=asc", user)

      response_dates = json_response.map { |issue| issue['updated_at'] }

438
      expect(response).to have_gitlab_http_status(200)
439 440 441 442 443 444 445 446
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort)
    end
  end

  describe "GET /projects/:id/issues" do
    let(:base_url) { "/projects/#{project.id}" }

447 448 449
    it 'returns 404 when project does not exist' do
      get v3_api('/projects/1000/issues', non_member)

450
      expect(response).to have_gitlab_http_status(404)
451 452
    end

453
    it "returns 404 on private projects for other users" do
454
      private_project = create(:project, :private)
455 456 457 458
      create(:issue, project: private_project)

      get v3_api("/projects/#{private_project.id}/issues", non_member)

459
      expect(response).to have_gitlab_http_status(404)
460 461 462
    end

    it 'returns no issues when user has access to project but not issues' do
463
      restricted_project = create(:project, :public, issues_access_level: ProjectFeature::PRIVATE)
464 465 466 467 468 469 470 471 472 473
      create(:issue, project: restricted_project)

      get v3_api("/projects/#{restricted_project.id}/issues", non_member)

      expect(json_response).to eq([])
    end

    it 'returns project issues without confidential issues for non project members' do
      get v3_api("#{base_url}/issues", non_member)

474
      expect(response).to have_gitlab_http_status(200)
475 476 477 478 479 480 481 482
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
      expect(json_response.first['title']).to eq(issue.title)
    end

    it 'returns project issues without confidential issues for project members with guest role' do
      get v3_api("#{base_url}/issues", guest)

483
      expect(response).to have_gitlab_http_status(200)
484 485 486 487 488 489 490 491
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
      expect(json_response.first['title']).to eq(issue.title)
    end

    it 'returns project confidential issues for author' do
      get v3_api("#{base_url}/issues", author)

492
      expect(response).to have_gitlab_http_status(200)
493 494 495 496 497 498 499 500
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(3)
      expect(json_response.first['title']).to eq(issue.title)
    end

    it 'returns project confidential issues for assignee' do
      get v3_api("#{base_url}/issues", assignee)

501
      expect(response).to have_gitlab_http_status(200)
502 503 504 505 506 507 508 509
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(3)
      expect(json_response.first['title']).to eq(issue.title)
    end

    it 'returns project issues with confidential issues for project members' do
      get v3_api("#{base_url}/issues", user)

510
      expect(response).to have_gitlab_http_status(200)
511 512 513 514 515 516 517 518
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(3)
      expect(json_response.first['title']).to eq(issue.title)
    end

    it 'returns project confidential issues for admin' do
      get v3_api("#{base_url}/issues", admin)

519
      expect(response).to have_gitlab_http_status(200)
520 521 522 523 524 525 526 527
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(3)
      expect(json_response.first['title']).to eq(issue.title)
    end

    it 'returns an array of labeled project issues' do
      get v3_api("#{base_url}/issues?labels=#{label.title}", user)

528
      expect(response).to have_gitlab_http_status(200)
529 530 531 532 533 534 535 536
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['labels']).to eq([label.title])
    end

    it 'returns an array of labeled project issues where all labels match' do
      get v3_api("#{base_url}/issues?labels=#{label.title},foo,bar", user)

537
      expect(response).to have_gitlab_http_status(200)
538 539 540 541 542 543 544 545
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['labels']).to eq([label.title])
    end

    it 'returns an empty array if no project issue matches labels' do
      get v3_api("#{base_url}/issues?labels=foo,bar", user)

546
      expect(response).to have_gitlab_http_status(200)
547 548 549 550 551 552 553
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an empty array if no issue matches milestone' do
      get v3_api("#{base_url}/issues?milestone=#{empty_milestone.title}", user)

554
      expect(response).to have_gitlab_http_status(200)
555 556 557 558 559 560 561
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an empty array if milestone does not exist' do
      get v3_api("#{base_url}/issues?milestone=foo", user)

562
      expect(response).to have_gitlab_http_status(200)
563 564 565 566 567 568 569
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(0)
    end

    it 'returns an array of issues in given milestone' do
      get v3_api("#{base_url}/issues?milestone=#{milestone.title}", user)

570
      expect(response).to have_gitlab_http_status(200)
571 572 573 574 575 576 577 578 579 580
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(2)
      expect(json_response.first['id']).to eq(issue.id)
      expect(json_response.second['id']).to eq(closed_issue.id)
    end

    it 'returns an array of issues matching state in milestone' do
      get v3_api("#{base_url}/issues?milestone=#{milestone.title}", user),
        '&state=closed'

581
      expect(response).to have_gitlab_http_status(200)
582 583 584 585 586 587 588 589
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['id']).to eq(closed_issue.id)
    end

    it 'returns an array of issues with no milestone' do
      get v3_api("#{base_url}/issues?milestone=#{no_milestone_title}", user)

590
      expect(response).to have_gitlab_http_status(200)
591 592 593 594 595 596 597 598 599 600
      expect(json_response).to be_an Array
      expect(json_response.length).to eq(1)
      expect(json_response.first['id']).to eq(confidential_issue.id)
    end

    it 'sorts by created_at descending by default' do
      get v3_api("#{base_url}/issues", user)

      response_dates = json_response.map { |issue| issue['created_at'] }

601
      expect(response).to have_gitlab_http_status(200)
602 603 604 605 606 607 608 609 610
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort.reverse)
    end

    it 'sorts ascending when requested' do
      get v3_api("#{base_url}/issues?sort=asc", user)

      response_dates = json_response.map { |issue| issue['created_at'] }

611
      expect(response).to have_gitlab_http_status(200)
612 613 614 615 616 617 618 619 620
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort)
    end

    it 'sorts by updated_at descending when requested' do
      get v3_api("#{base_url}/issues?order_by=updated_at", user)

      response_dates = json_response.map { |issue| issue['updated_at'] }

621
      expect(response).to have_gitlab_http_status(200)
622 623 624 625 626 627 628 629 630
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort.reverse)
    end

    it 'sorts by updated_at ascending when requested' do
      get v3_api("#{base_url}/issues?order_by=updated_at&sort=asc", user)

      response_dates = json_response.map { |issue| issue['updated_at'] }

631
      expect(response).to have_gitlab_http_status(200)
632 633 634 635 636 637 638 639 640
      expect(json_response).to be_an Array
      expect(response_dates).to eq(response_dates.sort)
    end
  end

  describe "GET /projects/:id/issues/:issue_id" do
    it 'exposes known attributes' do
      get v3_api("/projects/#{project.id}/issues/#{issue.id}", user)

641
      expect(response).to have_gitlab_http_status(200)
642 643 644 645 646 647 648 649 650 651 652 653 654
      expect(json_response['id']).to eq(issue.id)
      expect(json_response['iid']).to eq(issue.iid)
      expect(json_response['project_id']).to eq(issue.project.id)
      expect(json_response['title']).to eq(issue.title)
      expect(json_response['description']).to eq(issue.description)
      expect(json_response['state']).to eq(issue.state)
      expect(json_response['created_at']).to be_present
      expect(json_response['updated_at']).to be_present
      expect(json_response['labels']).to eq(issue.label_names)
      expect(json_response['milestone']).to be_a Hash
      expect(json_response['assignee']).to be_a Hash
      expect(json_response['author']).to be_a Hash
      expect(json_response['confidential']).to be_falsy
655
      expect(json_response['weight']).to be_nil
656 657 658 659 660
    end

    it "returns a project issue by id" do
      get v3_api("/projects/#{project.id}/issues/#{issue.id}", user)

661
      expect(response).to have_gitlab_http_status(200)
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
      expect(json_response['title']).to eq(issue.title)
      expect(json_response['iid']).to eq(issue.iid)
    end

    it 'returns a project issue by iid' do
      get v3_api("/projects/#{project.id}/issues?iid=#{issue.iid}", user)

      expect(response.status).to eq 200
      expect(json_response.length).to eq 1
      expect(json_response.first['title']).to eq issue.title
      expect(json_response.first['id']).to eq issue.id
      expect(json_response.first['iid']).to eq issue.iid
    end

    it 'returns an empty array for an unknown project issue iid' do
      get v3_api("/projects/#{project.id}/issues?iid=#{issue.iid + 10}", user)

      expect(response.status).to eq 200
      expect(json_response.length).to eq 0
    end

    it "returns 404 if issue id not found" do
      get v3_api("/projects/#{project.id}/issues/54321", user)

686
      expect(response).to have_gitlab_http_status(404)
687 688 689 690 691 692
    end

    context 'confidential issues' do
      it "returns 404 for non project members" do
        get v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member)

693
        expect(response).to have_gitlab_http_status(404)
694 695 696 697 698
      end

      it "returns 404 for project members with guest role" do
        get v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest)

699
        expect(response).to have_gitlab_http_status(404)
700 701 702 703 704
      end

      it "returns confidential issue for project members" do
        get v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", user)

705
        expect(response).to have_gitlab_http_status(200)
706 707 708 709 710 711 712
        expect(json_response['title']).to eq(confidential_issue.title)
        expect(json_response['iid']).to eq(confidential_issue.iid)
      end

      it "returns confidential issue for author" do
        get v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", author)

713
        expect(response).to have_gitlab_http_status(200)
714 715 716 717 718 719 720
        expect(json_response['title']).to eq(confidential_issue.title)
        expect(json_response['iid']).to eq(confidential_issue.iid)
      end

      it "returns confidential issue for assignee" do
        get v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", assignee)

721
        expect(response).to have_gitlab_http_status(200)
722 723 724 725 726 727 728
        expect(json_response['title']).to eq(confidential_issue.title)
        expect(json_response['iid']).to eq(confidential_issue.iid)
      end

      it "returns confidential issue for admin" do
        get v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin)

729
        expect(response).to have_gitlab_http_status(200)
730 731 732 733 734 735 736 737 738
        expect(json_response['title']).to eq(confidential_issue.title)
        expect(json_response['iid']).to eq(confidential_issue.iid)
      end
    end
  end

  describe "POST /projects/:id/issues" do
    it 'creates a new project issue' do
      post v3_api("/projects/#{project.id}/issues", user),
739
        title: 'new issue', labels: 'label, label2', weight: 3, assignee_id: assignee.id
740

741
      expect(response).to have_gitlab_http_status(201)
742 743
      expect(json_response['title']).to eq('new issue')
      expect(json_response['description']).to be_nil
Douwe Maan's avatar
Douwe Maan committed
744
      expect(json_response['labels']).to eq(%w(label label2))
745
      expect(json_response['confidential']).to be_falsy
746
      expect(json_response['weight']).to eq(3)
747
      expect(json_response['assignee']['name']).to eq(assignee.name)
748 749 750 751 752 753
    end

    it 'creates a new confidential project issue' do
      post v3_api("/projects/#{project.id}/issues", user),
        title: 'new issue', confidential: true

754
      expect(response).to have_gitlab_http_status(201)
755 756 757 758 759 760 761 762
      expect(json_response['title']).to eq('new issue')
      expect(json_response['confidential']).to be_truthy
    end

    it 'creates a new confidential project issue with a different param' do
      post v3_api("/projects/#{project.id}/issues", user),
        title: 'new issue', confidential: 'y'

763
      expect(response).to have_gitlab_http_status(201)
764 765 766 767 768 769 770 771
      expect(json_response['title']).to eq('new issue')
      expect(json_response['confidential']).to be_truthy
    end

    it 'creates a public issue when confidential param is false' do
      post v3_api("/projects/#{project.id}/issues", user),
        title: 'new issue', confidential: false

772
      expect(response).to have_gitlab_http_status(201)
773 774 775 776 777 778 779 780
      expect(json_response['title']).to eq('new issue')
      expect(json_response['confidential']).to be_falsy
    end

    it 'creates a public issue when confidential param is invalid' do
      post v3_api("/projects/#{project.id}/issues", user),
        title: 'new issue', confidential: 'foo'

781
      expect(response).to have_gitlab_http_status(400)
782 783 784 785 786 787
      expect(json_response['error']).to eq('confidential is invalid')
    end

    it "returns a 400 bad request if title not given" do
      post v3_api("/projects/#{project.id}/issues", user), labels: 'label, label2'

788
      expect(response).to have_gitlab_http_status(400)
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
    end

    it 'allows special label names' do
      post v3_api("/projects/#{project.id}/issues", user),
           title: 'new issue',
           labels: 'label, label?, label&foo, ?, &'

      expect(response.status).to eq(201)
      expect(json_response['labels']).to include 'label'
      expect(json_response['labels']).to include 'label?'
      expect(json_response['labels']).to include 'label&foo'
      expect(json_response['labels']).to include '?'
      expect(json_response['labels']).to include '&'
    end

    it 'returns 400 if title is too long' do
      post v3_api("/projects/#{project.id}/issues", user),
           title: 'g' * 256

808
      expect(response).to have_gitlab_http_status(400)
809 810 811 812 813 814
      expect(json_response['message']['title']).to eq([
        'is too long (maximum is 255 characters)'
      ])
    end

    context 'resolving issues in a merge request' do
Rémy Coutable's avatar
Rémy Coutable committed
815 816
      set(:diff_note_on_merge_request) { create(:diff_note_on_merge_request) }
      let(:discussion) { diff_note_on_merge_request.to_discussion }
817 818 819
      let(:merge_request) { discussion.noteable }
      let(:project) { merge_request.source_project }
      before do
820
        project.add_master(user)
821 822 823 824 825 826
        post v3_api("/projects/#{project.id}/issues", user),
             title: 'New Issue',
             merge_request_for_resolving_discussions: merge_request.iid
      end

      it 'creates a new project issue' do
827
        expect(response).to have_gitlab_http_status(:created)
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
      end

      it 'resolves the discussions in a merge request' do
        discussion.first_note.reload

        expect(discussion.resolved?).to be(true)
      end

      it 'assigns a description to the issue mentioning the merge request' do
        expect(json_response['description']).to include(merge_request.to_reference)
      end
    end

    context 'with due date' do
      it 'creates a new project issue' do
        due_date = 2.weeks.from_now.strftime('%Y-%m-%d')

        post v3_api("/projects/#{project.id}/issues", user),
          title: 'new issue', due_date: due_date

848
        expect(response).to have_gitlab_http_status(201)
849 850 851 852 853 854 855 856 857 858 859 860
        expect(json_response['title']).to eq('new issue')
        expect(json_response['description']).to be_nil
        expect(json_response['due_date']).to eq(due_date)
      end
    end

    context 'when an admin or owner makes the request' do
      it 'accepts the creation date to be set' do
        creation_time = 2.weeks.ago
        post v3_api("/projects/#{project.id}/issues", user),
          title: 'new issue', labels: 'label, label2', created_at: creation_time

861
        expect(response).to have_gitlab_http_status(201)
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
        expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
      end
    end

    context 'the user can only read the issue' do
      it 'cannot create new labels' do
        expect do
          post v3_api("/projects/#{project.id}/issues", non_member), title: 'new issue', labels: 'label, label2'
        end.not_to change { project.labels.count }
      end
    end
  end

  describe 'POST /projects/:id/issues with spam filtering' do
    before do
      allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true)
878
      allow_any_instance_of(AkismetService).to receive_messages(spam?: true)
879 880 881 882 883 884 885 886 887 888 889 890 891
    end

    let(:params) do
      {
        title: 'new issue',
        description: 'content here',
        labels: 'label, label2'
      }
    end

    it "does not create a new project issue" do
      expect { post v3_api("/projects/#{project.id}/issues", user), params }.not_to change(Issue, :count)

892
      expect(response).to have_gitlab_http_status(400)
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
      expect(json_response['message']).to eq({ "error" => "Spam detected" })

      spam_logs = SpamLog.all

      expect(spam_logs.count).to eq(1)
      expect(spam_logs[0].title).to eq('new issue')
      expect(spam_logs[0].description).to eq('content here')
      expect(spam_logs[0].user).to eq(user)
      expect(spam_logs[0].noteable_type).to eq('Issue')
    end
  end

  describe "PUT /projects/:id/issues/:issue_id to update only title" do
    it "updates a project issue" do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
        title: 'updated title'

910
      expect(response).to have_gitlab_http_status(200)
911 912 913 914 915 916 917
      expect(json_response['title']).to eq('updated title')
    end

    it "returns 404 error if issue id not found" do
      put v3_api("/projects/#{project.id}/issues/44444", user),
        title: 'updated title'

918
      expect(response).to have_gitlab_http_status(404)
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
    end

    it 'allows special label names' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          title: 'updated title',
          labels: 'label, label?, label&foo, ?, &'

      expect(response.status).to eq(200)
      expect(json_response['labels']).to include 'label'
      expect(json_response['labels']).to include 'label?'
      expect(json_response['labels']).to include 'label&foo'
      expect(json_response['labels']).to include '?'
      expect(json_response['labels']).to include '&'
    end

    context 'confidential issues' do
      it "returns 403 for non project members" do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member),
          title: 'updated title'

939
        expect(response).to have_gitlab_http_status(403)
940 941 942 943 944 945
      end

      it "returns 403 for project members with guest role" do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest),
          title: 'updated title'

946
        expect(response).to have_gitlab_http_status(403)
947 948 949 950 951 952
      end

      it "updates a confidential issue for project members" do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
          title: 'updated title'

953
        expect(response).to have_gitlab_http_status(200)
954 955 956 957 958 959 960
        expect(json_response['title']).to eq('updated title')
      end

      it "updates a confidential issue for author" do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", author),
          title: 'updated title'

961
        expect(response).to have_gitlab_http_status(200)
962 963 964 965 966 967 968
        expect(json_response['title']).to eq('updated title')
      end

      it "updates a confidential issue for admin" do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin),
          title: 'updated title'

969
        expect(response).to have_gitlab_http_status(200)
970 971 972 973 974 975 976
        expect(json_response['title']).to eq('updated title')
      end

      it 'sets an issue to confidential' do
        put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          confidential: true

977
        expect(response).to have_gitlab_http_status(200)
978 979 980 981 982 983 984
        expect(json_response['confidential']).to be_truthy
      end

      it 'makes a confidential issue public' do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
          confidential: false

985
        expect(response).to have_gitlab_http_status(200)
986 987 988 989 990 991 992
        expect(json_response['confidential']).to be_falsy
      end

      it 'does not update a confidential issue with wrong confidential flag' do
        put v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
          confidential: 'foo'

993
        expect(response).to have_gitlab_http_status(400)
994 995 996 997 998
        expect(json_response['error']).to eq('confidential is invalid')
      end
    end
  end

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
  describe 'PUT /projects/:id/issues/:issue_id with spam filtering' do
    let(:params) do
      {
        title: 'updated title',
        description: 'content here',
        labels: 'label, label2'
      }
    end

    it "does not create a new project issue" do
      allow_any_instance_of(SpamService).to receive_messages(check_for_spam?: true)
1010
      allow_any_instance_of(AkismetService).to receive_messages(spam?: true)
1011 1012 1013

      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), params

1014
      expect(response).to have_gitlab_http_status(400)
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
      expect(json_response['message']).to eq({ "error" => "Spam detected" })

      spam_logs = SpamLog.all
      expect(spam_logs.count).to eq(1)
      expect(spam_logs[0].title).to eq('updated title')
      expect(spam_logs[0].description).to eq('content here')
      expect(spam_logs[0].user).to eq(user)
      expect(spam_logs[0].noteable_type).to eq('Issue')
    end
  end

1026 1027 1028 1029 1030 1031 1032 1033
  describe 'PUT /projects/:id/issues/:issue_id to update labels' do
    let!(:label) { create(:label, title: 'dummy', project: project) }
    let!(:label_link) { create(:label_link, label: label, target: issue) }

    it 'does not update labels if not present' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          title: 'updated title'

1034
      expect(response).to have_gitlab_http_status(200)
1035 1036 1037 1038 1039 1040
      expect(json_response['labels']).to eq([label.title])
    end

    it 'removes all labels' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), labels: ''

1041
      expect(response).to have_gitlab_http_status(200)
1042 1043 1044 1045 1046 1047 1048
      expect(json_response['labels']).to eq([])
    end

    it 'updates labels' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          labels: 'foo,bar'

1049
      expect(response).to have_gitlab_http_status(200)
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
      expect(json_response['labels']).to include 'foo'
      expect(json_response['labels']).to include 'bar'
    end

    it 'allows special label names' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          labels: 'label:foo, label-bar,label_bar,label/bar,label?bar,label&bar,?,&'

      expect(response.status).to eq(200)
      expect(json_response['labels']).to include 'label:foo'
      expect(json_response['labels']).to include 'label-bar'
      expect(json_response['labels']).to include 'label_bar'
      expect(json_response['labels']).to include 'label/bar'
      expect(json_response['labels']).to include 'label?bar'
      expect(json_response['labels']).to include 'label&bar'
      expect(json_response['labels']).to include '?'
      expect(json_response['labels']).to include '&'
    end

    it 'returns 400 if title is too long' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          title: 'g' * 256

1073
      expect(response).to have_gitlab_http_status(400)
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
      expect(json_response['message']['title']).to eq([
        'is too long (maximum is 255 characters)'
      ])
    end
  end

  describe "PUT /projects/:id/issues/:issue_id to update state and label" do
    it "updates a project issue" do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
        labels: 'label2', state_event: "close"

1085
      expect(response).to have_gitlab_http_status(200)
1086 1087 1088 1089 1090 1091 1092
      expect(json_response['labels']).to include 'label2'
      expect(json_response['state']).to eq "closed"
    end

    it 'reopens a project isssue' do
      put v3_api("/projects/#{project.id}/issues/#{closed_issue.id}", user), state_event: 'reopen'

1093
      expect(response).to have_gitlab_http_status(200)
1094
      expect(json_response['state']).to eq 'opened'
1095 1096 1097 1098 1099 1100 1101 1102
    end

    context 'when an admin or owner makes the request' do
      it 'accepts the update date to be set' do
        update_time = 2.weeks.ago
        put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
          labels: 'label3', state_event: 'close', updated_at: update_time

1103
        expect(response).to have_gitlab_http_status(200)
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
        expect(json_response['labels']).to include 'label3'
        expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time)
      end
    end
  end

  describe 'PUT /projects/:id/issues/:issue_id to update due date' do
    it 'creates a new project issue' do
      due_date = 2.weeks.from_now.strftime('%Y-%m-%d')

      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), due_date: due_date

1116
      expect(response).to have_gitlab_http_status(200)
1117 1118 1119 1120
      expect(json_response['due_date']).to eq(due_date)
    end
  end

1121 1122 1123 1124
  describe 'PUT /projects/:id/issues/:issue_id to update assignee' do
    it 'updates an issue with no assignee' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), assignee_id: 0

1125
      expect(response).to have_gitlab_http_status(200)
1126 1127 1128 1129 1130 1131
      expect(json_response['assignee']).to eq(nil)
    end

    it 'updates an issue with assignee' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), assignee_id: user2.id

1132
      expect(response).to have_gitlab_http_status(200)
1133 1134 1135 1136
      expect(json_response['assignee']['name']).to eq(user2.name)
    end
  end

1137 1138 1139 1140
  describe 'PUT /projects/:id/issues/:issue_id to update weight' do
    it 'updates an issue with no weight' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), weight: 5

1141
      expect(response).to have_gitlab_http_status(200)
1142 1143 1144 1145 1146 1147 1148 1149
      expect(json_response['weight']).to eq(5)
    end

    it 'removes a weight from an issue' do
      weighted_issue = create(:issue, project: project, weight: 2)

      put v3_api("/projects/#{project.id}/issues/#{weighted_issue.id}", user), weight: nil

1150
      expect(response).to have_gitlab_http_status(200)
1151 1152 1153 1154 1155 1156
      expect(json_response['weight']).to be_nil
    end

    it 'returns 400 if weight is less than minimum weight' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), weight: -1

1157
      expect(response).to have_gitlab_http_status(400)
1158 1159 1160 1161 1162 1163
      expect(json_response['error']).to eq('weight does not have a valid value')
    end

    it 'returns 400 if weight is more than maximum weight' do
      put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), weight: 10

1164
      expect(response).to have_gitlab_http_status(400)
1165 1166
      expect(json_response['error']).to eq('weight does not have a valid value')
    end
1167 1168 1169 1170 1171 1172 1173 1174 1175

    context 'issuable weights unlicensed' do
      before do
        stub_licensed_features(issue_weights: false)
      end

      it 'ignores the update' do
        put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), weight: 5

1176
        expect(response).to have_gitlab_http_status(200)
1177 1178 1179 1180
        expect(json_response['weight']).to be_nil
        expect(issue.reload.read_attribute(:weight)).to be_nil
      end
    end
1181 1182
  end

1183 1184 1185 1186
  describe "DELETE /projects/:id/issues/:issue_id" do
    it "rejects a non member from deleting an issue" do
      delete v3_api("/projects/#{project.id}/issues/#{issue.id}", non_member)

1187
      expect(response).to have_gitlab_http_status(403)
1188 1189 1190 1191 1192
    end

    it "rejects a developer from deleting an issue" do
      delete v3_api("/projects/#{project.id}/issues/#{issue.id}", author)

1193
      expect(response).to have_gitlab_http_status(403)
1194 1195 1196
    end

    context "when the user is project owner" do
Rémy Coutable's avatar
Rémy Coutable committed
1197
      set(:owner)     { create(:user) }
1198
      let(:project)   { create(:project, namespace: owner.namespace) }
1199 1200 1201 1202

      it "deletes the issue if an admin requests it" do
        delete v3_api("/projects/#{project.id}/issues/#{issue.id}", owner)

1203
        expect(response).to have_gitlab_http_status(200)
1204 1205 1206 1207 1208 1209 1210 1211
        expect(json_response['state']).to eq 'opened'
      end
    end

    context 'when issue does not exist' do
      it 'returns 404 when trying to move an issue' do
        delete v3_api("/projects/#{project.id}/issues/123", user)

1212
        expect(response).to have_gitlab_http_status(404)
1213 1214 1215 1216 1217
      end
    end
  end

  describe '/projects/:id/issues/:issue_id/move' do
1218
    let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) }
1219
    let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
1220 1221 1222 1223 1224

    it 'moves an issue' do
      post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user),
               to_project_id: target_project.id

1225
      expect(response).to have_gitlab_http_status(201)
1226 1227 1228 1229 1230 1231 1232 1233
      expect(json_response['project_id']).to eq(target_project.id)
    end

    context 'when source and target projects are the same' do
      it 'returns 400 when trying to move an issue' do
        post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                 to_project_id: project.id

1234
        expect(response).to have_gitlab_http_status(400)
1235 1236 1237 1238 1239 1240 1241 1242 1243
        expect(json_response['message']).to eq('Cannot move issue to project it originates from!')
      end
    end

    context 'when the user does not have the permission to move issues' do
      it 'returns 400 when trying to move an issue' do
        post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                 to_project_id: target_project2.id

1244
        expect(response).to have_gitlab_http_status(400)
1245 1246 1247 1248 1249 1250 1251 1252
        expect(json_response['message']).to eq('Cannot move issue due to insufficient permissions!')
      end
    end

    it 'moves the issue to another namespace if I am admin' do
      post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", admin),
               to_project_id: target_project2.id

1253
      expect(response).to have_gitlab_http_status(201)
1254 1255 1256 1257 1258 1259 1260 1261
      expect(json_response['project_id']).to eq(target_project2.id)
    end

    context 'when issue does not exist' do
      it 'returns 404 when trying to move an issue' do
        post v3_api("/projects/#{project.id}/issues/123/move", user),
                 to_project_id: target_project.id

1262
        expect(response).to have_gitlab_http_status(404)
1263 1264 1265 1266 1267 1268 1269 1270 1271
        expect(json_response['message']).to eq('404 Issue Not Found')
      end
    end

    context 'when source project does not exist' do
      it 'returns 404 when trying to move an issue' do
        post v3_api("/projects/123/issues/#{issue.id}/move", user),
                 to_project_id: target_project.id

1272
        expect(response).to have_gitlab_http_status(404)
1273 1274 1275 1276 1277 1278 1279 1280 1281
        expect(json_response['message']).to eq('404 Project Not Found')
      end
    end

    context 'when target project does not exist' do
      it 'returns 404 when trying to move an issue' do
        post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                 to_project_id: 123

1282
        expect(response).to have_gitlab_http_status(404)
1283 1284 1285 1286 1287 1288 1289 1290
      end
    end
  end

  describe 'POST :id/issues/:issue_id/subscription' do
    it 'subscribes to an issue' do
      post v3_api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2)

1291
      expect(response).to have_gitlab_http_status(201)
1292 1293 1294 1295 1296 1297
      expect(json_response['subscribed']).to eq(true)
    end

    it 'returns 304 if already subscribed' do
      post v3_api("/projects/#{project.id}/issues/#{issue.id}/subscription", user)

1298
      expect(response).to have_gitlab_http_status(304)
1299 1300 1301 1302 1303
    end

    it 'returns 404 if the issue is not found' do
      post v3_api("/projects/#{project.id}/issues/123/subscription", user)

1304
      expect(response).to have_gitlab_http_status(404)
1305 1306 1307 1308 1309
    end

    it 'returns 404 if the issue is confidential' do
      post v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)

1310
      expect(response).to have_gitlab_http_status(404)
1311 1312 1313 1314 1315 1316 1317
    end
  end

  describe 'DELETE :id/issues/:issue_id/subscription' do
    it 'unsubscribes from an issue' do
      delete v3_api("/projects/#{project.id}/issues/#{issue.id}/subscription", user)

1318
      expect(response).to have_gitlab_http_status(200)
1319 1320 1321 1322 1323 1324
      expect(json_response['subscribed']).to eq(false)
    end

    it 'returns 304 if not subscribed' do
      delete v3_api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2)

1325
      expect(response).to have_gitlab_http_status(304)
1326 1327 1328 1329 1330
    end

    it 'returns 404 if the issue is not found' do
      delete v3_api("/projects/#{project.id}/issues/123/subscription", user)

1331
      expect(response).to have_gitlab_http_status(404)
1332 1333 1334 1335 1336
    end

    it 'returns 404 if the issue is confidential' do
      delete v3_api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)

1337
      expect(response).to have_gitlab_http_status(404)
1338 1339 1340 1341 1342 1343
    end
  end

  describe 'time tracking endpoints' do
    let(:issuable) { issue }

1344
    include_examples 'V3 time tracking endpoints', 'issue'
1345 1346
  end
end