##// END OF EJS Templates
Adds tests for creating an issue with multiple uploads....
Jean-Philippe Lang -
r13409:9b082712a091
parent child
Show More
@@ -1,590 +1,647
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 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 test "GET /issues.xml should contain metadata" do
48 48 get '/issues.xml'
49 49 assert_select 'issues[type=array][total_count=?][limit="25"][offset="0"]',
50 50 assigns(:issue_count).to_s
51 51 end
52 52
53 53 test "GET /issues.xml with nometa param should not contain metadata" do
54 54 get '/issues.xml?nometa=1'
55 55 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
56 56 end
57 57
58 58 test "GET /issues.xml with nometa header should not contain metadata" do
59 59 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
60 60 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
61 61 end
62 62
63 63 test "GET /issues.xml with offset and limit" do
64 64 get '/issues.xml?offset=2&limit=3'
65 65
66 66 assert_equal 3, assigns(:limit)
67 67 assert_equal 2, assigns(:offset)
68 68 assert_select 'issues issue', 3
69 69 end
70 70
71 71 test "GET /issues.xml with relations" do
72 72 get '/issues.xml?include=relations'
73 73
74 74 assert_response :success
75 75 assert_equal 'application/xml', @response.content_type
76 76
77 77 assert_select 'issue id:content(3)' do
78 78 assert_select '~ relations relation', 1
79 79 assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
80 80 end
81 81
82 82 assert_select 'issue id:content(1)' do
83 83 assert_select '~ relations'
84 84 assert_select '~ relations relation', 0
85 85 end
86 86 end
87 87
88 88 test "GET /issues.xml with invalid query params" do
89 89 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
90 90
91 91 assert_response :unprocessable_entity
92 92 assert_equal 'application/xml', @response.content_type
93 93 assert_select 'errors error', :text => "Start date cannot be blank"
94 94 end
95 95
96 96 test "GET /issues.xml with custom field filter" do
97 97 get '/issues.xml',
98 98 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
99 99
100 100 expected_ids = Issue.visible.
101 101 joins(:custom_values).
102 102 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
103 103 assert expected_ids.any?
104 104
105 105 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
106 106 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
107 107 end
108 108 end
109 109
110 110 test "GET /issues.xml with custom field filter (shorthand method)" do
111 111 get '/issues.xml', {:cf_1 => 'MySQL'}
112 112
113 113 expected_ids = Issue.visible.
114 114 joins(:custom_values).
115 115 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
116 116 assert expected_ids.any?
117 117
118 118 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
119 119 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
120 120 end
121 121 end
122 122
123 123 def test_index_should_include_issue_attributes
124 124 get '/issues.xml'
125 125 assert_select 'issues>issue>is_private', :text => 'false'
126 126 end
127 127
128 128 def test_index_should_allow_timestamp_filtering
129 129 Issue.delete_all
130 130 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
131 131 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
132 132
133 133 get '/issues.xml',
134 134 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
135 135 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
136 136 assert_select 'issues>issue', :count => 1
137 137 assert_select 'issues>issue>subject', :text => '1'
138 138
139 139 get '/issues.xml',
140 140 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
141 141 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
142 142 assert_select 'issues>issue', :count => 1
143 143 assert_select 'issues>issue>subject', :text => '2'
144 144
145 145 get '/issues.xml',
146 146 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
147 147 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
148 148 assert_select 'issues>issue', :count => 2
149 149 end
150 150
151 151 test "GET /issues.xml with filter" do
152 152 get '/issues.xml?status_id=5'
153 153
154 154 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
155 155 assert expected_ids.any?
156 156
157 157 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
158 158 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
159 159 end
160 160 end
161 161
162 162 test "GET /issues.json with filter" do
163 163 get '/issues.json?status_id=5'
164 164
165 165 json = ActiveSupport::JSON.decode(response.body)
166 166 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
167 167 assert_equal 3, status_ids_used.length
168 168 assert status_ids_used.all? {|id| id == 5 }
169 169 end
170 170
171 171 test "GET /issues/:id.xml with journals" do
172 172 get '/issues/1.xml?include=journals'
173 173
174 174 assert_select 'issue journals[type=array]' do
175 175 assert_select 'journal[id="1"]' do
176 176 assert_select 'details[type=array]' do
177 177 assert_select 'detail[name=status_id]' do
178 178 assert_select 'old_value', :text => '1'
179 179 assert_select 'new_value', :text => '2'
180 180 end
181 181 end
182 182 end
183 183 end
184 184 end
185 185
186 186 test "GET /issues/:id.xml with custom fields" do
187 187 get '/issues/3.xml'
188 188
189 189 assert_select 'issue custom_fields[type=array]' do
190 190 assert_select 'custom_field[id="1"]' do
191 191 assert_select 'value', :text => 'MySQL'
192 192 end
193 193 end
194 194 assert_nothing_raised do
195 195 Hash.from_xml(response.body).to_xml
196 196 end
197 197 end
198 198
199 199 test "GET /issues/:id.xml with multi custom fields" do
200 200 field = CustomField.find(1)
201 201 field.update_attribute :multiple, true
202 202 issue = Issue.find(3)
203 203 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
204 204 issue.save!
205 205
206 206 get '/issues/3.xml'
207 207 assert_response :success
208 208
209 209 assert_select 'issue custom_fields[type=array]' do
210 210 assert_select 'custom_field[id="1"]' do
211 211 assert_select 'value[type=array] value', 2
212 212 end
213 213 end
214 214 xml = Hash.from_xml(response.body)
215 215 custom_fields = xml['issue']['custom_fields']
216 216 assert_kind_of Array, custom_fields
217 217 field = custom_fields.detect {|f| f['id'] == '1'}
218 218 assert_kind_of Hash, field
219 219 assert_equal ['MySQL', 'Oracle'], field['value'].sort
220 220 end
221 221
222 222 test "GET /issues/:id.json with multi custom fields" do
223 223 field = CustomField.find(1)
224 224 field.update_attribute :multiple, true
225 225 issue = Issue.find(3)
226 226 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
227 227 issue.save!
228 228
229 229 get '/issues/3.json'
230 230 assert_response :success
231 231
232 232 json = ActiveSupport::JSON.decode(response.body)
233 233 custom_fields = json['issue']['custom_fields']
234 234 assert_kind_of Array, custom_fields
235 235 field = custom_fields.detect {|f| f['id'] == 1}
236 236 assert_kind_of Hash, field
237 237 assert_equal ['MySQL', 'Oracle'], field['value'].sort
238 238 end
239 239
240 240 test "GET /issues/:id.xml with empty value for multi custom field" do
241 241 field = CustomField.find(1)
242 242 field.update_attribute :multiple, true
243 243 issue = Issue.find(3)
244 244 issue.custom_field_values = {1 => ['']}
245 245 issue.save!
246 246
247 247 get '/issues/3.xml'
248 248
249 249 assert_select 'issue custom_fields[type=array]' do
250 250 assert_select 'custom_field[id="1"]' do
251 251 assert_select 'value[type=array]:empty'
252 252 end
253 253 end
254 254 xml = Hash.from_xml(response.body)
255 255 custom_fields = xml['issue']['custom_fields']
256 256 assert_kind_of Array, custom_fields
257 257 field = custom_fields.detect {|f| f['id'] == '1'}
258 258 assert_kind_of Hash, field
259 259 assert_equal [], field['value']
260 260 end
261 261
262 262 test "GET /issues/:id.json with empty value for multi custom field" do
263 263 field = CustomField.find(1)
264 264 field.update_attribute :multiple, true
265 265 issue = Issue.find(3)
266 266 issue.custom_field_values = {1 => ['']}
267 267 issue.save!
268 268
269 269 get '/issues/3.json'
270 270 assert_response :success
271 271 json = ActiveSupport::JSON.decode(response.body)
272 272 custom_fields = json['issue']['custom_fields']
273 273 assert_kind_of Array, custom_fields
274 274 field = custom_fields.detect {|f| f['id'] == 1}
275 275 assert_kind_of Hash, field
276 276 assert_equal [], field['value'].sort
277 277 end
278 278
279 279 test "GET /issues/:id.xml with attachments" do
280 280 get '/issues/3.xml?include=attachments'
281 281
282 282 assert_select 'issue attachments[type=array]' do
283 283 assert_select 'attachment', 5
284 284 assert_select 'attachment id:content(4)' do
285 285 assert_select '~ filename', :text => 'source.rb'
286 286 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/4/source.rb'
287 287 end
288 288 end
289 289 end
290 290
291 291 test "GET /issues/:id.xml with subtasks" do
292 292 issue = Issue.generate_with_descendants!(:project_id => 1)
293 293 get "/issues/#{issue.id}.xml?include=children"
294 294
295 295 assert_select 'issue children[type=array]' do
296 296 assert_select 'issue', 2
297 297 assert_select 'issue children', 1
298 298 end
299 299 end
300 300
301 301 test "GET /issues/:id.json with subtasks" do
302 302 issue = Issue.generate_with_descendants!(:project_id => 1)
303 303 get "/issues/#{issue.id}.json?include=children"
304 304
305 305 json = ActiveSupport::JSON.decode(response.body)
306 306 assert_equal 2, json['issue']['children'].size
307 307 assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
308 308 end
309 309
310 310 def test_show_should_include_issue_attributes
311 311 get '/issues/1.xml'
312 312 assert_select 'issue>is_private', :text => 'false'
313 313 end
314 314
315 315 test "GET /issues/:id.xml?include=watchers should include watchers" do
316 316 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
317 317
318 318 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
319 319
320 320 assert_response :ok
321 321 assert_equal 'application/xml', response.content_type
322 322 assert_select 'issue' do
323 323 assert_select 'watchers', Issue.find(1).watchers.count
324 324 assert_select 'watchers' do
325 325 assert_select 'user[id=3]'
326 326 end
327 327 end
328 328 end
329 329
330 330 test "POST /issues.xml should create an issue with the attributes" do
331 331 assert_difference('Issue.count') do
332 332 post '/issues.xml',
333 333 {:issue => {:project_id => 1, :subject => 'API test',
334 334 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
335 335 end
336 336 issue = Issue.order('id DESC').first
337 337 assert_equal 1, issue.project_id
338 338 assert_equal 2, issue.tracker_id
339 339 assert_equal 3, issue.status_id
340 340 assert_equal 'API test', issue.subject
341 341
342 342 assert_response :created
343 343 assert_equal 'application/xml', @response.content_type
344 344 assert_select 'issue > id', :text => issue.id.to_s
345 345 end
346 346
347 347 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
348 348 assert_difference('Issue.count') do
349 349 post '/issues.xml',
350 350 {:issue => {:project_id => 1, :subject => 'Watchers',
351 351 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
352 352 assert_response :created
353 353 end
354 354 issue = Issue.order('id desc').first
355 355 assert_equal 2, issue.watchers.size
356 356 assert_equal [1, 3], issue.watcher_user_ids.sort
357 357 end
358 358
359 359 test "POST /issues.xml with failure should return errors" do
360 360 assert_no_difference('Issue.count') do
361 361 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
362 362 end
363 363
364 364 assert_select 'errors error', :text => "Subject cannot be blank"
365 365 end
366 366
367 367 test "POST /issues.json should create an issue with the attributes" do
368 368 assert_difference('Issue.count') do
369 369 post '/issues.json',
370 370 {:issue => {:project_id => 1, :subject => 'API test',
371 371 :tracker_id => 2, :status_id => 3}},
372 372 credentials('jsmith')
373 373 end
374 374
375 375 issue = Issue.order('id DESC').first
376 376 assert_equal 1, issue.project_id
377 377 assert_equal 2, issue.tracker_id
378 378 assert_equal 3, issue.status_id
379 379 assert_equal 'API test', issue.subject
380 380 end
381 381
382 382 test "POST /issues.json with failure should return errors" do
383 383 assert_no_difference('Issue.count') do
384 384 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
385 385 end
386 386
387 387 json = ActiveSupport::JSON.decode(response.body)
388 388 assert json['errors'].include?("Subject cannot be blank")
389 389 end
390 390
391 391 test "PUT /issues/:id.xml" do
392 392 assert_difference('Journal.count') do
393 393 put '/issues/6.xml',
394 394 {:issue => {:subject => 'API update', :notes => 'A new note'}},
395 395 credentials('jsmith')
396 396 end
397 397
398 398 issue = Issue.find(6)
399 399 assert_equal "API update", issue.subject
400 400 journal = Journal.last
401 401 assert_equal "A new note", journal.notes
402 402 end
403 403
404 404 test "PUT /issues/:id.xml with custom fields" do
405 405 put '/issues/3.xml',
406 406 {:issue => {:custom_fields => [
407 407 {'id' => '1', 'value' => 'PostgreSQL' },
408 408 {'id' => '2', 'value' => '150'}
409 409 ]}},
410 410 credentials('jsmith')
411 411
412 412 issue = Issue.find(3)
413 413 assert_equal '150', issue.custom_value_for(2).value
414 414 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
415 415 end
416 416
417 417 test "PUT /issues/:id.xml with multi custom fields" do
418 418 field = CustomField.find(1)
419 419 field.update_attribute :multiple, true
420 420
421 421 put '/issues/3.xml',
422 422 {:issue => {:custom_fields => [
423 423 {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
424 424 {'id' => '2', 'value' => '150'}
425 425 ]}},
426 426 credentials('jsmith')
427 427
428 428 issue = Issue.find(3)
429 429 assert_equal '150', issue.custom_value_for(2).value
430 430 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
431 431 end
432 432
433 433 test "PUT /issues/:id.xml with project change" do
434 434 put '/issues/3.xml',
435 435 {:issue => {:project_id => 2, :subject => 'Project changed'}},
436 436 credentials('jsmith')
437 437
438 438 issue = Issue.find(3)
439 439 assert_equal 2, issue.project_id
440 440 assert_equal 'Project changed', issue.subject
441 441 end
442 442
443 443 test "PUT /issues/:id.xml with failed update" do
444 444 put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith')
445 445
446 446 assert_response :unprocessable_entity
447 447 assert_select 'errors error', :text => "Subject cannot be blank"
448 448 end
449 449
450 450 test "PUT /issues/:id.json" do
451 451 assert_difference('Journal.count') do
452 452 put '/issues/6.json',
453 453 {:issue => {:subject => 'API update', :notes => 'A new note'}},
454 454 credentials('jsmith')
455 455
456 456 assert_response :ok
457 457 assert_equal '', response.body
458 458 end
459 459
460 460 issue = Issue.find(6)
461 461 assert_equal "API update", issue.subject
462 462 journal = Journal.last
463 463 assert_equal "A new note", journal.notes
464 464 end
465 465
466 466 test "PUT /issues/:id.json with failed update" do
467 467 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
468 468
469 469 assert_response :unprocessable_entity
470 470 json = ActiveSupport::JSON.decode(response.body)
471 471 assert json['errors'].include?("Subject cannot be blank")
472 472 end
473 473
474 474 test "DELETE /issues/:id.xml" do
475 475 assert_difference('Issue.count', -1) do
476 476 delete '/issues/6.xml', {}, credentials('jsmith')
477 477
478 478 assert_response :ok
479 479 assert_equal '', response.body
480 480 end
481 481 assert_nil Issue.find_by_id(6)
482 482 end
483 483
484 484 test "DELETE /issues/:id.json" do
485 485 assert_difference('Issue.count', -1) do
486 486 delete '/issues/6.json', {}, credentials('jsmith')
487 487
488 488 assert_response :ok
489 489 assert_equal '', response.body
490 490 end
491 491 assert_nil Issue.find_by_id(6)
492 492 end
493 493
494 494 test "POST /issues/:id/watchers.xml should add watcher" do
495 495 assert_difference 'Watcher.count' do
496 496 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
497 497
498 498 assert_response :ok
499 499 assert_equal '', response.body
500 500 end
501 501 watcher = Watcher.order('id desc').first
502 502 assert_equal Issue.find(1), watcher.watchable
503 503 assert_equal User.find(3), watcher.user
504 504 end
505 505
506 506 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
507 507 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
508 508
509 509 assert_difference 'Watcher.count', -1 do
510 510 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
511 511
512 512 assert_response :ok
513 513 assert_equal '', response.body
514 514 end
515 515 assert_equal false, Issue.find(1).watched_by?(User.find(3))
516 516 end
517 517
518 518 def test_create_issue_with_uploaded_file
519 519 set_tmp_attachments_directory
520 520 # upload the file
521 521 assert_difference 'Attachment.count' do
522 522 post '/uploads.xml', 'test_create_with_upload',
523 523 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
524 524 assert_response :created
525 525 end
526 526 xml = Hash.from_xml(response.body)
527 527 token = xml['upload']['token']
528 528 attachment = Attachment.order('id DESC').first
529 529
530 530 # create the issue with the upload's token
531 531 assert_difference 'Issue.count' do
532 532 post '/issues.xml',
533 533 {:issue => {:project_id => 1, :subject => 'Uploaded file',
534 534 :uploads => [{:token => token, :filename => 'test.txt',
535 535 :content_type => 'text/plain'}]}},
536 536 credentials('jsmith')
537 537 assert_response :created
538 538 end
539 539 issue = Issue.order('id DESC').first
540 540 assert_equal 1, issue.attachments.count
541 541 assert_equal attachment, issue.attachments.first
542 542
543 543 attachment.reload
544 544 assert_equal 'test.txt', attachment.filename
545 545 assert_equal 'text/plain', attachment.content_type
546 546 assert_equal 'test_create_with_upload'.size, attachment.filesize
547 547 assert_equal 2, attachment.author_id
548 548
549 549 # get the issue with its attachments
550 550 get "/issues/#{issue.id}.xml", :include => 'attachments'
551 551 assert_response :success
552 552 xml = Hash.from_xml(response.body)
553 553 attachments = xml['issue']['attachments']
554 554 assert_kind_of Array, attachments
555 555 assert_equal 1, attachments.size
556 556 url = attachments.first['content_url']
557 557 assert_not_nil url
558 558
559 559 # download the attachment
560 560 get url
561 561 assert_response :success
562 562 end
563 563
564 def test_create_issue_with_multiple_uploaded_files_as_xml
565 token1 = xml_upload('File content 1', credentials('jsmith'))
566 token2 = xml_upload('File content 2', credentials('jsmith'))
567
568 payload = <<-XML
569 <?xml version="1.0" encoding="UTF-8" ?>
570 <issue>
571 <project_id>1</project_id>
572 <tracker_id>1</tracker_id>
573 <subject>Issue with multiple attachments</subject>
574 <uploads type="array">
575 <upload>
576 <token>#{token1}</token>
577 <filename>test1.txt</filename>
578 </upload>
579 <upload>
580 <token>#{token2}</token>
581 <filename>test1.txt</filename>
582 </upload>
583 </uploads>
584 </issue>
585 XML
586
587 assert_difference 'Issue.count' do
588 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
589 assert_response :created
590 end
591 issue = Issue.order('id DESC').first
592 assert_equal 2, issue.attachments.count
593 end
594
595 def test_create_issue_with_multiple_uploaded_files_as_json
596 token1 = json_upload('File content 1', credentials('jsmith'))
597 token2 = json_upload('File content 2', credentials('jsmith'))
598
599 payload = <<-JSON
600 {
601 "issue": {
602 "project_id": "1",
603 "tracker_id": "1",
604 "subject": "Issue with multiple attachments",
605 "uploads": [
606 {"token": "#{token1}", "filename": "test1.txt"},
607 {"token": "#{token2}", "filename": "test2.txt"}
608 ]
609 }
610 }
611 JSON
612
613 assert_difference 'Issue.count' do
614 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
615 assert_response :created
616 end
617 issue = Issue.order('id DESC').first
618 assert_equal 2, issue.attachments.count
619 end
620
564 621 def test_update_issue_with_uploaded_file
565 622 set_tmp_attachments_directory
566 623 # upload the file
567 624 assert_difference 'Attachment.count' do
568 625 post '/uploads.xml', 'test_upload_with_upload',
569 626 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
570 627 assert_response :created
571 628 end
572 629 xml = Hash.from_xml(response.body)
573 630 token = xml['upload']['token']
574 631 attachment = Attachment.order('id DESC').first
575 632
576 633 # update the issue with the upload's token
577 634 assert_difference 'Journal.count' do
578 635 put '/issues/1.xml',
579 636 {:issue => {:notes => 'Attachment added',
580 637 :uploads => [{:token => token, :filename => 'test.txt',
581 638 :content_type => 'text/plain'}]}},
582 639 credentials('jsmith')
583 640 assert_response :ok
584 641 assert_equal '', @response.body
585 642 end
586 643
587 644 issue = Issue.find(1)
588 645 assert_include attachment, issue.attachments
589 646 end
590 647 end
@@ -1,290 +1,329
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 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 if ENV["COVERAGE"]
19 19 require 'simplecov'
20 20 require File.expand_path(File.dirname(__FILE__) + "/coverage/html_formatter")
21 21 SimpleCov.formatter = Redmine::Coverage::HtmlFormatter
22 22 SimpleCov.start 'rails'
23 23 end
24 24
25 25 ENV["RAILS_ENV"] = "test"
26 26 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
27 27 require 'rails/test_help'
28 28 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
29 29
30 30 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
31 31 include ObjectHelpers
32 32
33 33 require 'awesome_nested_set/version'
34 34 require 'net/ldap'
35 35
36 36 class ActionView::TestCase
37 37 helper :application
38 38 include ApplicationHelper
39 39 end
40 40
41 41 class ActiveSupport::TestCase
42 42 include ActionDispatch::TestProcess
43 43
44 44 self.use_transactional_fixtures = true
45 45 self.use_instantiated_fixtures = false
46 46
47 47 def uploaded_test_file(name, mime)
48 48 fixture_file_upload("files/#{name}", mime, true)
49 49 end
50 50
51 51 # Mock out a file
52 52 def self.mock_file
53 53 file = 'a_file.png'
54 54 file.stubs(:size).returns(32)
55 55 file.stubs(:original_filename).returns('a_file.png')
56 56 file.stubs(:content_type).returns('image/png')
57 57 file.stubs(:read).returns(false)
58 58 file
59 59 end
60 60
61 61 def mock_file
62 62 self.class.mock_file
63 63 end
64 64
65 65 def mock_file_with_options(options={})
66 66 file = ''
67 67 file.stubs(:size).returns(32)
68 68 original_filename = options[:original_filename] || nil
69 69 file.stubs(:original_filename).returns(original_filename)
70 70 content_type = options[:content_type] || nil
71 71 file.stubs(:content_type).returns(content_type)
72 72 file.stubs(:read).returns(false)
73 73 file
74 74 end
75 75
76 76 # Use a temporary directory for attachment related tests
77 77 def set_tmp_attachments_directory
78 78 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
79 79 unless File.directory?("#{Rails.root}/tmp/test/attachments")
80 80 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
81 81 end
82 82 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
83 83 end
84 84
85 85 def set_fixtures_attachments_directory
86 86 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
87 87 end
88 88
89 89 def with_settings(options, &block)
90 90 saved_settings = options.keys.inject({}) do |h, k|
91 91 h[k] = case Setting[k]
92 92 when Symbol, false, true, nil
93 93 Setting[k]
94 94 else
95 95 Setting[k].dup
96 96 end
97 97 h
98 98 end
99 99 options.each {|k, v| Setting[k] = v}
100 100 yield
101 101 ensure
102 102 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
103 103 end
104 104
105 105 # Yields the block with user as the current user
106 106 def with_current_user(user, &block)
107 107 saved_user = User.current
108 108 User.current = user
109 109 yield
110 110 ensure
111 111 User.current = saved_user
112 112 end
113 113
114 114 def with_locale(locale, &block)
115 115 saved_localed = ::I18n.locale
116 116 ::I18n.locale = locale
117 117 yield
118 118 ensure
119 119 ::I18n.locale = saved_localed
120 120 end
121 121
122 122 def self.ldap_configured?
123 123 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
124 124 return @test_ldap.bind
125 125 rescue Exception => e
126 126 # LDAP is not listening
127 127 return nil
128 128 end
129 129
130 130 def self.convert_installed?
131 131 Redmine::Thumbnail.convert_available?
132 132 end
133 133
134 134 # Returns the path to the test +vendor+ repository
135 135 def self.repository_path(vendor)
136 136 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
137 137 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
138 138 path.tr("\\", "/")
139 139 end
140 140
141 141 # Returns the url of the subversion test repository
142 142 def self.subversion_repository_url
143 143 path = repository_path('subversion')
144 144 path = '/' + path unless path.starts_with?('/')
145 145 "file://#{path}"
146 146 end
147 147
148 148 # Returns true if the +vendor+ test repository is configured
149 149 def self.repository_configured?(vendor)
150 150 File.directory?(repository_path(vendor))
151 151 end
152 152
153 153 def repository_path_hash(arr)
154 154 hs = {}
155 155 hs[:path] = arr.join("/")
156 156 hs[:param] = arr.join("/")
157 157 hs
158 158 end
159 159
160 160 def sqlite?
161 161 ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
162 162 end
163 163
164 164 def mysql?
165 165 ActiveRecord::Base.connection.adapter_name =~ /mysql/i
166 166 end
167 167
168 168 def assert_save(object)
169 169 saved = object.save
170 170 message = "#{object.class} could not be saved"
171 171 errors = object.errors.full_messages.map {|m| "- #{m}"}
172 172 message << ":\n#{errors.join("\n")}" if errors.any?
173 173 assert_equal true, saved, message
174 174 end
175 175
176 176 def assert_select_error(arg)
177 177 assert_select '#errorExplanation', :text => arg
178 178 end
179 179
180 180 def assert_include(expected, s, message=nil)
181 181 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
182 182 end
183 183
184 184 def assert_not_include(expected, s, message=nil)
185 185 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
186 186 end
187 187
188 188 def assert_select_in(text, *args, &block)
189 189 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
190 190 assert_select(d, *args, &block)
191 191 end
192 192
193 193 def assert_mail_body_match(expected, mail, message=nil)
194 194 if expected.is_a?(String)
195 195 assert_include expected, mail_body(mail), message
196 196 else
197 197 assert_match expected, mail_body(mail), message
198 198 end
199 199 end
200 200
201 201 def assert_mail_body_no_match(expected, mail, message=nil)
202 202 if expected.is_a?(String)
203 203 assert_not_include expected, mail_body(mail), message
204 204 else
205 205 assert_no_match expected, mail_body(mail), message
206 206 end
207 207 end
208 208
209 209 def mail_body(mail)
210 210 mail.parts.first.body.encoded
211 211 end
212 212
213 213 # awesome_nested_set new node lft and rgt value changed this refactor revision.
214 214 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
215 215 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
216 216 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
217 217 # It seems correct behavior because of this line comment.
218 218 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
219 219 def new_issue_lft
220 220 # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
221 221 Issue.maximum(:rgt) + 1
222 222 end
223 223 end
224 224
225 225 module Redmine
226 226 class RoutingTest < ActionDispatch::IntegrationTest
227 227 def should_route(arg)
228 228 arg = arg.dup
229 229 request = arg.keys.detect {|key| key.is_a?(String)}
230 230 raise ArgumentError unless request
231 231 options = arg.slice!(request)
232 232
233 233 raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/
234 234 method, path = $1.downcase.to_sym, $2
235 235
236 236 raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/
237 237 controller, action = $1, $2
238 238
239 239 assert_routing(
240 240 {:method => method, :path => path},
241 241 options.merge(:controller => controller, :action => action)
242 242 )
243 243 end
244 244 end
245 245
246 246 class IntegrationTest < ActionDispatch::IntegrationTest
247 247 def log_user(login, password)
248 248 User.anonymous
249 249 get "/login"
250 250 assert_equal nil, session[:user_id]
251 251 assert_response :success
252 252 assert_template "account/login"
253 253 post "/login", :username => login, :password => password
254 254 assert_equal login, User.find(session[:user_id]).login
255 255 end
256 256
257 257 def credentials(user, password=nil)
258 258 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
259 259 end
260 260 end
261 261
262 262 module ApiTest
263 263 API_FORMATS = %w(json xml).freeze
264 264
265 265 # Base class for API tests
266 266 class Base < Redmine::IntegrationTest
267 267 def setup
268 268 Setting.rest_api_enabled = '1'
269 269 end
270 270
271 271 def teardown
272 272 Setting.rest_api_enabled = '0'
273 273 end
274
275 # Uploads content using the XML API and returns the attachment token
276 def xml_upload(content, credentials)
277 upload('xml', content, credentials)
278 end
279
280 # Uploads content using the JSON API and returns the attachment token
281 def json_upload(content, credentials)
282 upload('json', content, credentials)
283 end
284
285 def upload(format, content, credentials)
286 set_tmp_attachments_directory
287 assert_difference 'Attachment.count' do
288 post "/uploads.#{format}", content, {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials)
289 assert_response :created
290 end
291 data = response_data
292 assert_kind_of Hash, data['upload']
293 token = data['upload']['token']
294 assert_not_nil token
295 token
296 end
297
298 # Parses the response body based on its content type
299 def response_data
300 unless response.content_type.to_s =~ /^application\/(.+)/
301 raise "Unexpected response type: #{response.content_type}"
302 end
303 format = $1
304 case format
305 when 'xml'
306 Hash.from_xml(response.body)
307 when 'json'
308 ActiveSupport::JSON.decode(response.body)
309 else
310 raise "Unknown response format: #{format}"
311 end
312 end
274 313 end
275 314
276 315 class Routing < Redmine::RoutingTest
277 316 def should_route(arg)
278 317 arg = arg.dup
279 318 request = arg.keys.detect {|key| key.is_a?(String)}
280 319 raise ArgumentError unless request
281 320 options = arg.slice!(request)
282 321
283 322 API_FORMATS.each do |format|
284 323 format_request = request.sub /$/, ".#{format}"
285 324 super options.merge(format_request => arg[request], :format => format)
286 325 end
287 326 end
288 327 end
289 328 end
290 329 end
General Comments 0
You need to be logged in to leave comments. Login now