To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 2b / 2b0a9f999f6063790f2925cfcbf5bec32cb0108e.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (18 KB)

1
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

    
18
require File.expand_path('../../../test_helper', __FILE__)
19

    
20
class ApiTest::IssuesTest < ActionController::IntegrationTest
21
  fixtures :projects,
22
    :users,
23
    :roles,
24
    :members,
25
    :member_roles,
26
    :issues,
27
    :issue_statuses,
28
    :versions,
29
    :trackers,
30
    :projects_trackers,
31
    :issue_categories,
32
    :enabled_modules,
33
    :enumerations,
34
    :attachments,
35
    :workflows,
36
    :custom_fields,
37
    :custom_values,
38
    :custom_fields_projects,
39
    :custom_fields_trackers,
40
    :time_entries,
41
    :journals,
42
    :journal_details,
43
    :queries,
44
    :attachments
45

    
46
  def setup
47
    Setting.rest_api_enabled = '1'
48
  end
49

    
50
  context "/issues" do
51
    # Use a private project to make sure auth is really working and not just
52
    # only showing public issues.
53
    should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
54

    
55
    should "contain metadata" do
56
      get '/issues.xml'
57

    
58
      assert_tag :tag => 'issues',
59
        :attributes => {
60
          :type => 'array',
61
          :total_count => assigns(:issue_count),
62
          :limit => 25,
63
          :offset => 0
64
        }
65
    end
66

    
67
    context "with offset and limit" do
68
      should "use the params" do
69
        get '/issues.xml?offset=2&limit=3'
70

    
71
        assert_equal 3, assigns(:limit)
72
        assert_equal 2, assigns(:offset)
73
        assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
74
      end
75
    end
76

    
77
    context "with nometa param" do
78
      should "not contain metadata" do
79
        get '/issues.xml?nometa=1'
80

    
81
        assert_tag :tag => 'issues',
82
          :attributes => {
83
            :type => 'array',
84
            :total_count => nil,
85
            :limit => nil,
86
            :offset => nil
87
          }
88
      end
89
    end
90

    
91
    context "with nometa header" do
92
      should "not contain metadata" do
93
        get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
94

    
95
        assert_tag :tag => 'issues',
96
          :attributes => {
97
            :type => 'array',
98
            :total_count => nil,
99
            :limit => nil,
100
            :offset => nil
101
          }
102
      end
103
    end
104

    
105
    context "with relations" do
106
      should "display relations" do
107
        get '/issues.xml?include=relations'
108

    
109
        assert_response :success
110
        assert_equal 'application/xml', @response.content_type
111
        assert_tag 'relations',
112
          :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
113
          :children => {:count => 1},
114
          :child => {
115
            :tag => 'relation',
116
            :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3', :relation_type => 'relates'}
117
          }
118
        assert_tag 'relations',
119
          :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
120
          :children => {:count => 0}
121
      end
122
    end
123

    
124
    context "with invalid query params" do
125
      should "return errors" do
126
        get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
127

    
128
        assert_response :unprocessable_entity
129
        assert_equal 'application/xml', @response.content_type
130
        assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
131
      end
132
    end
133

    
134
    context "with custom field filter" do
135
      should "show only issues with the custom field value" do
