##// END OF EJS Templates
Merged r14794 (#21136)....
Jean-Philippe Lang -
r14461:8d8f612fa368
parent child
Show More
@@ -1,75 +1,75
1 1 api.issue do
2 2 api.id @issue.id
3 3 api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
4 4 api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
5 5 api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
6 6 api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
7 7 api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
8 8 api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
9 9 api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
10 10 api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
11 11 api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
12 12
13 13 api.subject @issue.subject
14 14 api.description @issue.description
15 15 api.start_date @issue.start_date
16 16 api.due_date @issue.due_date
17 17 api.done_ratio @issue.done_ratio
18 18 api.is_private @issue.is_private
19 19 api.estimated_hours @issue.estimated_hours
20 20 api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
21 21
22 22 render_api_custom_values @issue.visible_custom_field_values, api
23 23
24 24 api.created_on @issue.created_on
25 25 api.updated_on @issue.updated_on
26 26 api.closed_on @issue.closed_on
27 27
28 28 render_api_issue_children(@issue, api) if include_in_api_response?('children')
29 29
30 30 api.array :attachments do
31 31 @issue.attachments.each do |attachment|
32 32 render_api_attachment(attachment, api)
33 33 end
34 34 end if include_in_api_response?('attachments')
35 35
36 36 api.array :relations do
37 37 @relations.each do |relation|
38 38 api.relation(:id => relation.id, :issue_id => relation.issue_from_id, :issue_to_id => relation.issue_to_id, :relation_type => relation.relation_type, :delay => relation.delay)
39 39 end
40 40 end if include_in_api_response?('relations') && @relations.present?
41 41
42 42 api.array :changesets do
43 @issue.changesets.each do |changeset|
43 @changesets.each do |changeset|
44 44 api.changeset :revision => changeset.revision do
45 45 api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
46 46 api.comments changeset.comments
47 47 api.committed_on changeset.committed_on
48 48 end
49 49 end
50 end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
50 end if include_in_api_response?('changesets')
51 51
52 52 api.array :journals do
53 53 @journals.each do |journal|
54 54 api.journal :id => journal.id do
55 55 api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
56 56 api.notes journal.notes
57 57 api.created_on journal.created_on
58 58 api.array :details do
59 59 journal.visible_details.each do |detail|
60 60 api.detail :property => detail.property, :name => detail.prop_key do
61 61 api.old_value detail.old_value
62 62 api.new_value detail.value
63 63 end
64 64 end
65 65 end
66 66 end
67 67 end
68 68 end if include_in_api_response?('journals')
69 69
70 70 api.array :watchers do
71 71 @issue.watcher_users.each do |user|
72 72 api.user :id => user.id, :name => user.name
73 73 end
74 74 end if include_in_api_response?('watchers') && User.current.allowed_to?(:view_issue_watchers, @issue.project)
75 75 end
@@ -1,888 +1,902
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
21 21 fixtures :projects,
22 22 :users,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :issues,
27 27 :issue_statuses,
28 28 :issue_relations,
29 29 :versions,
30 30 :trackers,
31 31 :projects_trackers,
32 32 :issue_categories,
33 33 :enabled_modules,
34 34 :enumerations,
35 35 :attachments,
36 36 :workflows,
37 37 :custom_fields,
38 38 :custom_values,
39 39 :custom_fields_projects,
40 40 :custom_fields_trackers,
41 41 :time_entries,
42 42 :journals,
43 43 :journal_details,
44 44 :queries,
45 45 :attachments
46 46
47 47 def setup
48 48 Setting.rest_api_enabled = '1'
49 49 end
50 50
51 51 context "/issues" do
52 52 # Use a private project to make sure auth is really working and not just
53 53 # only showing public issues.
54 54 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
55 55
56 56 should "contain metadata" do
57 57 get '/issues.xml'
58 58
59 59 assert_tag :tag => 'issues',
60 60 :attributes => {
61 61 :type => 'array',
62 62 :total_count => assigns(:issue_count),
63 63 :limit => 25,
64 64 :offset => 0
65 65 }
66 66 end
67 67
68 68 context "with offset and limit" do
69 69 should "use the params" do
70 70 get '/issues.xml?offset=2&limit=3'
71 71
72 72 assert_equal 3, assigns(:limit)
73 73 assert_equal 2, assigns(:offset)
74 74 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
75 75 end
76 76 end
77 77
78 78 context "with nometa param" do
79 79 should "not contain metadata" do
80 80 get '/issues.xml?nometa=1'
81 81
82 82 assert_tag :tag => 'issues',
83 83 :attributes => {
84 84 :type => 'array',
85 85 :total_count => nil,
86 86 :limit => nil,
87 87 :offset => nil
88 88 }
89 89 end
90 90 end
91 91
92 92 context "with nometa header" do
93 93 should "not contain metadata" do
94 94 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
95 95
96 96 assert_tag :tag => 'issues',
97 97 :attributes => {
98 98 :type => 'array',
99 99 :total_count => nil,
100 100 :limit => nil,
101 101 :offset => nil
102 102 }
103 103 end
104 104 end
105 105
106 106 context "with relations" do
107 107 should "display relations" do
108 108 get '/issues.xml?include=relations'
109 109
110 110 assert_response :success
111 111 assert_equal 'application/xml', @response.content_type
112 112 assert_tag 'relations',
113 113 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
114 114 :children => {:count => 1},
115 115 :child => {
116 116 :tag => 'relation',
117 117 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3',
118 118 :relation_type => 'relates'}
119 119 }
120 120 assert_tag 'relations',
121 121 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
122 122 :children => {:count => 0}
123 123 end
124 124 end
125 125
126 126 context "with invalid query params" do
127 127 should "return errors" do
128 128 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
129 129
130 130 assert_response :unprocessable_entity
131 131 assert_equal 'application/xml', @response.content_type
132 132 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
133 133 end
134 134 end
135 135
136 136 context "with custom field filter" do
137 137 should "show only issues with the custom field value" do
138 138 get '/issues.xml',
139 139 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='},
140 140 :v => {:cf_1 => ['MySQL']}}
141 141 expected_ids = Issue.visible.
142 142 joins(:custom_values).
143 143 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
144 144 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
145 145 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
146 146 end
147 147 end
148 148 end
149 149
150 150 context "with custom field filter (shorthand method)" do
151 151 should "show only issues with the custom field value" do
152 152 get '/issues.xml', { :cf_1 => 'MySQL' }
153 153
154 154 expected_ids = Issue.visible.
155 155 joins(:custom_values).
156 156 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
157 157
158 158 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
159 159 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
160 160 end
161 161 end
162 162 end
163 163 end
164 164
165 165 test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
166 166 get '/issues/1.xml?include=journals'
167 167
168 168 iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
169 169 assert_select 'issue>created_on', :text => iso_date
170 170 assert_select 'issue>updated_on', :text => iso_date
171 171 assert_select 'issue journal>created_on', :text => iso_date
172 172 end
173 173
174 174 def test_index_should_include_issue_attributes
175 175 get '/issues.xml'
176 176 assert_select 'issues>issue>is_private', :text => 'false'
177 177 end
178 178
179 179 def test_index_should_allow_timestamp_filtering
180 180 Issue.delete_all
181 181 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
182 182 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
183 183
184 184 get '/issues.xml',
185 185 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
186 186 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
187 187 assert_select 'issues>issue', :count => 1
188 188 assert_select 'issues>issue>subject', :text => '1'
189 189
190 190 get '/issues.xml',
191 191 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
192 192 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
193 193 assert_select 'issues>issue', :count => 1
194 194 assert_select 'issues>issue>subject', :text => '2'
195 195
196 196 get '/issues.xml',
197 197 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
198 198 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
199 199 assert_select 'issues>issue', :count => 2
200 200 end
201 201
202 202 context "/index.json" do
203 203 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
204 204 end
205 205
206 206 context "/index.xml with filter" do
207 207 should "show only issues with the status_id" do
208 208 get '/issues.xml?status_id=5'
209 209
210 210 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
211 211
212 212 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
213 213 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
214 214 end
215 215 end
216 216 end
217 217
218 218 context "/index.json with filter" do
219 219 should "show only issues with the status_id" do
220 220 get '/issues.json?status_id=5'
221 221
222 222 json = ActiveSupport::JSON.decode(response.body)
223 223 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
224 224 assert_equal 3, status_ids_used.length
225 225 assert status_ids_used.all? {|id| id == 5 }
226 226 end
227 227
228 228 end
229 229
230 230 # Issue 6 is on a private project
231 231 context "/issues/6.xml" do
232 232 should_allow_api_authentication(:get, "/issues/6.xml")
233 233 end
234 234
235 235 context "/issues/6.json" do
236 236 should_allow_api_authentication(:get, "/issues/6.json")
237 237 end
238 238
239 239 context "GET /issues/:id" do
240 240 context "with journals" do
241 241 context ".xml" do
242 242 should "display journals" do
243 243 get '/issues/1.xml?include=journals'
244 244
245 245 assert_tag :tag => 'issue',
246 246 :child => {
247 247 :tag => 'journals',
248 248 :attributes => { :type => 'array' },
249 249 :child => {
250 250 :tag => 'journal',
251 251 :attributes => { :id => '1'},
252 252 :child => {
253 253 :tag => 'details',
254 254 :attributes => { :type => 'array' },
255 255 :child => {
256 256 :tag => 'detail',
257 257 :attributes => { :name => 'status_id' },
258 258 :child => {
259 259 :tag => 'old_value',
260 260 :content => '1',
261 261 :sibling => {
262 262 :tag => 'new_value',
263 263 :content => '2'
264 264 }
265 265 }
266 266 }
267 267 }
268 268 }
269 269 }
270 270 end
271 271 end
272 272 end
273 273
274 274 context "with custom fields" do
275 275 context ".xml" do
276 276 should "display custom fields" do
277 277 get '/issues/3.xml'
278 278
279 279 assert_tag :tag => 'issue',
280 280 :child => {
281 281 :tag => 'custom_fields',
282 282 :attributes => { :type => 'array' },
283 283 :child => {
284 284 :tag => 'custom_field',
285 285 :attributes => { :id => '1'},
286 286 :child => {
287 287 :tag => 'value',
288 288 :content => 'MySQL'
289 289 }
290 290 }
291 291 }
292 292
293 293 assert_nothing_raised do
294 294 Hash.from_xml(response.body).to_xml
295 295 end
296 296 end
297 297 end
298 298 end
299 299
300 300 context "with multi custom fields" do
301 301 setup do
302 302 field = CustomField.find(1)
303 303 field.update_attribute :multiple, true
304 304 issue = Issue.find(3)
305 305 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
306 306 issue.save!
307 307 end
308 308
309 309 context ".xml" do
310 310 should "display custom fields" do
311 311 get '/issues/3.xml'
312 312 assert_response :success
313 313 assert_tag :tag => 'issue',
314 314 :child => {
315 315 :tag => 'custom_fields',
316 316 :attributes => { :type => 'array' },
317 317 :child => {
318 318 :tag => 'custom_field',
319 319 :attributes => { :id => '1'},
320 320 :child => {
321 321 :tag => 'value',
322 322 :attributes => { :type => 'array' },
323 323 :children => { :count => 2 }
324 324 }
325 325 }
326 326 }
327 327
328 328 xml = Hash.from_xml(response.body)
329 329 custom_fields = xml['issue']['custom_fields']
330 330 assert_kind_of Array, custom_fields
331 331 field = custom_fields.detect {|f| f['id'] == '1'}
332 332 assert_kind_of Hash, field
333 333 assert_equal ['MySQL', 'Oracle'], field['value'].sort
334 334 end
335 335 end
336 336
337 337 context ".json" do
338 338 should "display custom fields" do
339 339 get '/issues/3.json'
340 340 assert_response :success
341 341 json = ActiveSupport::JSON.decode(response.body)
342 342 custom_fields = json['issue']['custom_fields']
343 343 assert_kind_of Array, custom_fields
344 344 field = custom_fields.detect {|f| f['id'] == 1}
345 345 assert_kind_of Hash, field
346 346 assert_equal ['MySQL', 'Oracle'], field['value'].sort
347 347 end
348 348 end
349 349 end
350 350
351 351 context "with empty value for multi custom field" do
352 352 setup do
353 353 field = CustomField.find(1)
354 354 field.update_attribute :multiple, true
355 355 issue = Issue.find(3)
356 356 issue.custom_field_values = {1 => ['']}
357 357 issue.save!
358 358 end
359 359
360 360 context ".xml" do
361 361 should "display custom fields" do
362 362 get '/issues/3.xml'
363 363 assert_response :success
364 364 assert_tag :tag => 'issue',
365 365 :child => {
366 366 :tag => 'custom_fields',
367 367 :attributes => { :type => 'array' },
368 368 :child => {
369 369 :tag => 'custom_field',
370 370 :attributes => { :id => '1'},
371 371 :child => {
372 372 :tag => 'value',
373 373 :attributes => { :type => 'array' },
374 374 :children => { :count => 0 }
375 375 }
376 376 }
377 377 }
378 378
379 379 xml = Hash.from_xml(response.body)
380 380 custom_fields = xml['issue']['custom_fields']
381 381 assert_kind_of Array, custom_fields
382 382 field = custom_fields.detect {|f| f['id'] == '1'}
383 383 assert_kind_of Hash, field
384 384 assert_equal [], field['value']
385 385 end
386 386 end
387 387
388 388 context ".json" do
389 389 should "display custom fields" do
390 390 get '/issues/3.json'
391 391 assert_response :success
392 392 json = ActiveSupport::JSON.decode(response.body)
393 393 custom_fields = json['issue']['custom_fields']
394 394 assert_kind_of Array, custom_fields
395 395 field = custom_fields.detect {|f| f['id'] == 1}
396 396 assert_kind_of Hash, field
397 397 assert_equal [], field['value'].sort
398 398 end
399 399 end
400 400 end
401 401
402 402 context "with attachments" do
403 403 context ".xml" do
404 404 should "display attachments" do
405 405 get '/issues/3.xml?include=attachments'
406 406
407 407 assert_tag :tag => 'issue',
408 408 :child => {
409 409 :tag => 'attachments',
410 410 :children => {:count => 5},
411 411 :child => {
412 412 :tag => 'attachment',
413 413 :child => {
414 414 :tag => 'filename',
415 415 :content => 'source.rb',
416 416 :sibling => {
417 417 :tag => 'content_url',
418 418 :content => 'http://www.example.com/attachments/download/4/source.rb'
419 419 }
420 420 }
421 421 }
422 422 }
423 423 end
424 424 end
425 425 end
426 426
427 427 context "with subtasks" do
428 428 setup do
429 429 @c1 = Issue.create!(
430 430 :status_id => 1, :subject => "child c1",
431 431 :tracker_id => 1, :project_id => 1, :author_id => 1,
432 432 :parent_issue_id => 1
433 433 )
434 434 @c2 = Issue.create!(
435 435 :status_id => 1, :subject => "child c2",
436 436 :tracker_id => 1, :project_id => 1, :author_id => 1,
437 437 :parent_issue_id => 1
438 438 )
439 439 @c3 = Issue.create!(
440 440 :status_id => 1, :subject => "child c3",
441 441 :tracker_id => 1, :project_id => 1, :author_id => 1,
442 442 :parent_issue_id => @c1.id
443 443 )
444 444 end
445 445
446 446 context ".xml" do
447 447 should "display children" do
448 448 get '/issues/1.xml?include=children'
449 449
450 450 assert_tag :tag => 'issue',
451 451 :child => {
452 452 :tag => 'children',
453 453 :children => {:count => 2},
454 454 :child => {
455 455 :tag => 'issue',
456 456 :attributes => {:id => @c1.id.to_s},
457 457 :child => {
458 458 :tag => 'subject',
459 459 :content => 'child c1',
460 460 :sibling => {
461 461 :tag => 'children',
462 462 :children => {:count => 1},
463 463 :child => {
464 464 :tag => 'issue',
465 465 :attributes => {:id => @c3.id.to_s}
466 466 }
467 467 }
468 468 }
469 469 }
470 470 }
471 471 end
472 472
473 473 context ".json" do
474 474 should "display children" do
475 475 get '/issues/1.json?include=children'
476 476
477 477 json = ActiveSupport::JSON.decode(response.body)
478 478 assert_equal([
479 479 {
480 480 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
481 481 'children' => [{'id' => @c3.id, 'subject' => 'child c3',
482 482 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
483 483 },
484 484 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
485 485 ],
486 486 json['issue']['children'])
487 487 end
488 488 end
489 489 end
490 490 end
491 491 end
492 492
493 493 def test_show_should_include_issue_attributes
494 494 get '/issues/1.xml'
495 495 assert_select 'issue>is_private', :text => 'false'
496 496 end
497 497
498 498 test "GET /issues/:id.xml?include=watchers should include watchers" do
499 499 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
500 500
501 501 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
502 502
503 503 assert_response :ok
504 504 assert_equal 'application/xml', response.content_type
505 505 assert_select 'issue' do
506 506 assert_select 'watchers', Issue.find(1).watchers.count
507 507 assert_select 'watchers' do
508 508 assert_select 'user[id=3]'
509 509 end
510 510 end
511 511 end
512 512
513 test "GET /issues/:id.xml should not disclose associated changesets from projects the user has no access to" do
514 project = Project.generate!(:is_public => false)
515 repository = Repository::Subversion.create!(:project => project, :url => "svn://localhost")
516 Issue.find(1).changesets << Changeset.generate!(:repository => repository)
517 assert Issue.find(1).changesets.any?
518
519 get '/issues/1.xml?include=changesets', {}, credentials('jsmith')
520
521 # the user jsmith has no permission to view the associated changeset
522 assert_select 'issue changesets[type=array]' do
523 assert_select 'changeset', 0
524 end
525 end
526
513 527 context "POST /issues.xml" do
514 528 should_allow_api_authentication(
515 529 :post,
516 530 '/issues.xml',
517 531 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
518 532 {:success_code => :created}
519 533 )
520 534 should "create an issue with the attributes" do
521 535 assert_difference('Issue.count') do
522 536 post '/issues.xml',
523 537 {:issue => {:project_id => 1, :subject => 'API test',
524 538 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
525 539 end
526 540 issue = Issue.order('id DESC').first
527 541 assert_equal 1, issue.project_id
528 542 assert_equal 2, issue.tracker_id
529 543 assert_equal 3, issue.status_id
530 544 assert_equal 'API test', issue.subject
531 545
532 546 assert_response :created
533 547 assert_equal 'application/xml', @response.content_type
534 548 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
535 549 end
536 550 end
537 551
538 552 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
539 553 assert_difference('Issue.count') do
540 554 post '/issues.xml',
541 555 {:issue => {:project_id => 1, :subject => 'Watchers',
542 556 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
543 557 assert_response :created
544 558 end
545 559 issue = Issue.order('id desc').first
546 560 assert_equal 2, issue.watchers.size
547 561 assert_equal [1, 3], issue.watcher_user_ids.sort
548 562 end
549 563
550 564 context "POST /issues.xml with failure" do
551 565 should "have an errors tag" do
552 566 assert_no_difference('Issue.count') do
553 567 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
554 568 end
555 569
556 570 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
557 571 end
558 572 end
559 573
560 574 context "POST /issues.json" do
561 575 should_allow_api_authentication(:post,
562 576 '/issues.json',
563 577 {:issue => {:project_id => 1, :subject => 'API test',
564 578 :tracker_id => 2, :status_id => 3}},
565 579 {:success_code => :created})
566 580
567 581 should "create an issue with the attributes" do
568 582 assert_difference('Issue.count') do
569 583 post '/issues.json',
570 584 {:issue => {:project_id => 1, :subject => 'API test',
571 585 :tracker_id => 2, :status_id => 3}},
572 586 credentials('jsmith')
573 587 end
574 588
575 589 issue = Issue.order('id DESC').first
576 590 assert_equal 1, issue.project_id
577 591 assert_equal 2, issue.tracker_id
578 592 assert_equal 3, issue.status_id
579 593 assert_equal 'API test', issue.subject
580 594 end
581 595
582 596 end
583 597
584 598 context "POST /issues.json with failure" do
585 599 should "have an errors element" do
586 600 assert_no_difference('Issue.count') do
587 601 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
588 602 end
589 603
590 604 json = ActiveSupport::JSON.decode(response.body)
591 605 assert json['errors'].include?("Subject can't be blank")
592 606 end
593 607 end
594 608
595 609 # Issue 6 is on a private project
596 610 context "PUT /issues/6.xml" do
597 611 setup do
598 612 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
599 613 end
600 614
601 615 should_allow_api_authentication(:put,
602 616 '/issues/6.xml',
603 617 {:issue => {:subject => 'API update', :notes => 'A new note'}},
604 618 {:success_code => :ok})
605 619
606 620 should "not create a new issue" do
607 621 assert_no_difference('Issue.count') do
608 622 put '/issues/6.xml', @parameters, credentials('jsmith')
609 623 end
610 624 end
611 625
612 626 should "create a new journal" do
613 627 assert_difference('Journal.count') do
614 628 put '/issues/6.xml', @parameters, credentials('jsmith')
615 629 end
616 630 end
617 631
618 632 should "add the note to the journal" do
619 633 put '/issues/6.xml', @parameters, credentials('jsmith')
620 634
621 635 journal = Journal.last
622 636 assert_equal "A new note", journal.notes
623 637 end
624 638
625 639 should "update the issue" do
626 640 put '/issues/6.xml', @parameters, credentials('jsmith')
627 641
628 642 issue = Issue.find(6)
629 643 assert_equal "API update", issue.subject
630 644 end
631 645
632 646 end
633 647
634 648 context "PUT /issues/3.xml with custom fields" do
635 649 setup do
636 650 @parameters = {
637 651 :issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' },
638 652 {'id' => '2', 'value' => '150'}]}
639 653 }
640 654 end
641 655
642 656 should "update custom fields" do
643 657 assert_no_difference('Issue.count') do
644 658 put '/issues/3.xml', @parameters, credentials('jsmith')
645 659 end
646 660
647 661 issue = Issue.find(3)
648 662 assert_equal '150', issue.custom_value_for(2).value
649 663 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
650 664 end
651 665 end
652 666
653 667 context "PUT /issues/3.xml with multi custom fields" do
654 668 setup do
655 669 field = CustomField.find(1)
656 670 field.update_attribute :multiple, true
657 671 @parameters = {
658 672 :issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
659 673 {'id' => '2', 'value' => '150'}]}
660 674 }
661 675 end
662 676
663 677 should "update custom fields" do
664 678 assert_no_difference('Issue.count') do
665 679 put '/issues/3.xml', @parameters, credentials('jsmith')
666 680 end
667 681
668 682 issue = Issue.find(3)
669 683 assert_equal '150', issue.custom_value_for(2).value
670 684 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
671 685 end
672 686 end
673 687
674 688 context "PUT /issues/3.xml with project change" do
675 689 setup do
676 690 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
677 691 end
678 692
679 693 should "update project" do
680 694 assert_no_difference('Issue.count') do
681 695 put '/issues/3.xml', @parameters, credentials('jsmith')
682 696 end
683 697
684 698 issue = Issue.find(3)
685 699 assert_equal 2, issue.project_id
686 700 assert_equal 'Project changed', issue.subject
687 701 end
688 702 end
689 703
690 704 context "PUT /issues/6.xml with failed update" do
691 705 setup do
692 706 @parameters = {:issue => {:subject => ''}}
693 707 end
694 708
695 709 should "not create a new issue" do
696 710 assert_no_difference('Issue.count') do
697 711 put '/issues/6.xml', @parameters, credentials('jsmith')
698 712 end
699 713 end
700 714
701 715 should "not create a new journal" do
702 716 assert_no_difference('Journal.count') do
703 717 put '/issues/6.xml', @parameters, credentials('jsmith')
704 718 end
705 719 end
706 720
707 721 should "have an errors tag" do
708 722 put '/issues/6.xml', @parameters, credentials('jsmith')
709 723
710 724 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
711 725 end
712 726 end
713 727
714 728 context "PUT /issues/6.json" do
715 729 setup do
716 730 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
717 731 end
718 732
719 733 should_allow_api_authentication(:put,
720 734 '/issues/6.json',
721 735 {:issue => {:subject => 'API update', :notes => 'A new note'}},
722 736 {:success_code => :ok})
723 737
724 738 should "update the issue" do
725 739 assert_no_difference('Issue.count') do
726 740 assert_difference('Journal.count') do
727 741 put '/issues/6.json', @parameters, credentials('jsmith')
728 742
729 743 assert_response :ok
730 744 assert_equal '', response.body
731 745 end
732 746 end
733 747
734 748 issue = Issue.find(6)
735 749 assert_equal "API update", issue.subject
736 750 journal = Journal.last
737 751 assert_equal "A new note", journal.notes
738 752 end
739 753 end
740 754
741 755 context "PUT /issues/6.json with failed update" do
742 756 should "return errors" do
743 757 assert_no_difference('Issue.count') do
744 758 assert_no_difference('Journal.count') do
745 759 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
746 760
747 761 assert_response :unprocessable_entity
748 762 end
749 763 end
750 764
751 765 json = ActiveSupport::JSON.decode(response.body)
752 766 assert json['errors'].include?("Subject can't be blank")
753 767 end
754 768 end
755 769
756 770 context "DELETE /issues/1.xml" do
757 771 should_allow_api_authentication(:delete,
758 772 '/issues/6.xml',
759 773 {},
760 774 {:success_code => :ok})
761 775
762 776 should "delete the issue" do
763 777 assert_difference('Issue.count', -1) do
764 778 delete '/issues/6.xml', {}, credentials('jsmith')
765 779
766 780 assert_response :ok
767 781 assert_equal '', response.body
768 782 end
769 783
770 784 assert_nil Issue.find_by_id(6)
771 785 end
772 786 end
773 787
774 788 context "DELETE /issues/1.json" do
775 789 should_allow_api_authentication(:delete,
776 790 '/issues/6.json',
777 791 {},
778 792 {:success_code => :ok})
779 793
780 794 should "delete the issue" do
781 795 assert_difference('Issue.count', -1) do
782 796 delete '/issues/6.json', {}, credentials('jsmith')
783 797
784 798 assert_response :ok
785 799 assert_equal '', response.body
786 800 end
787 801
788 802 assert_nil Issue.find_by_id(6)
789 803 end
790 804 end
791 805
792 806 test "POST /issues/:id/watchers.xml should add watcher" do
793 807 assert_difference 'Watcher.count' do
794 808 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
795 809
796 810 assert_response :ok
797 811 assert_equal '', response.body
798 812 end
799 813 watcher = Watcher.order('id desc').first
800 814 assert_equal Issue.find(1), watcher.watchable
801 815 assert_equal User.find(3), watcher.user
802 816 end
803 817
804 818 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
805 819 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
806 820
807 821 assert_difference 'Watcher.count', -1 do
808 822 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
809 823
810 824 assert_response :ok
811 825 assert_equal '', response.body
812 826 end
813 827 assert_equal false, Issue.find(1).watched_by?(User.find(3))
814 828 end
815 829
816 830 def test_create_issue_with_uploaded_file
817 831 set_tmp_attachments_directory
818 832 # upload the file
819 833 assert_difference 'Attachment.count' do
820 834 post '/uploads.xml', 'test_create_with_upload',
821 835 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
822 836 assert_response :created
823 837 end
824 838 xml = Hash.from_xml(response.body)
825 839 token = xml['upload']['token']
826 840 attachment = Attachment.order('id DESC').first
827 841
828 842 # create the issue with the upload's token
829 843 assert_difference 'Issue.count' do
830 844 post '/issues.xml',
831 845 {:issue => {:project_id => 1, :subject => 'Uploaded file',
832 846 :uploads => [{:token => token, :filename => 'test.txt',
833 847 :content_type => 'text/plain'}]}},
834 848 credentials('jsmith')
835 849 assert_response :created
836 850 end
837 851 issue = Issue.order('id DESC').first
838 852 assert_equal 1, issue.attachments.count
839 853 assert_equal attachment, issue.attachments.first
840 854
841 855 attachment.reload
842 856 assert_equal 'test.txt', attachment.filename
843 857 assert_equal 'text/plain', attachment.content_type
844 858 assert_equal 'test_create_with_upload'.size, attachment.filesize
845 859 assert_equal 2, attachment.author_id
846 860
847 861 # get the issue with its attachments
848 862 get "/issues/#{issue.id}.xml", :include => 'attachments'
849 863 assert_response :success
850 864 xml = Hash.from_xml(response.body)
851 865 attachments = xml['issue']['attachments']
852 866 assert_kind_of Array, attachments
853 867 assert_equal 1, attachments.size
854 868 url = attachments.first['content_url']
855 869 assert_not_nil url
856 870
857 871 # download the attachment
858 872 get url
859 873 assert_response :success
860 874 end
861 875
862 876 def test_update_issue_with_uploaded_file
863 877 set_tmp_attachments_directory
864 878 # upload the file
865 879 assert_difference 'Attachment.count' do
866 880 post '/uploads.xml', 'test_upload_with_upload',
867 881 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
868 882 assert_response :created
869 883 end
870 884 xml = Hash.from_xml(response.body)
871 885 token = xml['upload']['token']
872 886 attachment = Attachment.order('id DESC').first
873 887
874 888 # update the issue with the upload's token
875 889 assert_difference 'Journal.count' do
876 890 put '/issues/1.xml',
877 891 {:issue => {:notes => 'Attachment added',
878 892 :uploads => [{:token => token, :filename => 'test.txt',
879 893 :content_type => 'text/plain'}]}},
880 894 credentials('jsmith')
881 895 assert_response :ok
882 896 assert_equal '', @response.body
883 897 end
884 898
885 899 issue = Issue.find(1)
886 900 assert_include attachment, issue.attachments
887 901 end
888 902 end
General Comments 0
You need to be logged in to leave comments. Login now