136
        get '/issues.xml', { :set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
137

    
138
        expected_ids = Issue.visible.all(
139
            :include => :custom_values,
140
            :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
141

    
142
        assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
143
           ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
144
        end
145
      end
146
    end
147

    
148
    context "with custom field filter (shorthand method)" do
149
      should "show only issues with the custom field value" do
150
        get '/issues.xml', { :cf_1 => 'MySQL' }
151

    
152
        expected_ids = Issue.visible.all(
153
            :include => :custom_values,
154
            :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
155

    
156
        assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
157
          ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
158
        end
159
      end
160
    end
161
  end
162

    
163
  context "/index.json" do
164
    should_allow_api_authentication(:get, "/projects/private-child/issues.json")
165
  end
166

    
167
  context "/index.xml with filter" do
168
    should "show only issues with the status_id" do
169
      get '/issues.xml?status_id=5'
170

    
171
      expected_ids = Issue.visible.all(:conditions => {:status_id => 5}).map(&:id)
172

    
173
      assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
174
         ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
175
      end
176
    end
177
  end
178

    
179
  context "/index.json with filter" do
180
    should "show only issues with the status_id" do
181
      get '/issues.json?status_id=5'
182

    
183
      json = ActiveSupport::JSON.decode(response.body)
184
      status_ids_used = json['issues'].collect {|j| j['status']['id'] }
185
      assert_equal 3, status_ids_used.length
186
      assert status_ids_used.all? {|id| id == 5 }
187
    end
188

    
189
  end
190

    
191
  # Issue 6 is on a private project
192
  context "/issues/6.xml" do
193
    should_allow_api_authentication(:get, "/issues/6.xml")
194
  end
195

    
196
  context "/issues/6.json" do
197
    should_allow_api_authentication(:get, "/issues/6.json")
198
  end
199

    
200
  context "GET /issues/:id" do
201
    context "with journals" do
202
      context ".xml" do
203
        should "display journals" do
204
          get '/issues/1.xml?include=journals'
205

    
206
          assert_tag :tag => 'issue',
207
            :child => {
208
              :tag => 'journals',
209
              :attributes => { :type => 'array' },
210
              :child => {
211
                :tag => 'journal',
212
                :attributes => { :id => '1'},
213
                :child => {
214
                  :tag => 'details',
215
                  :attributes => { :type => 'array' },
216
                  :child => {
217
                    :tag => 'detail',
218
                    :attributes => { :name => 'status_id' },
219
                    :child => {
220
                      :tag => 'old_value',
221
                      :content => '1',
222
                      :sibling => {
223
                        :tag => 'new_value',
224
                        :content => '2'
225
                      }
226
                    }
227
                  }
228
                }
229
              }
230
            }
231
        end
232
      end
233
    end
234

    
235
    context "with custom fields" do
236
      context ".xml" do
237
        should "display custom fields" do
238
          get '/issues/3.xml'
239

    
240
          assert_tag :tag => 'issue',
241
            :child => {
242
              :tag => 'custom_fields',
243
              :attributes => { :type => 'array' },
244
              :child => {
245
                :tag => 'custom_field',
246
                :attributes => { :id => '1'},
247
                :child => {
248
                  :tag => 'value',
249
                  :content => 'MySQL'
250
                }
251
              }
252
            }
253

    
254
          assert_nothing_raised do
255
            Hash.from_xml(response.body).to_xml
256
          end
257
        end
258
      end
259
    end
260

    
261
    context "with attachments" do
262
      context ".xml" do
263
        should "display attachments" do
264
          get '/issues/3.xml?include=attachments'
265

    
266
          assert_tag :tag => 'issue',
267
            :child => {
268
              :tag => 'attachments',
269
              :children => {:count => 5},
270
              :child => {
271
                :tag => 'attachment',
272
                :child => {
273
                  :tag => 'filename',
274
                  :content => 'source.rb',
275
                  :sibling => {
276
                    :tag => 'content_url',
277
                    :content => 'http://www.example.com/attachments/download/4/source.rb'
278
                  }
279
                }
280
              }
281
            }
282
        end
283
      end
284
    end
285

    
286
    context "with subtasks" do
287
      setup do
288
        @c1 = Issue.generate!(:status_id => 1, :subject => "child c1", :tracker_id => 1, :project_id => 1, :parent_issue_id => 1)
289
        @c2 = Issue.generate!(:status_id => 1, :subject => "child c2", :tracker_id => 1, :project_id => 1, :parent_issue_id => 1)
290
        @c3 = Issue.generate!(:status_id => 1, :subject => "child c3", :tracker_id => 1, :project_id => 1, :parent_issue_id => @c1.id)
291
      end
292

    
293
      context ".xml" do
294
        should "display children" do
295
          get '/issues/1.xml?include=children'
296

    
297
          assert_tag :tag => 'issue',
298
            :child => {
299
              :tag => 'children',
300
              :children => {:count => 2},
301
              :child => {
302
                :tag => 'issue',
303
                :attributes => {:id => @c1.id.to_s},
304
                :child => {
305
                  :tag => 'subject',
306
                  :content => 'child c1',
307
                  :sibling => {
308
                    :tag => 'children',
309
                    :children => {:count => 1},
310
                    :child => {
311
                      :tag => 'issue',
312
                      :attributes => {:id => @c3.id.to_s}
313
                    }
314
                  }
315
                }
316
              }
317
            }
318
        end
319

    
320
        context ".json" do
321
          should "display children" do
322
            get '/issues/1.json?include=children'
323

    
324
            json = ActiveSupport::JSON.decode(response.body)
325
            assert_equal([
326
              {
327
                'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
328
                'children' => [{ 'id' => @c3.id, 'subject' => 'child c3', 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
329
              },
330
              { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
331
              ],
332
              json['issue']['children'])
333
          end
334
        end
335
      end
336
    end
337
  end
338

    
339
  context "POST /issues.xml" do
340
    should_allow_api_authentication(:post,
341
                                    '/issues.xml',
342
                                    {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
343
                                    {:success_code => :created})
344

    
345
    should "create an issue with the attributes" do
346
      assert_difference('Issue.count') do
347
        post '/issues.xml', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, :authorization => credentials('jsmith')
348
      end
349

    
350
      issue = Issue.first(:order => 'id DESC')
351
      assert_equal 1, issue.project_id
352
      assert_equal 2, issue.tracker_id
353
      assert_equal 3, issue.status_id
354
      assert_equal 'API test', issue.subject
355

    
356
      assert_response :created
357
      assert_equal 'application/xml', @response.content_type
358
      assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
359
    end
360
  end
361

    
362
  context "POST /issues.xml with failure" do
363
    should "have an errors tag" do
364
      assert_no_difference('Issue.count') do
365
        post '/issues.xml', {:issue => {:project_id => 1}}, :authorization => credentials('jsmith')
366
      end
367

    
368
      assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
369
    end
370
  end
371

    
372
  context "POST /issues.json" do
373
    should_allow_api_authentication(:post,
374
                                    '/issues.json',
375
                                    {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
376
                                    {:success_code => :created})
377

    
378
    should "create an issue with the attributes" do
379
      assert_difference('Issue.count') do
380
        post '/issues.json', {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, :authorization => credentials('jsmith')
381
      end
382

    
383
      issue = Issue.first(:order => 'id DESC')
384
      assert_equal 1, issue.project_id
385
      assert_equal 2, issue.tracker_id
386
      assert_equal 3, issue.status_id
387
      assert_equal 'API test', issue.subject
388
    end
389

    
390
  end
391

    
392
  context "POST /issues.json with failure" do
393
    should "have an errors element" do
394
      assert_no_difference('Issue.count') do
395
        post '/issues.json', {:issue => {:project_id => 1}}, :authorization => credentials('jsmith')
396
      end
397

    
398
      json = ActiveSupport::JSON.decode(response.body)
399
      assert json['errors'].include?(['subject', "can't be blank"])
400
    end
401
  end
402

    
403
  # Issue 6 is on a private project
404
  context "PUT /issues/6.xml" do
405
    setup do
406
      @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
407
      @headers = { :authorization => credentials('jsmith') }
408
    end
409

    
410
    should_allow_api_authentication(:put,
411
                                    '/issues/6.xml',
412
                                    {:issue => {:subject => 'API update', :notes => 'A new note'}},
413
                                    {:success_code => :ok})
414

    
415
    should "not create a new issue" do
416
      assert_no_difference('Issue.count') do
417
        put '/issues/6.xml', @parameters, @headers
418
      end
419
    end
420

    
421
    should "create a new journal" do
422
      assert_difference('Journal.count') do
423
        put '/issues/6.xml', @parameters, @headers
424
      end
425
    end
426

    
427
    should "add the note to the journal" do
428
      put '/issues/6.xml', @parameters, @headers
429

    
430
      journal = Journal.last
431
      assert_equal "A new note", journal.notes
432
    end
433

    
434
    should "update the issue" do
435
      put '/issues/6.xml', @parameters, @headers
436

    
437
      issue = Issue.find(6)
438
      assert_equal "API update", issue.subject
439
    end
440

    
441
  end
442

    
443
  context "PUT /issues/3.xml with custom fields" do
444
    setup do
445
      @parameters = {:issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' }, {'id' => '2', 'value' => '150'}]}}
446
      @headers = { :authorization => credentials('jsmith') }
447
    end
448

    
449
    should "update custom fields" do
450
      assert_no_difference('Issue.count') do
451
        put '/issues/3.xml', @parameters, @headers
452
      end
453

    
454
      issue = Issue.find(3)
455
      assert_equal '150', issue.custom_value_for(2).value
456
      assert_equal 'PostgreSQL', issue.custom_value_for(1).value
457
    end
458
  end
459

    
460
  context "PUT /issues/6.xml with failed update" do
461
    setup do
462
      @parameters = {:issue => {:subject => ''}}
463
      @headers = { :authorization => credentials('jsmith') }
464
    end
465

    
466
    should "not create a new issue" do
467
      assert_no_difference('Issue.count') do
468
        put '/issues/6.xml', @parameters, @headers
469
      end
470
    end
471

    
472
    should "not create a new journal" do
473
      assert_no_difference('Journal.count') do
474
        put '/issues/6.xml', @parameters, @headers
475
      end
476
    end
477

    
478
    should "have an errors tag" do
479
      put '/issues/6.xml', @parameters, @headers
480

    
481
      assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
482
    end
483
  end
484

    
485
  context "PUT /issues/6.json" do
486
    setup do
487
      @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
488
      @headers = { :authorization => credentials('jsmith') }
489
    end
490

    
491
    should_allow_api_authentication(:put,
492
                                    '/issues/6.json',
493
                                    {:issue => {:subject => 'API update', :notes => 'A new note'}},
494
                                    {:success_code => :ok})
495

    
496
    should "not create a new issue" do
497
      assert_no_difference('Issue.count') do
498
        put '/issues/6.json', @parameters, @headers
499
      end
500
    end
501

    
502
    should "create a new journal" do
503
      assert_difference('Journal.count') do
504
        put '/issues/6.json', @parameters, @headers
505
      end
506
    end
507

    
508
    should "add the note to the journal" do
509
      put '/issues/6.json', @parameters, @headers
510

    
511
      journal = Journal.last
512
      assert_equal "A new note", journal.notes
513
    end
514

    
515
    should "update the issue" do
516
      put '/issues/6.json', @parameters, @headers
517

    
518
      issue = Issue.find(6)
519
      assert_equal "API update", issue.subject
520
    end
521

    
522
  end
523

    
524
  context "PUT /issues/6.json with failed update" do
525
    setup do
526
      @parameters = {:issue => {:subject => ''}}
527
      @headers = { :authorization => credentials('jsmith') }
528
    end
529

    
530
    should "not create a new issue" do
531
      assert_no_difference('Issue.count') do
532
        put '/issues/6.json', @parameters, @headers
533
      end
534
    end
535

    
536
    should "not create a new journal" do
537
      assert_no_difference('Journal.count') do
538
        put '/issues/6.json', @parameters, @headers
539
      end
540
    end
541

    
542
    should "have an errors attribute" do
543
      put '/issues/6.json', @parameters, @headers
544

    
545
      json = ActiveSupport::JSON.decode(response.body)
546
      assert json['errors'].include?(['subject', "can't be blank"])
547
    end
548
  end
549

    
550
  context "DELETE /issues/1.xml" do
551
    should_allow_api_authentication(:delete,
552
                                    '/issues/6.xml',
553
                                    {},
554
                                    {:success_code => :ok})
555

    
556
    should "delete the issue" do
557
      assert_difference('Issue.count',-1) do
558
        delete '/issues/6.xml', {}, :authorization => credentials('jsmith')
559
      end
560

    
561
      assert_nil Issue.find_by_id(6)
562
    end
563
  end
564

    
565
  context "DELETE /issues/1.json" do
566
    should_allow_api_authentication(:delete,
567
                                    '/issues/6.json',
568
                                    {},
569
                                    {:success_code => :ok})
570

    
571
    should "delete the issue" do
572
      assert_difference('Issue.count',-1) do
573
        delete '/issues/6.json', {}, :authorization => credentials('jsmith')
574
      end
575

    
576
      assert_nil Issue.find_by_id(6)
577
    end
578
  end
579

    
580
  def credentials(user, password=nil)
581
    ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
582
  end
583
end