##// END OF EJS Templates
not use assert_not_nil in Errors#[]...
Toshi MARUYAMA -
r11840:c49451eb102d
parent child
Show More
@@ -1,592 +1,592
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 ProjectsControllerTest < ActionController::TestCase
21 21 fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
22 22 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
23 23 :attachments, :custom_fields, :custom_values, :time_entries
24 24
25 25 def setup
26 26 @request.session[:user_id] = nil
27 27 Setting.default_language = 'en'
28 28 end
29 29
30 30 def test_index_by_anonymous_should_not_show_private_projects
31 31 get :index
32 32 assert_response :success
33 33 assert_template 'index'
34 34 projects = assigns(:projects)
35 35 assert_not_nil projects
36 36 assert projects.all?(&:is_public?)
37 37
38 38 assert_select 'ul' do
39 39 assert_select 'li' do
40 40 assert_select 'a', :text => 'eCookbook'
41 41 assert_select 'ul' do
42 42 assert_select 'a', :text => 'Child of private child'
43 43 end
44 44 end
45 45 end
46 46 assert_select 'a', :text => /Private child of eCookbook/, :count => 0
47 47 end
48 48
49 49 def test_index_atom
50 50 get :index, :format => 'atom'
51 51 assert_response :success
52 52 assert_template 'common/feed'
53 53 assert_select 'feed>title', :text => 'Redmine: Latest projects'
54 54 assert_select 'feed>entry', :count => Project.visible(User.current).count
55 55 end
56 56
57 57 test "#index by non-admin user with view_time_entries permission should show overall spent time link" do
58 58 @request.session[:user_id] = 3
59 59 get :index
60 60 assert_template 'index'
61 61 assert_select 'a[href=?]', '/time_entries'
62 62 end
63 63
64 64 test "#index by non-admin user without view_time_entries permission should not show overall spent time link" do
65 65 Role.find(2).remove_permission! :view_time_entries
66 66 Role.non_member.remove_permission! :view_time_entries
67 67 Role.anonymous.remove_permission! :view_time_entries
68 68 @request.session[:user_id] = 3
69 69
70 70 get :index
71 71 assert_template 'index'
72 72 assert_select 'a[href=?]', '/time_entries', 0
73 73 end
74 74
75 75 test "#new by admin user should accept get" do
76 76 @request.session[:user_id] = 1
77 77
78 78 get :new
79 79 assert_response :success
80 80 assert_template 'new'
81 81 end
82 82
83 83 test "#new by non-admin user with add_project permission should accept get" do
84 84 Role.non_member.add_permission! :add_project
85 85 @request.session[:user_id] = 9
86 86
87 87 get :new
88 88 assert_response :success
89 89 assert_template 'new'
90 90 assert_select 'select[name=?]', 'project[parent_id]', 0
91 91 end
92 92
93 93 test "#new by non-admin user with add_subprojects permission should accept get" do
94 94 Role.find(1).remove_permission! :add_project
95 95 Role.find(1).add_permission! :add_subprojects
96 96 @request.session[:user_id] = 2
97 97
98 98 get :new, :parent_id => 'ecookbook'
99 99 assert_response :success
100 100 assert_template 'new'
101 101
102 102 assert_select 'select[name=?]', 'project[parent_id]' do
103 103 # parent project selected
104 104 assert_select 'option[value=1][selected=selected]'
105 105 # no empty value
106 106 assert_select 'option[value=]', 0
107 107 end
108 108 end
109 109
110 110 test "#create by admin user should create a new project" do
111 111 @request.session[:user_id] = 1
112 112
113 113 post :create,
114 114 :project => {
115 115 :name => "blog",
116 116 :description => "weblog",
117 117 :homepage => 'http://weblog',
118 118 :identifier => "blog",
119 119 :is_public => 1,
120 120 :custom_field_values => { '3' => 'Beta' },
121 121 :tracker_ids => ['1', '3'],
122 122 # an issue custom field that is not for all project
123 123 :issue_custom_field_ids => ['9'],
124 124 :enabled_module_names => ['issue_tracking', 'news', 'repository']
125 125 }
126 126 assert_redirected_to '/projects/blog/settings'
127 127
128 128 project = Project.find_by_name('blog')
129 129 assert_kind_of Project, project
130 130 assert project.active?
131 131 assert_equal 'weblog', project.description
132 132 assert_equal 'http://weblog', project.homepage
133 133 assert_equal true, project.is_public?
134 134 assert_nil project.parent
135 135 assert_equal 'Beta', project.custom_value_for(3).value
136 136 assert_equal [1, 3], project.trackers.map(&:id).sort
137 137 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
138 138 assert project.issue_custom_fields.include?(IssueCustomField.find(9))
139 139 end
140 140
141 141 test "#create by admin user should create a new subproject" do
142 142 @request.session[:user_id] = 1
143 143
144 144 assert_difference 'Project.count' do
145 145 post :create, :project => { :name => "blog",
146 146 :description => "weblog",
147 147 :identifier => "blog",
148 148 :is_public => 1,
149 149 :custom_field_values => { '3' => 'Beta' },
150 150 :parent_id => 1
151 151 }
152 152 assert_redirected_to '/projects/blog/settings'
153 153 end
154 154
155 155 project = Project.find_by_name('blog')
156 156 assert_kind_of Project, project
157 157 assert_equal Project.find(1), project.parent
158 158 end
159 159
160 160 test "#create by admin user should continue" do
161 161 @request.session[:user_id] = 1
162 162
163 163 assert_difference 'Project.count' do
164 164 post :create, :project => {:name => "blog", :identifier => "blog"}, :continue => 'Create and continue'
165 165 end
166 166 assert_redirected_to '/projects/new'
167 167 end
168 168
169 169 test "#create by non-admin user with add_project permission should create a new project" do
170 170 Role.non_member.add_permission! :add_project
171 171 @request.session[:user_id] = 9
172 172
173 173 post :create, :project => { :name => "blog",
174 174 :description => "weblog",
175 175 :identifier => "blog",
176 176 :is_public => 1,
177 177 :custom_field_values => { '3' => 'Beta' },
178 178 :tracker_ids => ['1', '3'],
179 179 :enabled_module_names => ['issue_tracking', 'news', 'repository']
180 180 }
181 181
182 182 assert_redirected_to '/projects/blog/settings'
183 183
184 184 project = Project.find_by_name('blog')
185 185 assert_kind_of Project, project
186 186 assert_equal 'weblog', project.description
187 187 assert_equal true, project.is_public?
188 188 assert_equal [1, 3], project.trackers.map(&:id).sort
189 189 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
190 190
191 191 # User should be added as a project member
192 192 assert User.find(9).member_of?(project)
193 193 assert_equal 1, project.members.size
194 194 end
195 195
196 196 test "#create by non-admin user with add_project permission should fail with parent_id" do
197 197 Role.non_member.add_permission! :add_project
198 198 @request.session[:user_id] = 9
199 199
200 200 assert_no_difference 'Project.count' do
201 201 post :create, :project => { :name => "blog",
202 202 :description => "weblog",
203 203 :identifier => "blog",
204 204 :is_public => 1,
205 205 :custom_field_values => { '3' => 'Beta' },
206 206 :parent_id => 1
207 207 }
208 208 end
209 209 assert_response :success
210 210 project = assigns(:project)
211 211 assert_kind_of Project, project
212 assert_not_nil project.errors[:parent_id]
212 assert_not_equal [], project.errors[:parent_id]
213 213 end
214 214
215 215 test "#create by non-admin user with add_subprojects permission should create a project with a parent_id" do
216 216 Role.find(1).remove_permission! :add_project
217 217 Role.find(1).add_permission! :add_subprojects
218 218 @request.session[:user_id] = 2
219 219
220 220 post :create, :project => { :name => "blog",
221 221 :description => "weblog",
222 222 :identifier => "blog",
223 223 :is_public => 1,
224 224 :custom_field_values => { '3' => 'Beta' },
225 225 :parent_id => 1
226 226 }
227 227 assert_redirected_to '/projects/blog/settings'
228 228 project = Project.find_by_name('blog')
229 229 end
230 230
231 231 test "#create by non-admin user with add_subprojects permission should fail without parent_id" do
232 232 Role.find(1).remove_permission! :add_project
233 233 Role.find(1).add_permission! :add_subprojects
234 234 @request.session[:user_id] = 2
235 235
236 236 assert_no_difference 'Project.count' do
237 237 post :create, :project => { :name => "blog",
238 238 :description => "weblog",
239 239 :identifier => "blog",
240 240 :is_public => 1,
241 241 :custom_field_values => { '3' => 'Beta' }
242 242 }
243 243 end
244 244 assert_response :success
245 245 project = assigns(:project)
246 246 assert_kind_of Project, project
247 assert_not_nil project.errors[:parent_id]
247 assert_not_equal [], project.errors[:parent_id]
248 248 end
249 249
250 250 test "#create by non-admin user with add_subprojects permission should fail with unauthorized parent_id" do
251 251 Role.find(1).remove_permission! :add_project
252 252 Role.find(1).add_permission! :add_subprojects
253 253 @request.session[:user_id] = 2
254 254
255 255 assert !User.find(2).member_of?(Project.find(6))
256 256 assert_no_difference 'Project.count' do
257 257 post :create, :project => { :name => "blog",
258 258 :description => "weblog",
259 259 :identifier => "blog",
260 260 :is_public => 1,
261 261 :custom_field_values => { '3' => 'Beta' },
262 262 :parent_id => 6
263 263 }
264 264 end
265 265 assert_response :success
266 266 project = assigns(:project)
267 267 assert_kind_of Project, project
268 assert_not_nil project.errors[:parent_id]
268 assert_not_equal [], project.errors[:parent_id]
269 269 end
270 270
271 271 def test_create_subproject_with_inherit_members_should_inherit_members
272 272 Role.find_by_name('Manager').add_permission! :add_subprojects
273 273 parent = Project.find(1)
274 274 @request.session[:user_id] = 2
275 275
276 276 assert_difference 'Project.count' do
277 277 post :create, :project => {
278 278 :name => 'inherited', :identifier => 'inherited', :parent_id => parent.id, :inherit_members => '1'
279 279 }
280 280 assert_response 302
281 281 end
282 282
283 283 project = Project.order('id desc').first
284 284 assert_equal 'inherited', project.name
285 285 assert_equal parent, project.parent
286 286 assert project.memberships.count > 0
287 287 assert_equal parent.memberships.count, project.memberships.count
288 288 end
289 289
290 290 def test_create_should_preserve_modules_on_validation_failure
291 291 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
292 292 @request.session[:user_id] = 1
293 293 assert_no_difference 'Project.count' do
294 294 post :create, :project => {
295 295 :name => "blog",
296 296 :identifier => "",
297 297 :enabled_module_names => %w(issue_tracking news)
298 298 }
299 299 end
300 300 assert_response :success
301 301 project = assigns(:project)
302 302 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
303 303 end
304 304 end
305 305
306 306 def test_show_by_id
307 307 get :show, :id => 1
308 308 assert_response :success
309 309 assert_template 'show'
310 310 assert_not_nil assigns(:project)
311 311 end
312 312
313 313 def test_show_by_identifier
314 314 get :show, :id => 'ecookbook'
315 315 assert_response :success
316 316 assert_template 'show'
317 317 assert_not_nil assigns(:project)
318 318 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
319 319
320 320 assert_select 'li', :text => /Development status/
321 321 end
322 322
323 323 def test_show_should_not_display_hidden_custom_fields
324 324 ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
325 325 get :show, :id => 'ecookbook'
326 326 assert_response :success
327 327 assert_template 'show'
328 328 assert_not_nil assigns(:project)
329 329
330 330 assert_select 'li', :text => /Development status/, :count => 0
331 331 end
332 332
333 333 def test_show_should_not_fail_when_custom_values_are_nil
334 334 project = Project.find_by_identifier('ecookbook')
335 335 project.custom_values.first.update_attribute(:value, nil)
336 336 get :show, :id => 'ecookbook'
337 337 assert_response :success
338 338 assert_template 'show'
339 339 assert_not_nil assigns(:project)
340 340 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
341 341 end
342 342
343 343 def show_archived_project_should_be_denied
344 344 project = Project.find_by_identifier('ecookbook')
345 345 project.archive!
346 346
347 347 get :show, :id => 'ecookbook'
348 348 assert_response 403
349 349 assert_nil assigns(:project)
350 350 assert_select 'p', :text => /archived/
351 351 end
352 352
353 353 def test_show_should_not_show_private_subprojects_that_are_not_visible
354 354 get :show, :id => 'ecookbook'
355 355 assert_response :success
356 356 assert_template 'show'
357 357 assert_select 'a', :text => /Private child/, :count => 0
358 358 end
359 359
360 360 def test_show_should_show_private_subprojects_that_are_visible
361 361 @request.session[:user_id] = 2 # manager who is a member of the private subproject
362 362 get :show, :id => 'ecookbook'
363 363 assert_response :success
364 364 assert_template 'show'
365 365 assert_select 'a', :text => /Private child/
366 366 end
367 367
368 368 def test_settings
369 369 @request.session[:user_id] = 2 # manager
370 370 get :settings, :id => 1
371 371 assert_response :success
372 372 assert_template 'settings'
373 373 end
374 374
375 375 def test_settings_of_subproject
376 376 @request.session[:user_id] = 2
377 377 get :settings, :id => 'private-child'
378 378 assert_response :success
379 379 assert_template 'settings'
380 380
381 381 assert_select 'input[type=checkbox][name=?]', 'project[inherit_members]'
382 382 end
383 383
384 384 def test_settings_should_be_denied_for_member_on_closed_project
385 385 Project.find(1).close
386 386 @request.session[:user_id] = 2 # manager
387 387
388 388 get :settings, :id => 1
389 389 assert_response 403
390 390 end
391 391
392 392 def test_settings_should_be_denied_for_anonymous_on_closed_project
393 393 Project.find(1).close
394 394
395 395 get :settings, :id => 1
396 396 assert_response 302
397 397 end
398 398
399 399 def test_update
400 400 @request.session[:user_id] = 2 # manager
401 401 post :update, :id => 1, :project => {:name => 'Test changed name',
402 402 :issue_custom_field_ids => ['']}
403 403 assert_redirected_to '/projects/ecookbook/settings'
404 404 project = Project.find(1)
405 405 assert_equal 'Test changed name', project.name
406 406 end
407 407
408 408 def test_update_with_failure
409 409 @request.session[:user_id] = 2 # manager
410 410 post :update, :id => 1, :project => {:name => ''}
411 411 assert_response :success
412 412 assert_template 'settings'
413 413 assert_error_tag :content => /name can&#x27;t be blank/i
414 414 end
415 415
416 416 def test_update_should_be_denied_for_member_on_closed_project
417 417 Project.find(1).close
418 418 @request.session[:user_id] = 2 # manager
419 419
420 420 post :update, :id => 1, :project => {:name => 'Closed'}
421 421 assert_response 403
422 422 assert_equal 'eCookbook', Project.find(1).name
423 423 end
424 424
425 425 def test_update_should_be_denied_for_anonymous_on_closed_project
426 426 Project.find(1).close
427 427
428 428 post :update, :id => 1, :project => {:name => 'Closed'}
429 429 assert_response 302
430 430 assert_equal 'eCookbook', Project.find(1).name
431 431 end
432 432
433 433 def test_modules
434 434 @request.session[:user_id] = 2
435 435 Project.find(1).enabled_module_names = ['issue_tracking', 'news']
436 436
437 437 post :modules, :id => 1, :enabled_module_names => ['issue_tracking', 'repository', 'documents']
438 438 assert_redirected_to '/projects/ecookbook/settings/modules'
439 439 assert_equal ['documents', 'issue_tracking', 'repository'], Project.find(1).enabled_module_names.sort
440 440 end
441 441
442 442 def test_destroy_leaf_project_without_confirmation_should_show_confirmation
443 443 @request.session[:user_id] = 1 # admin
444 444
445 445 assert_no_difference 'Project.count' do
446 446 delete :destroy, :id => 2
447 447 assert_response :success
448 448 assert_template 'destroy'
449 449 end
450 450 end
451 451
452 452 def test_destroy_without_confirmation_should_show_confirmation_with_subprojects
453 453 @request.session[:user_id] = 1 # admin
454 454
455 455 assert_no_difference 'Project.count' do
456 456 delete :destroy, :id => 1
457 457 assert_response :success
458 458 assert_template 'destroy'
459 459 end
460 460 assert_select 'strong',
461 461 :text => ['Private child of eCookbook',
462 462 'Child of private child, eCookbook Subproject 1',
463 463 'eCookbook Subproject 2'].join(', ')
464 464 end
465 465
466 466 def test_destroy_with_confirmation_should_destroy_the_project_and_subprojects
467 467 @request.session[:user_id] = 1 # admin
468 468
469 469 assert_difference 'Project.count', -5 do
470 470 delete :destroy, :id => 1, :confirm => 1
471 471 assert_redirected_to '/admin/projects'
472 472 end
473 473 assert_nil Project.find_by_id(1)
474 474 end
475 475
476 476 def test_archive
477 477 @request.session[:user_id] = 1 # admin
478 478 post :archive, :id => 1
479 479 assert_redirected_to '/admin/projects'
480 480 assert !Project.find(1).active?
481 481 end
482 482
483 483 def test_archive_with_failure
484 484 @request.session[:user_id] = 1
485 485 Project.any_instance.stubs(:archive).returns(false)
486 486 post :archive, :id => 1
487 487 assert_redirected_to '/admin/projects'
488 488 assert_match /project cannot be archived/i, flash[:error]
489 489 end
490 490
491 491 def test_unarchive
492 492 @request.session[:user_id] = 1 # admin
493 493 Project.find(1).archive
494 494 post :unarchive, :id => 1
495 495 assert_redirected_to '/admin/projects'
496 496 assert Project.find(1).active?
497 497 end
498 498
499 499 def test_close
500 500 @request.session[:user_id] = 2
501 501 post :close, :id => 1
502 502 assert_redirected_to '/projects/ecookbook'
503 503 assert_equal Project::STATUS_CLOSED, Project.find(1).status
504 504 end
505 505
506 506 def test_reopen
507 507 Project.find(1).close
508 508 @request.session[:user_id] = 2
509 509 post :reopen, :id => 1
510 510 assert_redirected_to '/projects/ecookbook'
511 511 assert Project.find(1).active?
512 512 end
513 513
514 514 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
515 515 CustomField.delete_all
516 516 parent = nil
517 517 6.times do |i|
518 518 p = Project.generate_with_parent!(parent)
519 519 get :show, :id => p
520 520 assert_select '#header h1' do
521 521 assert_select 'a', :count => [i, 3].min
522 522 end
523 523
524 524 parent = p
525 525 end
526 526 end
527 527
528 528 def test_get_copy
529 529 @request.session[:user_id] = 1 # admin
530 530 get :copy, :id => 1
531 531 assert_response :success
532 532 assert_template 'copy'
533 533 assert assigns(:project)
534 534 assert_equal Project.find(1).description, assigns(:project).description
535 535 assert_nil assigns(:project).id
536 536
537 537 assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
538 538 end
539 539
540 540 def test_get_copy_with_invalid_source_should_respond_with_404
541 541 @request.session[:user_id] = 1
542 542 get :copy, :id => 99
543 543 assert_response 404
544 544 end
545 545
546 546 def test_post_copy_should_copy_requested_items
547 547 @request.session[:user_id] = 1 # admin
548 548 CustomField.delete_all
549 549
550 550 assert_difference 'Project.count' do
551 551 post :copy, :id => 1,
552 552 :project => {
553 553 :name => 'Copy',
554 554 :identifier => 'unique-copy',
555 555 :tracker_ids => ['1', '2', '3', ''],
556 556 :enabled_module_names => %w(issue_tracking time_tracking)
557 557 },
558 558 :only => %w(issues versions)
559 559 end
560 560 project = Project.find('unique-copy')
561 561 source = Project.find(1)
562 562 assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
563 563
564 564 assert_equal source.versions.count, project.versions.count, "All versions were not copied"
565 565 assert_equal source.issues.count, project.issues.count, "All issues were not copied"
566 566 assert_equal 0, project.members.count
567 567 end
568 568
569 569 def test_post_copy_should_redirect_to_settings_when_successful
570 570 @request.session[:user_id] = 1 # admin
571 571 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => 'unique-copy'}
572 572 assert_response :redirect
573 573 assert_redirected_to :controller => 'projects', :action => 'settings', :id => 'unique-copy'
574 574 end
575 575
576 576 def test_jump_should_redirect_to_active_tab
577 577 get :show, :id => 1, :jump => 'issues'
578 578 assert_redirected_to '/projects/ecookbook/issues'
579 579 end
580 580
581 581 def test_jump_should_not_redirect_to_inactive_tab
582 582 get :show, :id => 3, :jump => 'documents'
583 583 assert_response :success
584 584 assert_template 'show'
585 585 end
586 586
587 587 def test_jump_should_not_redirect_to_unknown_tab
588 588 get :show, :id => 3, :jump => 'foobar'
589 589 assert_response :success
590 590 assert_template 'show'
591 591 end
592 592 end
@@ -1,283 +1,283
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../test_helper', __FILE__)
21 21
22 22 class AttachmentTest < ActiveSupport::TestCase
23 23 fixtures :users, :projects, :roles, :members, :member_roles,
24 24 :enabled_modules, :issues, :trackers, :attachments
25 25
26 26 class MockFile
27 27 attr_reader :original_filename, :content_type, :content, :size
28 28
29 29 def initialize(attributes)
30 30 @original_filename = attributes[:original_filename]
31 31 @content_type = attributes[:content_type]
32 32 @content = attributes[:content] || "Content"
33 33 @size = content.size
34 34 end
35 35 end
36 36
37 37 def setup
38 38 set_tmp_attachments_directory
39 39 end
40 40
41 41 def test_container_for_new_attachment_should_be_nil
42 42 assert_nil Attachment.new.container
43 43 end
44 44
45 45 def test_create
46 46 a = Attachment.new(:container => Issue.find(1),
47 47 :file => uploaded_test_file("testfile.txt", "text/plain"),
48 48 :author => User.find(1))
49 49 assert a.save
50 50 assert_equal 'testfile.txt', a.filename
51 51 assert_equal 59, a.filesize
52 52 assert_equal 'text/plain', a.content_type
53 53 assert_equal 0, a.downloads
54 54 assert_equal '1478adae0d4eb06d35897518540e25d6', a.digest
55 55
56 56 assert a.disk_directory
57 57 assert_match %r{\A\d{4}/\d{2}\z}, a.disk_directory
58 58
59 59 assert File.exist?(a.diskfile)
60 60 assert_equal 59, File.size(a.diskfile)
61 61 end
62 62
63 63 def test_copy_should_preserve_attributes
64 64 a = Attachment.find(1)
65 65 copy = a.copy
66 66
67 67 assert_save copy
68 68 copy = Attachment.order('id DESC').first
69 69 %w(filename filesize content_type author_id created_on description digest disk_filename disk_directory diskfile).each do |attribute|
70 70 assert_equal a.send(attribute), copy.send(attribute), "#{attribute} was different"
71 71 end
72 72 end
73 73
74 74 def test_size_should_be_validated_for_new_file
75 75 with_settings :attachment_max_size => 0 do
76 76 a = Attachment.new(:container => Issue.find(1),
77 77 :file => uploaded_test_file("testfile.txt", "text/plain"),
78 78 :author => User.find(1))
79 79 assert !a.save
80 80 end
81 81 end
82 82
83 83 def test_size_should_not_be_validated_when_copying
84 84 a = Attachment.create!(:container => Issue.find(1),
85 85 :file => uploaded_test_file("testfile.txt", "text/plain"),
86 86 :author => User.find(1))
87 87 with_settings :attachment_max_size => 0 do
88 88 copy = a.copy
89 89 assert copy.save
90 90 end
91 91 end
92 92
93 93 def test_description_length_should_be_validated
94 94 a = Attachment.new(:description => 'a' * 300)
95 95 assert !a.save
96 assert_not_nil a.errors[:description]
96 assert_not_equal [], a.errors[:description]
97 97 end
98 98
99 99 def test_destroy
100 100 a = Attachment.new(:container => Issue.find(1),
101 101 :file => uploaded_test_file("testfile.txt", "text/plain"),
102 102 :author => User.find(1))
103 103 assert a.save
104 104 assert_equal 'testfile.txt', a.filename
105 105 assert_equal 59, a.filesize
106 106 assert_equal 'text/plain', a.content_type
107 107 assert_equal 0, a.downloads
108 108 assert_equal '1478adae0d4eb06d35897518540e25d6', a.digest
109 109 diskfile = a.diskfile
110 110 assert File.exist?(diskfile)
111 111 assert_equal 59, File.size(a.diskfile)
112 112 assert a.destroy
113 113 assert !File.exist?(diskfile)
114 114 end
115 115
116 116 def test_destroy_should_not_delete_file_referenced_by_other_attachment
117 117 a = Attachment.create!(:container => Issue.find(1),
118 118 :file => uploaded_test_file("testfile.txt", "text/plain"),
119 119 :author => User.find(1))
120 120 diskfile = a.diskfile
121 121
122 122 copy = a.copy
123 123 copy.save!
124 124
125 125 assert File.exists?(diskfile)
126 126 a.destroy
127 127 assert File.exists?(diskfile)
128 128 copy.destroy
129 129 assert !File.exists?(diskfile)
130 130 end
131 131
132 132 def test_create_should_auto_assign_content_type
133 133 a = Attachment.new(:container => Issue.find(1),
134 134 :file => uploaded_test_file("testfile.txt", ""),
135 135 :author => User.find(1))
136 136 assert a.save
137 137 assert_equal 'text/plain', a.content_type
138 138 end
139 139
140 140 def test_identical_attachments_at_the_same_time_should_not_overwrite
141 141 a1 = Attachment.create!(:container => Issue.find(1),
142 142 :file => uploaded_test_file("testfile.txt", ""),
143 143 :author => User.find(1))
144 144 a2 = Attachment.create!(:container => Issue.find(1),
145 145 :file => uploaded_test_file("testfile.txt", ""),
146 146 :author => User.find(1))
147 147 assert a1.disk_filename != a2.disk_filename
148 148 end
149 149
150 150 def test_filename_should_be_basenamed
151 151 a = Attachment.new(:file => MockFile.new(:original_filename => "path/to/the/file"))
152 152 assert_equal 'file', a.filename
153 153 end
154 154
155 155 def test_filename_should_be_sanitized
156 156 a = Attachment.new(:file => MockFile.new(:original_filename => "valid:[] invalid:?%*|\"'<>chars"))
157 157 assert_equal 'valid_[] invalid_chars', a.filename
158 158 end
159 159
160 160 def test_diskfilename
161 161 assert Attachment.disk_filename("test_file.txt") =~ /^\d{12}_test_file.txt$/
162 162 assert_equal 'test_file.txt', Attachment.disk_filename("test_file.txt")[13..-1]
163 163 assert_equal '770c509475505f37c2b8fb6030434d6b.txt', Attachment.disk_filename("test_accentuΓ©.txt")[13..-1]
164 164 assert_equal 'f8139524ebb8f32e51976982cd20a85d', Attachment.disk_filename("test_accentuΓ©")[13..-1]
165 165 assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentuΓ©.Γ§a")[13..-1]
166 166 end
167 167
168 168 def test_title
169 169 a = Attachment.new(:filename => "test.png")
170 170 assert_equal "test.png", a.title
171 171
172 172 a = Attachment.new(:filename => "test.png", :description => "Cool image")
173 173 assert_equal "test.png (Cool image)", a.title
174 174 end
175 175
176 176 def test_prune_should_destroy_old_unattached_attachments
177 177 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1, :created_on => 2.days.ago)
178 178 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1, :created_on => 2.days.ago)
179 179 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1)
180 180
181 181 assert_difference 'Attachment.count', -2 do
182 182 Attachment.prune
183 183 end
184 184 end
185 185
186 186 def test_move_from_root_to_target_directory_should_move_root_files
187 187 a = Attachment.find(20)
188 188 assert a.disk_directory.blank?
189 189 # Create a real file for this fixture
190 190 File.open(a.diskfile, "w") do |f|
191 191 f.write "test file at the root of files directory"
192 192 end
193 193 assert a.readable?
194 194 Attachment.move_from_root_to_target_directory
195 195
196 196 a.reload
197 197 assert_equal '2012/05', a.disk_directory
198 198 assert a.readable?
199 199 end
200 200
201 201 test "Attachmnet.attach_files should attach the file" do
202 202 issue = Issue.first
203 203 assert_difference 'Attachment.count' do
204 204 Attachment.attach_files(issue,
205 205 '1' => {
206 206 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
207 207 'description' => 'test'
208 208 })
209 209 end
210 210
211 211 attachment = Attachment.first(:order => 'id DESC')
212 212 assert_equal issue, attachment.container
213 213 assert_equal 'testfile.txt', attachment.filename
214 214 assert_equal 59, attachment.filesize
215 215 assert_equal 'test', attachment.description
216 216 assert_equal 'text/plain', attachment.content_type
217 217 assert File.exists?(attachment.diskfile)
218 218 assert_equal 59, File.size(attachment.diskfile)
219 219 end
220 220
221 221 test "Attachmnet.attach_files should add unsaved files to the object as unsaved attachments" do
222 222 # Max size of 0 to force Attachment creation failures
223 223 with_settings(:attachment_max_size => 0) do
224 224 @project = Project.find(1)
225 225 response = Attachment.attach_files(@project, {
226 226 '1' => {'file' => mock_file, 'description' => 'test'},
227 227 '2' => {'file' => mock_file, 'description' => 'test'}
228 228 })
229 229
230 230 assert response[:unsaved].present?
231 231 assert_equal 2, response[:unsaved].length
232 232 assert response[:unsaved].first.new_record?
233 233 assert response[:unsaved].second.new_record?
234 234 assert_equal response[:unsaved], @project.unsaved_attachments
235 235 end
236 236 end
237 237
238 238 def test_latest_attach
239 239 set_fixtures_attachments_directory
240 240 a1 = Attachment.find(16)
241 241 assert_equal "testfile.png", a1.filename
242 242 assert a1.readable?
243 243 assert (! a1.visible?(User.anonymous))
244 244 assert a1.visible?(User.find(2))
245 245 a2 = Attachment.find(17)
246 246 assert_equal "testfile.PNG", a2.filename
247 247 assert a2.readable?
248 248 assert (! a2.visible?(User.anonymous))
249 249 assert a2.visible?(User.find(2))
250 250 assert a1.created_on < a2.created_on
251 251
252 252 la1 = Attachment.latest_attach([a1, a2], "testfile.png")
253 253 assert_equal 17, la1.id
254 254 la2 = Attachment.latest_attach([a1, a2], "Testfile.PNG")
255 255 assert_equal 17, la2.id
256 256
257 257 set_tmp_attachments_directory
258 258 end
259 259
260 260 def test_thumbnailable_should_be_true_for_images
261 261 assert_equal true, Attachment.new(:filename => 'test.jpg').thumbnailable?
262 262 end
263 263
264 264 def test_thumbnailable_should_be_true_for_non_images
265 265 assert_equal false, Attachment.new(:filename => 'test.txt').thumbnailable?
266 266 end
267 267
268 268 if convert_installed?
269 269 def test_thumbnail_should_generate_the_thumbnail
270 270 set_fixtures_attachments_directory
271 271 attachment = Attachment.find(16)
272 272 Attachment.clear_thumbnails
273 273
274 274 assert_difference "Dir.glob(File.join(Attachment.thumbnails_storage_path, '*.thumb')).size" do
275 275 thumbnail = attachment.thumbnail
276 276 assert_equal "16_8e0294de2441577c529f170b6fb8f638_100.thumb", File.basename(thumbnail)
277 277 assert File.exists?(thumbnail)
278 278 end
279 279 end
280 280 else
281 281 puts '(ImageMagick convert not available)'
282 282 end
283 283 end
@@ -1,358 +1,358
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 IssueNestedSetTest < ActiveSupport::TestCase
21 21 fixtures :projects, :users, :roles,
22 22 :trackers, :projects_trackers,
23 23 :issue_statuses, :issue_categories, :issue_relations,
24 24 :enumerations,
25 25 :issues
26 26
27 27 def test_create_root_issue
28 28 issue1 = Issue.generate!
29 29 issue2 = Issue.generate!
30 30 issue1.reload
31 31 issue2.reload
32 32
33 33 assert_equal [issue1.id, nil, 1, 2], [issue1.root_id, issue1.parent_id, issue1.lft, issue1.rgt]
34 34 assert_equal [issue2.id, nil, 1, 2], [issue2.root_id, issue2.parent_id, issue2.lft, issue2.rgt]
35 35 end
36 36
37 37 def test_create_child_issue
38 38 parent = Issue.generate!
39 39 child = Issue.generate!(:parent_issue_id => parent.id)
40 40 parent.reload
41 41 child.reload
42 42
43 43 assert_equal [parent.id, nil, 1, 4], [parent.root_id, parent.parent_id, parent.lft, parent.rgt]
44 44 assert_equal [parent.id, parent.id, 2, 3], [child.root_id, child.parent_id, child.lft, child.rgt]
45 45 end
46 46
47 47 def test_creating_a_child_in_a_subproject_should_validate
48 48 issue = Issue.generate!
49 49 child = Issue.new(:project_id => 3, :tracker_id => 2, :author_id => 1,
50 50 :subject => 'child', :parent_issue_id => issue.id)
51 51 assert_save child
52 52 assert_equal issue, child.reload.parent
53 53 end
54 54
55 55 def test_creating_a_child_in_an_invalid_project_should_not_validate
56 56 issue = Issue.generate!
57 57 child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
58 58 :subject => 'child', :parent_issue_id => issue.id)
59 59 assert !child.save
60 assert_not_nil child.errors[:parent_issue_id]
60 assert_not_equal [], child.errors[:parent_issue_id]
61 61 end
62 62
63 63 def test_move_a_root_to_child
64 64 parent1 = Issue.generate!
65 65 parent2 = Issue.generate!
66 66 child = Issue.generate!(:parent_issue_id => parent1.id)
67 67
68 68 parent2.parent_issue_id = parent1.id
69 69 parent2.save!
70 70 child.reload
71 71 parent1.reload
72 72 parent2.reload
73 73
74 74 assert_equal [parent1.id, 1, 6], [parent1.root_id, parent1.lft, parent1.rgt]
75 75 assert_equal [parent1.id, 4, 5], [parent2.root_id, parent2.lft, parent2.rgt]
76 76 assert_equal [parent1.id, 2, 3], [child.root_id, child.lft, child.rgt]
77 77 end
78 78
79 79 def test_move_a_child_to_root
80 80 parent1 = Issue.generate!
81 81 parent2 = Issue.generate!
82 82 child = Issue.generate!(:parent_issue_id => parent1.id)
83 83
84 84 child.parent_issue_id = nil
85 85 child.save!
86 86 child.reload
87 87 parent1.reload
88 88 parent2.reload
89 89
90 90 assert_equal [parent1.id, 1, 2], [parent1.root_id, parent1.lft, parent1.rgt]
91 91 assert_equal [parent2.id, 1, 2], [parent2.root_id, parent2.lft, parent2.rgt]
92 92 assert_equal [child.id, 1, 2], [child.root_id, child.lft, child.rgt]
93 93 end
94 94
95 95 def test_move_a_child_to_another_issue
96 96 parent1 = Issue.generate!
97 97 parent2 = Issue.generate!
98 98 child = Issue.generate!(:parent_issue_id => parent1.id)
99 99
100 100 child.parent_issue_id = parent2.id
101 101 child.save!
102 102 child.reload
103 103 parent1.reload
104 104 parent2.reload
105 105
106 106 assert_equal [parent1.id, 1, 2], [parent1.root_id, parent1.lft, parent1.rgt]
107 107 assert_equal [parent2.id, 1, 4], [parent2.root_id, parent2.lft, parent2.rgt]
108 108 assert_equal [parent2.id, 2, 3], [child.root_id, child.lft, child.rgt]
109 109 end
110 110
111 111 def test_move_a_child_with_descendants_to_another_issue
112 112 parent1 = Issue.generate!
113 113 parent2 = Issue.generate!
114 114 child = Issue.generate!(:parent_issue_id => parent1.id)
115 115 grandchild = Issue.generate!(:parent_issue_id => child.id)
116 116
117 117 parent1.reload
118 118 parent2.reload
119 119 child.reload
120 120 grandchild.reload
121 121
122 122 assert_equal [parent1.id, 1, 6], [parent1.root_id, parent1.lft, parent1.rgt]
123 123 assert_equal [parent2.id, 1, 2], [parent2.root_id, parent2.lft, parent2.rgt]
124 124 assert_equal [parent1.id, 2, 5], [child.root_id, child.lft, child.rgt]
125 125 assert_equal [parent1.id, 3, 4], [grandchild.root_id, grandchild.lft, grandchild.rgt]
126 126
127 127 child.reload.parent_issue_id = parent2.id
128 128 child.save!
129 129 child.reload
130 130 grandchild.reload
131 131 parent1.reload
132 132 parent2.reload
133 133
134 134 assert_equal [parent1.id, 1, 2], [parent1.root_id, parent1.lft, parent1.rgt]
135 135 assert_equal [parent2.id, 1, 6], [parent2.root_id, parent2.lft, parent2.rgt]
136 136 assert_equal [parent2.id, 2, 5], [child.root_id, child.lft, child.rgt]
137 137 assert_equal [parent2.id, 3, 4], [grandchild.root_id, grandchild.lft, grandchild.rgt]
138 138 end
139 139
140 140 def test_move_a_child_with_descendants_to_another_project
141 141 parent1 = Issue.generate!
142 142 child = Issue.generate!(:parent_issue_id => parent1.id)
143 143 grandchild = Issue.generate!(:parent_issue_id => child.id)
144 144
145 145 child.reload
146 146 child.project = Project.find(2)
147 147 assert child.save
148 148 child.reload
149 149 grandchild.reload
150 150 parent1.reload
151 151
152 152 assert_equal [1, parent1.id, 1, 2], [parent1.project_id, parent1.root_id, parent1.lft, parent1.rgt]
153 153 assert_equal [2, child.id, 1, 4], [child.project_id, child.root_id, child.lft, child.rgt]
154 154 assert_equal [2, child.id, 2, 3], [grandchild.project_id, grandchild.root_id, grandchild.lft, grandchild.rgt]
155 155 end
156 156
157 157 def test_moving_an_issue_to_a_descendant_should_not_validate
158 158 parent1 = Issue.generate!
159 159 parent2 = Issue.generate!
160 160 child = Issue.generate!(:parent_issue_id => parent1.id)
161 161 grandchild = Issue.generate!(:parent_issue_id => child.id)
162 162
163 163 child.reload
164 164 child.parent_issue_id = grandchild.id
165 165 assert !child.save
166 assert_not_nil child.errors[:parent_issue_id]
166 assert_not_equal [], child.errors[:parent_issue_id]
167 167 end
168 168
169 169 def test_destroy_should_destroy_children
170 170 issue1 = Issue.generate!
171 171 issue2 = Issue.generate!
172 172 issue3 = Issue.generate!(:parent_issue_id => issue2.id)
173 173 issue4 = Issue.generate!(:parent_issue_id => issue1.id)
174 174
175 175 issue3.init_journal(User.find(2))
176 176 issue3.subject = 'child with journal'
177 177 issue3.save!
178 178
179 179 assert_difference 'Issue.count', -2 do
180 180 assert_difference 'Journal.count', -1 do
181 181 assert_difference 'JournalDetail.count', -1 do
182 182 Issue.find(issue2.id).destroy
183 183 end
184 184 end
185 185 end
186 186
187 187 issue1.reload
188 188 issue4.reload
189 189 assert !Issue.exists?(issue2.id)
190 190 assert !Issue.exists?(issue3.id)
191 191 assert_equal [issue1.id, 1, 4], [issue1.root_id, issue1.lft, issue1.rgt]
192 192 assert_equal [issue1.id, 2, 3], [issue4.root_id, issue4.lft, issue4.rgt]
193 193 end
194 194
195 195 def test_destroy_child_should_update_parent
196 196 issue = Issue.generate!
197 197 child1 = Issue.generate!(:parent_issue_id => issue.id)
198 198 child2 = Issue.generate!(:parent_issue_id => issue.id)
199 199
200 200 issue.reload
201 201 assert_equal [issue.id, 1, 6], [issue.root_id, issue.lft, issue.rgt]
202 202
203 203 child2.reload.destroy
204 204
205 205 issue.reload
206 206 assert_equal [issue.id, 1, 4], [issue.root_id, issue.lft, issue.rgt]
207 207 end
208 208
209 209 def test_destroy_parent_issue_updated_during_children_destroy
210 210 parent = Issue.generate!
211 211 Issue.generate!(:start_date => Date.today, :parent_issue_id => parent.id)
212 212 Issue.generate!(:start_date => 2.days.from_now, :parent_issue_id => parent.id)
213 213
214 214 assert_difference 'Issue.count', -3 do
215 215 Issue.find(parent.id).destroy
216 216 end
217 217 end
218 218
219 219 def test_destroy_child_issue_with_children
220 220 root = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'root')
221 221 child = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => root.id)
222 222 leaf = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'leaf', :parent_issue_id => child.id)
223 223 leaf.init_journal(User.find(2))
224 224 leaf.subject = 'leaf with journal'
225 225 leaf.save!
226 226
227 227 assert_difference 'Issue.count', -2 do
228 228 assert_difference 'Journal.count', -1 do
229 229 assert_difference 'JournalDetail.count', -1 do
230 230 Issue.find(child.id).destroy
231 231 end
232 232 end
233 233 end
234 234
235 235 root = Issue.find(root.id)
236 236 assert root.leaf?, "Root issue is not a leaf (lft: #{root.lft}, rgt: #{root.rgt})"
237 237 end
238 238
239 239 def test_destroy_issue_with_grand_child
240 240 parent = Issue.generate!
241 241 issue = Issue.generate!(:parent_issue_id => parent.id)
242 242 child = Issue.generate!(:parent_issue_id => issue.id)
243 243 grandchild1 = Issue.generate!(:parent_issue_id => child.id)
244 244 grandchild2 = Issue.generate!(:parent_issue_id => child.id)
245 245
246 246 assert_difference 'Issue.count', -4 do
247 247 Issue.find(issue.id).destroy
248 248 parent.reload
249 249 assert_equal [1, 2], [parent.lft, parent.rgt]
250 250 end
251 251 end
252 252
253 253 def test_parent_priority_should_be_the_highest_child_priority
254 254 parent = Issue.generate!(:priority => IssuePriority.find_by_name('Normal'))
255 255 # Create children
256 256 child1 = Issue.generate!(:priority => IssuePriority.find_by_name('High'), :parent_issue_id => parent.id)
257 257 assert_equal 'High', parent.reload.priority.name
258 258 child2 = Issue.generate!(:priority => IssuePriority.find_by_name('Immediate'), :parent_issue_id => child1.id)
259 259 assert_equal 'Immediate', child1.reload.priority.name
260 260 assert_equal 'Immediate', parent.reload.priority.name
261 261 child3 = Issue.generate!(:priority => IssuePriority.find_by_name('Low'), :parent_issue_id => parent.id)
262 262 assert_equal 'Immediate', parent.reload.priority.name
263 263 # Destroy a child
264 264 child1.destroy
265 265 assert_equal 'Low', parent.reload.priority.name
266 266 # Update a child
267 267 child3.reload.priority = IssuePriority.find_by_name('Normal')
268 268 child3.save!
269 269 assert_equal 'Normal', parent.reload.priority.name
270 270 end
271 271
272 272 def test_parent_dates_should_be_lowest_start_and_highest_due_dates
273 273 parent = Issue.generate!
274 274 Issue.generate!(:start_date => '2010-01-25', :due_date => '2010-02-15', :parent_issue_id => parent.id)
275 275 Issue.generate!( :due_date => '2010-02-13', :parent_issue_id => parent.id)
276 276 Issue.generate!(:start_date => '2010-02-01', :due_date => '2010-02-22', :parent_issue_id => parent.id)
277 277 parent.reload
278 278 assert_equal Date.parse('2010-01-25'), parent.start_date
279 279 assert_equal Date.parse('2010-02-22'), parent.due_date
280 280 end
281 281
282 282 def test_parent_done_ratio_should_be_average_done_ratio_of_leaves
283 283 parent = Issue.generate!
284 284 Issue.generate!(:done_ratio => 20, :parent_issue_id => parent.id)
285 285 assert_equal 20, parent.reload.done_ratio
286 286 Issue.generate!(:done_ratio => 70, :parent_issue_id => parent.id)
287 287 assert_equal 45, parent.reload.done_ratio
288 288
289 289 child = Issue.generate!(:done_ratio => 0, :parent_issue_id => parent.id)
290 290 assert_equal 30, parent.reload.done_ratio
291 291
292 292 Issue.generate!(:done_ratio => 30, :parent_issue_id => child.id)
293 293 assert_equal 30, child.reload.done_ratio
294 294 assert_equal 40, parent.reload.done_ratio
295 295 end
296 296
297 297 def test_parent_done_ratio_should_be_weighted_by_estimated_times_if_any
298 298 parent = Issue.generate!
299 299 Issue.generate!(:estimated_hours => 10, :done_ratio => 20, :parent_issue_id => parent.id)
300 300 assert_equal 20, parent.reload.done_ratio
301 301 Issue.generate!(:estimated_hours => 20, :done_ratio => 50, :parent_issue_id => parent.id)
302 302 assert_equal (50 * 20 + 20 * 10) / 30, parent.reload.done_ratio
303 303 end
304 304
305 305 def test_parent_estimate_should_be_sum_of_leaves
306 306 parent = Issue.generate!
307 307 Issue.generate!(:estimated_hours => nil, :parent_issue_id => parent.id)
308 308 assert_equal nil, parent.reload.estimated_hours
309 309 Issue.generate!(:estimated_hours => 5, :parent_issue_id => parent.id)
310 310 assert_equal 5, parent.reload.estimated_hours
311 311 Issue.generate!(:estimated_hours => 7, :parent_issue_id => parent.id)
312 312 assert_equal 12, parent.reload.estimated_hours
313 313 end
314 314
315 315 def test_move_parent_updates_old_parent_attributes
316 316 first_parent = Issue.generate!
317 317 second_parent = Issue.generate!
318 318 child = Issue.generate!(:estimated_hours => 5, :parent_issue_id => first_parent.id)
319 319 assert_equal 5, first_parent.reload.estimated_hours
320 320 child.update_attributes(:estimated_hours => 7, :parent_issue_id => second_parent.id)
321 321 assert_equal 7, second_parent.reload.estimated_hours
322 322 assert_nil first_parent.reload.estimated_hours
323 323 end
324 324
325 325 def test_reschuling_a_parent_should_reschedule_subtasks
326 326 parent = Issue.generate!
327 327 c1 = Issue.generate!(:start_date => '2010-05-12', :due_date => '2010-05-18', :parent_issue_id => parent.id)
328 328 c2 = Issue.generate!(:start_date => '2010-06-03', :due_date => '2010-06-10', :parent_issue_id => parent.id)
329 329 parent.reload
330 330 parent.reschedule_on!(Date.parse('2010-06-02'))
331 331 c1.reload
332 332 assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-08')], [c1.start_date, c1.due_date]
333 333 c2.reload
334 334 assert_equal [Date.parse('2010-06-03'), Date.parse('2010-06-10')], [c2.start_date, c2.due_date] # no change
335 335 parent.reload
336 336 assert_equal [Date.parse('2010-06-02'), Date.parse('2010-06-10')], [parent.start_date, parent.due_date]
337 337 end
338 338
339 339 def test_project_copy_should_copy_issue_tree
340 340 p = Project.create!(:name => 'Tree copy', :identifier => 'tree-copy', :tracker_ids => [1, 2])
341 341 i1 = Issue.generate!(:project => p, :subject => 'i1')
342 342 i2 = Issue.generate!(:project => p, :subject => 'i2', :parent_issue_id => i1.id)
343 343 i3 = Issue.generate!(:project => p, :subject => 'i3', :parent_issue_id => i1.id)
344 344 i4 = Issue.generate!(:project => p, :subject => 'i4', :parent_issue_id => i2.id)
345 345 i5 = Issue.generate!(:project => p, :subject => 'i5')
346 346 c = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1, 2])
347 347 c.copy(p, :only => 'issues')
348 348 c.reload
349 349
350 350 assert_equal 5, c.issues.count
351 351 ic1, ic2, ic3, ic4, ic5 = c.issues.order('subject').all
352 352 assert ic1.root?
353 353 assert_equal ic1, ic2.parent
354 354 assert_equal ic1, ic3.parent
355 355 assert_equal ic2, ic4.parent
356 356 assert ic5.root?
357 357 end
358 358 end
@@ -1,216 +1,216
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 IssueRelationTest < ActiveSupport::TestCase
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 :enabled_modules,
30 30 :enumerations,
31 31 :trackers,
32 32 :projects_trackers
33 33
34 34 include Redmine::I18n
35 35
36 36 def test_create
37 37 from = Issue.find(1)
38 38 to = Issue.find(2)
39 39
40 40 relation = IssueRelation.new :issue_from => from, :issue_to => to,
41 41 :relation_type => IssueRelation::TYPE_PRECEDES
42 42 assert relation.save
43 43 relation.reload
44 44 assert_equal IssueRelation::TYPE_PRECEDES, relation.relation_type
45 45 assert_equal from, relation.issue_from
46 46 assert_equal to, relation.issue_to
47 47 end
48 48
49 49 def test_create_minimum
50 50 relation = IssueRelation.new :issue_from => Issue.find(1), :issue_to => Issue.find(2)
51 51 assert relation.save
52 52 assert_equal IssueRelation::TYPE_RELATES, relation.relation_type
53 53 end
54 54
55 55 def test_follows_relation_should_be_reversed
56 56 from = Issue.find(1)
57 57 to = Issue.find(2)
58 58
59 59 relation = IssueRelation.new :issue_from => from, :issue_to => to,
60 60 :relation_type => IssueRelation::TYPE_FOLLOWS
61 61 assert relation.save
62 62 relation.reload
63 63 assert_equal IssueRelation::TYPE_PRECEDES, relation.relation_type
64 64 assert_equal to, relation.issue_from
65 65 assert_equal from, relation.issue_to
66 66 end
67 67
68 68 def test_follows_relation_should_not_be_reversed_if_validation_fails
69 69 from = Issue.find(1)
70 70 to = Issue.find(2)
71 71
72 72 relation = IssueRelation.new :issue_from => from, :issue_to => to,
73 73 :relation_type => IssueRelation::TYPE_FOLLOWS,
74 74 :delay => 'xx'
75 75 assert !relation.save
76 76 assert_equal IssueRelation::TYPE_FOLLOWS, relation.relation_type
77 77 assert_equal from, relation.issue_from
78 78 assert_equal to, relation.issue_to
79 79 end
80 80
81 81 def test_relation_type_for
82 82 from = Issue.find(1)
83 83 to = Issue.find(2)
84 84
85 85 relation = IssueRelation.new :issue_from => from, :issue_to => to,
86 86 :relation_type => IssueRelation::TYPE_PRECEDES
87 87 assert_equal IssueRelation::TYPE_PRECEDES, relation.relation_type_for(from)
88 88 assert_equal IssueRelation::TYPE_FOLLOWS, relation.relation_type_for(to)
89 89 end
90 90
91 91 def test_set_issue_to_dates_without_issue_to
92 92 r = IssueRelation.new(:issue_from => Issue.new(:start_date => Date.today),
93 93 :relation_type => IssueRelation::TYPE_PRECEDES,
94 94 :delay => 1)
95 95 assert_nil r.set_issue_to_dates
96 96 end
97 97
98 98 def test_set_issue_to_dates_without_issues
99 99 r = IssueRelation.new(:relation_type => IssueRelation::TYPE_PRECEDES, :delay => 1)
100 100 assert_nil r.set_issue_to_dates
101 101 end
102 102
103 103 def test_validates_circular_dependency
104 104 IssueRelation.delete_all
105 105 assert IssueRelation.create!(
106 106 :issue_from => Issue.find(1), :issue_to => Issue.find(2),
107 107 :relation_type => IssueRelation::TYPE_PRECEDES
108 108 )
109 109 assert IssueRelation.create!(
110 110 :issue_from => Issue.find(2), :issue_to => Issue.find(3),
111 111 :relation_type => IssueRelation::TYPE_PRECEDES
112 112 )
113 113 r = IssueRelation.new(
114 114 :issue_from => Issue.find(3), :issue_to => Issue.find(1),
115 115 :relation_type => IssueRelation::TYPE_PRECEDES
116 116 )
117 117 assert !r.save
118 assert_not_nil r.errors[:base]
118 assert_not_equal [], r.errors[:base]
119 119 end
120 120
121 121 def test_validates_circular_dependency_of_subtask
122 122 set_language_if_valid 'en'
123 123 issue1 = Issue.generate!
124 124 issue2 = Issue.generate!
125 125 IssueRelation.create!(
126 126 :issue_from => issue1, :issue_to => issue2,
127 127 :relation_type => IssueRelation::TYPE_PRECEDES
128 128 )
129 129 child = Issue.generate!(:parent_issue_id => issue2.id)
130 130 issue1.reload
131 131 child.reload
132 132
133 133 r = IssueRelation.new(
134 134 :issue_from => child, :issue_to => issue1,
135 135 :relation_type => IssueRelation::TYPE_PRECEDES
136 136 )
137 137 assert !r.save
138 138 assert_include 'This relation would create a circular dependency', r.errors.full_messages
139 139 end
140 140
141 141 def test_subtasks_should_allow_precedes_relation
142 142 parent = Issue.generate!
143 143 child1 = Issue.generate!(:parent_issue_id => parent.id)
144 144 child2 = Issue.generate!(:parent_issue_id => parent.id)
145 145
146 146 r = IssueRelation.new(
147 147 :issue_from => child1, :issue_to => child2,
148 148 :relation_type => IssueRelation::TYPE_PRECEDES
149 149 )
150 150 assert r.valid?
151 151 assert r.save
152 152 end
153 153
154 154 def test_validates_circular_dependency_on_reverse_relations
155 155 IssueRelation.delete_all
156 156 assert IssueRelation.create!(
157 157 :issue_from => Issue.find(1), :issue_to => Issue.find(3),
158 158 :relation_type => IssueRelation::TYPE_BLOCKS
159 159 )
160 160 assert IssueRelation.create!(
161 161 :issue_from => Issue.find(1), :issue_to => Issue.find(2),
162 162 :relation_type => IssueRelation::TYPE_BLOCKED
163 163 )
164 164 r = IssueRelation.new(
165 165 :issue_from => Issue.find(2), :issue_to => Issue.find(1),
166 166 :relation_type => IssueRelation::TYPE_BLOCKED
167 167 )
168 168 assert !r.save
169 assert_not_nil r.errors[:base]
169 assert_not_equal [], r.errors[:base]
170 170 end
171 171
172 172 def test_create_should_make_journal_entry
173 173 from = Issue.find(1)
174 174 to = Issue.find(2)
175 175 from_journals = from.journals.size
176 176 to_journals = to.journals.size
177 177 relation = IssueRelation.new(:issue_from => from, :issue_to => to,
178 178 :relation_type => IssueRelation::TYPE_PRECEDES)
179 179 assert relation.save
180 180 from.reload
181 181 to.reload
182 182 relation.reload
183 183 assert_equal from.journals.size, (from_journals + 1)
184 184 assert_equal to.journals.size, (to_journals + 1)
185 185 assert_equal 'relation', from.journals.last.details.last.property
186 186 assert_equal 'label_precedes', from.journals.last.details.last.prop_key
187 187 assert_equal '2', from.journals.last.details.last.value
188 188 assert_nil from.journals.last.details.last.old_value
189 189 assert_equal 'relation', to.journals.last.details.last.property
190 190 assert_equal 'label_follows', to.journals.last.details.last.prop_key
191 191 assert_equal '1', to.journals.last.details.last.value
192 192 assert_nil to.journals.last.details.last.old_value
193 193 end
194 194
195 195 def test_delete_should_make_journal_entry
196 196 relation = IssueRelation.find(1)
197 197 id = relation.id
198 198 from = relation.issue_from
199 199 to = relation.issue_to
200 200 from_journals = from.journals.size
201 201 to_journals = to.journals.size
202 202 assert relation.destroy
203 203 from.reload
204 204 to.reload
205 205 assert_equal from.journals.size, (from_journals + 1)
206 206 assert_equal to.journals.size, (to_journals + 1)
207 207 assert_equal 'relation', from.journals.last.details.last.property
208 208 assert_equal 'label_blocks', from.journals.last.details.last.prop_key
209 209 assert_equal '9', from.journals.last.details.last.old_value
210 210 assert_nil from.journals.last.details.last.value
211 211 assert_equal 'relation', to.journals.last.details.last.property
212 212 assert_equal 'label_blocked_by', to.journals.last.details.last.prop_key
213 213 assert_equal '10', to.journals.last.details.last.old_value
214 214 assert_nil to.journals.last.details.last.value
215 215 end
216 216 end
@@ -1,2290 +1,2290
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 IssueTest < ActiveSupport::TestCase
21 21 fixtures :projects, :users, :members, :member_roles, :roles,
22 22 :groups_users,
23 23 :trackers, :projects_trackers,
24 24 :enabled_modules,
25 25 :versions,
26 26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 27 :enumerations,
28 28 :issues, :journals, :journal_details,
29 29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 30 :time_entries
31 31
32 32 include Redmine::I18n
33 33
34 34 def teardown
35 35 User.current = nil
36 36 end
37 37
38 38 def test_initialize
39 39 issue = Issue.new
40 40
41 41 assert_nil issue.project_id
42 42 assert_nil issue.tracker_id
43 43 assert_nil issue.author_id
44 44 assert_nil issue.assigned_to_id
45 45 assert_nil issue.category_id
46 46
47 47 assert_equal IssueStatus.default, issue.status
48 48 assert_equal IssuePriority.default, issue.priority
49 49 end
50 50
51 51 def test_create
52 52 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
53 53 :status_id => 1, :priority => IssuePriority.all.first,
54 54 :subject => 'test_create',
55 55 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
56 56 assert issue.save
57 57 issue.reload
58 58 assert_equal 1.5, issue.estimated_hours
59 59 end
60 60
61 61 def test_create_minimal
62 62 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
63 63 :status_id => 1, :priority => IssuePriority.all.first,
64 64 :subject => 'test_create')
65 65 assert issue.save
66 66 assert issue.description.nil?
67 67 assert_nil issue.estimated_hours
68 68 end
69 69
70 70 def test_start_date_format_should_be_validated
71 71 set_language_if_valid 'en'
72 72 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
73 73 issue = Issue.new(:start_date => invalid_date)
74 74 assert !issue.valid?
75 75 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
76 76 end
77 77 end
78 78
79 79 def test_due_date_format_should_be_validated
80 80 set_language_if_valid 'en'
81 81 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
82 82 issue = Issue.new(:due_date => invalid_date)
83 83 assert !issue.valid?
84 84 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
85 85 end
86 86 end
87 87
88 88 def test_due_date_lesser_than_start_date_should_not_validate
89 89 set_language_if_valid 'en'
90 90 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
91 91 assert !issue.valid?
92 92 assert_include 'Due date must be greater than start date', issue.errors.full_messages
93 93 end
94 94
95 95 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
96 96 issue = Issue.generate(:start_date => '2013-06-04')
97 97 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
98 98 assert !issue.valid?
99 99 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
100 100 end
101 101
102 102 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
103 103 issue = Issue.generate!(:start_date => '2013-06-04')
104 104 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
105 105 issue.start_date = '2013-06-07'
106 106 assert !issue.valid?
107 107 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
108 108 end
109 109
110 110 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
111 111 issue = Issue.generate!(:start_date => '2013-06-04')
112 112 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
113 113 assert issue.valid?
114 114 end
115 115
116 116 def test_estimated_hours_should_be_validated
117 117 set_language_if_valid 'en'
118 118 ['-2'].each do |invalid|
119 119 issue = Issue.new(:estimated_hours => invalid)
120 120 assert !issue.valid?
121 121 assert_include 'Estimated time is invalid', issue.errors.full_messages
122 122 end
123 123 end
124 124
125 125 def test_create_with_required_custom_field
126 126 set_language_if_valid 'en'
127 127 field = IssueCustomField.find_by_name('Database')
128 128 field.update_attribute(:is_required, true)
129 129
130 130 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
131 131 :status_id => 1, :subject => 'test_create',
132 132 :description => 'IssueTest#test_create_with_required_custom_field')
133 133 assert issue.available_custom_fields.include?(field)
134 134 # No value for the custom field
135 135 assert !issue.save
136 136 assert_equal ["Database can't be blank"], issue.errors.full_messages
137 137 # Blank value
138 138 issue.custom_field_values = { field.id => '' }
139 139 assert !issue.save
140 140 assert_equal ["Database can't be blank"], issue.errors.full_messages
141 141 # Invalid value
142 142 issue.custom_field_values = { field.id => 'SQLServer' }
143 143 assert !issue.save
144 144 assert_equal ["Database is not included in the list"], issue.errors.full_messages
145 145 # Valid value
146 146 issue.custom_field_values = { field.id => 'PostgreSQL' }
147 147 assert issue.save
148 148 issue.reload
149 149 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
150 150 end
151 151
152 152 def test_create_with_group_assignment
153 153 with_settings :issue_group_assignment => '1' do
154 154 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
155 155 :subject => 'Group assignment',
156 156 :assigned_to_id => 11).save
157 157 issue = Issue.first(:order => 'id DESC')
158 158 assert_kind_of Group, issue.assigned_to
159 159 assert_equal Group.find(11), issue.assigned_to
160 160 end
161 161 end
162 162
163 163 def test_create_with_parent_issue_id
164 164 issue = Issue.new(:project_id => 1, :tracker_id => 1,
165 165 :author_id => 1, :subject => 'Group assignment',
166 166 :parent_issue_id => 1)
167 167 assert_save issue
168 168 assert_equal 1, issue.parent_issue_id
169 169 assert_equal Issue.find(1), issue.parent
170 170 end
171 171
172 172 def test_create_with_sharp_parent_issue_id
173 173 issue = Issue.new(:project_id => 1, :tracker_id => 1,
174 174 :author_id => 1, :subject => 'Group assignment',
175 175 :parent_issue_id => "#1")
176 176 assert_save issue
177 177 assert_equal 1, issue.parent_issue_id
178 178 assert_equal Issue.find(1), issue.parent
179 179 end
180 180
181 181 def test_create_with_invalid_parent_issue_id
182 182 set_language_if_valid 'en'
183 183 issue = Issue.new(:project_id => 1, :tracker_id => 1,
184 184 :author_id => 1, :subject => 'Group assignment',
185 185 :parent_issue_id => '01ABC')
186 186 assert !issue.save
187 187 assert_equal '01ABC', issue.parent_issue_id
188 188 assert_include 'Parent task is invalid', issue.errors.full_messages
189 189 end
190 190
191 191 def test_create_with_invalid_sharp_parent_issue_id
192 192 set_language_if_valid 'en'
193 193 issue = Issue.new(:project_id => 1, :tracker_id => 1,
194 194 :author_id => 1, :subject => 'Group assignment',
195 195 :parent_issue_id => '#01ABC')
196 196 assert !issue.save
197 197 assert_equal '#01ABC', issue.parent_issue_id
198 198 assert_include 'Parent task is invalid', issue.errors.full_messages
199 199 end
200 200
201 201 def assert_visibility_match(user, issues)
202 202 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
203 203 end
204 204
205 205 def test_visible_scope_for_anonymous
206 206 # Anonymous user should see issues of public projects only
207 207 issues = Issue.visible(User.anonymous).all
208 208 assert issues.any?
209 209 assert_nil issues.detect {|issue| !issue.project.is_public?}
210 210 assert_nil issues.detect {|issue| issue.is_private?}
211 211 assert_visibility_match User.anonymous, issues
212 212 end
213 213
214 214 def test_visible_scope_for_anonymous_without_view_issues_permissions
215 215 # Anonymous user should not see issues without permission
216 216 Role.anonymous.remove_permission!(:view_issues)
217 217 issues = Issue.visible(User.anonymous).all
218 218 assert issues.empty?
219 219 assert_visibility_match User.anonymous, issues
220 220 end
221 221
222 222 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
223 223 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
224 224 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
225 225 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
226 226 assert !issue.visible?(User.anonymous)
227 227 end
228 228
229 229 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
230 230 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
231 231 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
232 232 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
233 233 assert !issue.visible?(User.anonymous)
234 234 end
235 235
236 236 def test_visible_scope_for_non_member
237 237 user = User.find(9)
238 238 assert user.projects.empty?
239 239 # Non member user should see issues of public projects only
240 240 issues = Issue.visible(user).all
241 241 assert issues.any?
242 242 assert_nil issues.detect {|issue| !issue.project.is_public?}
243 243 assert_nil issues.detect {|issue| issue.is_private?}
244 244 assert_visibility_match user, issues
245 245 end
246 246
247 247 def test_visible_scope_for_non_member_with_own_issues_visibility
248 248 Role.non_member.update_attribute :issues_visibility, 'own'
249 249 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
250 250 user = User.find(9)
251 251
252 252 issues = Issue.visible(user).all
253 253 assert issues.any?
254 254 assert_nil issues.detect {|issue| issue.author != user}
255 255 assert_visibility_match user, issues
256 256 end
257 257
258 258 def test_visible_scope_for_non_member_without_view_issues_permissions
259 259 # Non member user should not see issues without permission
260 260 Role.non_member.remove_permission!(:view_issues)
261 261 user = User.find(9)
262 262 assert user.projects.empty?
263 263 issues = Issue.visible(user).all
264 264 assert issues.empty?
265 265 assert_visibility_match user, issues
266 266 end
267 267
268 268 def test_visible_scope_for_member
269 269 user = User.find(9)
270 270 # User should see issues of projects for which user has view_issues permissions only
271 271 Role.non_member.remove_permission!(:view_issues)
272 272 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
273 273 issues = Issue.visible(user).all
274 274 assert issues.any?
275 275 assert_nil issues.detect {|issue| issue.project_id != 3}
276 276 assert_nil issues.detect {|issue| issue.is_private?}
277 277 assert_visibility_match user, issues
278 278 end
279 279
280 280 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
281 281 user = User.find(8)
282 282 assert user.groups.any?
283 283 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
284 284 Role.non_member.remove_permission!(:view_issues)
285 285
286 286 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
287 287 :status_id => 1, :priority => IssuePriority.all.first,
288 288 :subject => 'Assignment test',
289 289 :assigned_to => user.groups.first,
290 290 :is_private => true)
291 291
292 292 Role.find(2).update_attribute :issues_visibility, 'default'
293 293 issues = Issue.visible(User.find(8)).all
294 294 assert issues.any?
295 295 assert issues.include?(issue)
296 296
297 297 Role.find(2).update_attribute :issues_visibility, 'own'
298 298 issues = Issue.visible(User.find(8)).all
299 299 assert issues.any?
300 300 assert issues.include?(issue)
301 301 end
302 302
303 303 def test_visible_scope_for_admin
304 304 user = User.find(1)
305 305 user.members.each(&:destroy)
306 306 assert user.projects.empty?
307 307 issues = Issue.visible(user).all
308 308 assert issues.any?
309 309 # Admin should see issues on private projects that admin does not belong to
310 310 assert issues.detect {|issue| !issue.project.is_public?}
311 311 # Admin should see private issues of other users
312 312 assert issues.detect {|issue| issue.is_private? && issue.author != user}
313 313 assert_visibility_match user, issues
314 314 end
315 315
316 316 def test_visible_scope_with_project
317 317 project = Project.find(1)
318 318 issues = Issue.visible(User.find(2), :project => project).all
319 319 projects = issues.collect(&:project).uniq
320 320 assert_equal 1, projects.size
321 321 assert_equal project, projects.first
322 322 end
323 323
324 324 def test_visible_scope_with_project_and_subprojects
325 325 project = Project.find(1)
326 326 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
327 327 projects = issues.collect(&:project).uniq
328 328 assert projects.size > 1
329 329 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
330 330 end
331 331
332 332 def test_visible_and_nested_set_scopes
333 333 assert_equal 0, Issue.find(1).descendants.visible.all.size
334 334 end
335 335
336 336 def test_open_scope
337 337 issues = Issue.open.all
338 338 assert_nil issues.detect(&:closed?)
339 339 end
340 340
341 341 def test_open_scope_with_arg
342 342 issues = Issue.open(false).all
343 343 assert_equal issues, issues.select(&:closed?)
344 344 end
345 345
346 346 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
347 347 version = Version.find(2)
348 348 assert version.fixed_issues.any?
349 349 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
350 350 end
351 351
352 352 def test_fixed_version_scope_with_empty_array_should_return_no_result
353 353 assert_equal 0, Issue.fixed_version([]).count
354 354 end
355 355
356 356 def test_errors_full_messages_should_include_custom_fields_errors
357 357 field = IssueCustomField.find_by_name('Database')
358 358
359 359 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
360 360 :status_id => 1, :subject => 'test_create',
361 361 :description => 'IssueTest#test_create_with_required_custom_field')
362 362 assert issue.available_custom_fields.include?(field)
363 363 # Invalid value
364 364 issue.custom_field_values = { field.id => 'SQLServer' }
365 365
366 366 assert !issue.valid?
367 367 assert_equal 1, issue.errors.full_messages.size
368 368 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
369 369 issue.errors.full_messages.first
370 370 end
371 371
372 372 def test_update_issue_with_required_custom_field
373 373 field = IssueCustomField.find_by_name('Database')
374 374 field.update_attribute(:is_required, true)
375 375
376 376 issue = Issue.find(1)
377 377 assert_nil issue.custom_value_for(field)
378 378 assert issue.available_custom_fields.include?(field)
379 379 # No change to custom values, issue can be saved
380 380 assert issue.save
381 381 # Blank value
382 382 issue.custom_field_values = { field.id => '' }
383 383 assert !issue.save
384 384 # Valid value
385 385 issue.custom_field_values = { field.id => 'PostgreSQL' }
386 386 assert issue.save
387 387 issue.reload
388 388 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
389 389 end
390 390
391 391 def test_should_not_update_attributes_if_custom_fields_validation_fails
392 392 issue = Issue.find(1)
393 393 field = IssueCustomField.find_by_name('Database')
394 394 assert issue.available_custom_fields.include?(field)
395 395
396 396 issue.custom_field_values = { field.id => 'Invalid' }
397 397 issue.subject = 'Should be not be saved'
398 398 assert !issue.save
399 399
400 400 issue.reload
401 401 assert_equal "Can't print recipes", issue.subject
402 402 end
403 403
404 404 def test_should_not_recreate_custom_values_objects_on_update
405 405 field = IssueCustomField.find_by_name('Database')
406 406
407 407 issue = Issue.find(1)
408 408 issue.custom_field_values = { field.id => 'PostgreSQL' }
409 409 assert issue.save
410 410 custom_value = issue.custom_value_for(field)
411 411 issue.reload
412 412 issue.custom_field_values = { field.id => 'MySQL' }
413 413 assert issue.save
414 414 issue.reload
415 415 assert_equal custom_value.id, issue.custom_value_for(field).id
416 416 end
417 417
418 418 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
419 419 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
420 420 :status_id => 1, :subject => 'Test',
421 421 :custom_field_values => {'2' => 'Test'})
422 422 assert !Tracker.find(2).custom_field_ids.include?(2)
423 423
424 424 issue = Issue.find(issue.id)
425 425 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
426 426
427 427 issue = Issue.find(issue.id)
428 428 custom_value = issue.custom_value_for(2)
429 429 assert_not_nil custom_value
430 430 assert_equal 'Test', custom_value.value
431 431 end
432 432
433 433 def test_assigning_tracker_id_should_reload_custom_fields_values
434 434 issue = Issue.new(:project => Project.find(1))
435 435 assert issue.custom_field_values.empty?
436 436 issue.tracker_id = 1
437 437 assert issue.custom_field_values.any?
438 438 end
439 439
440 440 def test_assigning_attributes_should_assign_project_and_tracker_first
441 441 seq = sequence('seq')
442 442 issue = Issue.new
443 443 issue.expects(:project_id=).in_sequence(seq)
444 444 issue.expects(:tracker_id=).in_sequence(seq)
445 445 issue.expects(:subject=).in_sequence(seq)
446 446 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
447 447 end
448 448
449 449 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
450 450 attributes = ActiveSupport::OrderedHash.new
451 451 attributes['custom_field_values'] = { '1' => 'MySQL' }
452 452 attributes['tracker_id'] = '1'
453 453 issue = Issue.new(:project => Project.find(1))
454 454 issue.attributes = attributes
455 455 assert_equal 'MySQL', issue.custom_field_value(1)
456 456 end
457 457
458 458 def test_reload_should_reload_custom_field_values
459 459 issue = Issue.generate!
460 460 issue.custom_field_values = {'2' => 'Foo'}
461 461 issue.save!
462 462
463 463 issue = Issue.order('id desc').first
464 464 assert_equal 'Foo', issue.custom_field_value(2)
465 465
466 466 issue.custom_field_values = {'2' => 'Bar'}
467 467 assert_equal 'Bar', issue.custom_field_value(2)
468 468
469 469 issue.reload
470 470 assert_equal 'Foo', issue.custom_field_value(2)
471 471 end
472 472
473 473 def test_should_update_issue_with_disabled_tracker
474 474 p = Project.find(1)
475 475 issue = Issue.find(1)
476 476
477 477 p.trackers.delete(issue.tracker)
478 478 assert !p.trackers.include?(issue.tracker)
479 479
480 480 issue.reload
481 481 issue.subject = 'New subject'
482 482 assert issue.save
483 483 end
484 484
485 485 def test_should_not_set_a_disabled_tracker
486 486 p = Project.find(1)
487 487 p.trackers.delete(Tracker.find(2))
488 488
489 489 issue = Issue.find(1)
490 490 issue.tracker_id = 2
491 491 issue.subject = 'New subject'
492 492 assert !issue.save
493 assert_not_nil issue.errors[:tracker_id]
493 assert_not_equal [], issue.errors[:tracker_id]
494 494 end
495 495
496 496 def test_category_based_assignment
497 497 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
498 498 :status_id => 1, :priority => IssuePriority.all.first,
499 499 :subject => 'Assignment test',
500 500 :description => 'Assignment test', :category_id => 1)
501 501 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
502 502 end
503 503
504 504 def test_new_statuses_allowed_to
505 505 WorkflowTransition.delete_all
506 506 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
507 507 :old_status_id => 1, :new_status_id => 2,
508 508 :author => false, :assignee => false)
509 509 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
510 510 :old_status_id => 1, :new_status_id => 3,
511 511 :author => true, :assignee => false)
512 512 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
513 513 :old_status_id => 1, :new_status_id => 4,
514 514 :author => false, :assignee => true)
515 515 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
516 516 :old_status_id => 1, :new_status_id => 5,
517 517 :author => true, :assignee => true)
518 518 status = IssueStatus.find(1)
519 519 role = Role.find(1)
520 520 tracker = Tracker.find(1)
521 521 user = User.find(2)
522 522
523 523 issue = Issue.generate!(:tracker => tracker, :status => status,
524 524 :project_id => 1, :author_id => 1)
525 525 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
526 526
527 527 issue = Issue.generate!(:tracker => tracker, :status => status,
528 528 :project_id => 1, :author => user)
529 529 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
530 530
531 531 issue = Issue.generate!(:tracker => tracker, :status => status,
532 532 :project_id => 1, :author_id => 1,
533 533 :assigned_to => user)
534 534 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
535 535
536 536 issue = Issue.generate!(:tracker => tracker, :status => status,
537 537 :project_id => 1, :author => user,
538 538 :assigned_to => user)
539 539 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
540 540
541 541 group = Group.generate!
542 542 group.users << user
543 543 issue = Issue.generate!(:tracker => tracker, :status => status,
544 544 :project_id => 1, :author => user,
545 545 :assigned_to => group)
546 546 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
547 547 end
548 548
549 549 def test_new_statuses_allowed_to_should_consider_group_assignment
550 550 WorkflowTransition.delete_all
551 551 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
552 552 :old_status_id => 1, :new_status_id => 4,
553 553 :author => false, :assignee => true)
554 554 user = User.find(2)
555 555 group = Group.generate!
556 556 group.users << user
557 557
558 558 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
559 559 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
560 560 end
561 561
562 562 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
563 563 admin = User.find(1)
564 564 issue = Issue.find(1)
565 565 assert !admin.member_of?(issue.project)
566 566 expected_statuses = [issue.status] +
567 567 WorkflowTransition.find_all_by_old_status_id(
568 568 issue.status_id).map(&:new_status).uniq.sort
569 569 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
570 570 end
571 571
572 572 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
573 573 issue = Issue.find(1).copy
574 574 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
575 575
576 576 issue = Issue.find(2).copy
577 577 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
578 578 end
579 579
580 580 def test_safe_attributes_names_should_not_include_disabled_field
581 581 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
582 582
583 583 issue = Issue.new(:tracker => tracker)
584 584 assert_include 'tracker_id', issue.safe_attribute_names
585 585 assert_include 'status_id', issue.safe_attribute_names
586 586 assert_include 'subject', issue.safe_attribute_names
587 587 assert_include 'description', issue.safe_attribute_names
588 588 assert_include 'custom_field_values', issue.safe_attribute_names
589 589 assert_include 'custom_fields', issue.safe_attribute_names
590 590 assert_include 'lock_version', issue.safe_attribute_names
591 591
592 592 tracker.core_fields.each do |field|
593 593 assert_include field, issue.safe_attribute_names
594 594 end
595 595
596 596 tracker.disabled_core_fields.each do |field|
597 597 assert_not_include field, issue.safe_attribute_names
598 598 end
599 599 end
600 600
601 601 def test_safe_attributes_should_ignore_disabled_fields
602 602 tracker = Tracker.find(1)
603 603 tracker.core_fields = %w(assigned_to_id due_date)
604 604 tracker.save!
605 605
606 606 issue = Issue.new(:tracker => tracker)
607 607 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
608 608 assert_nil issue.start_date
609 609 assert_equal Date.parse('2012-07-14'), issue.due_date
610 610 end
611 611
612 612 def test_safe_attributes_should_accept_target_tracker_enabled_fields
613 613 source = Tracker.find(1)
614 614 source.core_fields = []
615 615 source.save!
616 616 target = Tracker.find(2)
617 617 target.core_fields = %w(assigned_to_id due_date)
618 618 target.save!
619 619
620 620 issue = Issue.new(:tracker => source)
621 621 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
622 622 assert_equal target, issue.tracker
623 623 assert_equal Date.parse('2012-07-14'), issue.due_date
624 624 end
625 625
626 626 def test_safe_attributes_should_not_include_readonly_fields
627 627 WorkflowPermission.delete_all
628 628 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
629 629 :role_id => 1, :field_name => 'due_date',
630 630 :rule => 'readonly')
631 631 user = User.find(2)
632 632
633 633 issue = Issue.new(:project_id => 1, :tracker_id => 1)
634 634 assert_equal %w(due_date), issue.read_only_attribute_names(user)
635 635 assert_not_include 'due_date', issue.safe_attribute_names(user)
636 636
637 637 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
638 638 assert_equal Date.parse('2012-07-14'), issue.start_date
639 639 assert_nil issue.due_date
640 640 end
641 641
642 642 def test_safe_attributes_should_not_include_readonly_custom_fields
643 643 cf1 = IssueCustomField.create!(:name => 'Writable field',
644 644 :field_format => 'string',
645 645 :is_for_all => true, :tracker_ids => [1])
646 646 cf2 = IssueCustomField.create!(:name => 'Readonly field',
647 647 :field_format => 'string',
648 648 :is_for_all => true, :tracker_ids => [1])
649 649 WorkflowPermission.delete_all
650 650 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
651 651 :role_id => 1, :field_name => cf2.id.to_s,
652 652 :rule => 'readonly')
653 653 user = User.find(2)
654 654 issue = Issue.new(:project_id => 1, :tracker_id => 1)
655 655 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
656 656 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
657 657
658 658 issue.send :safe_attributes=, {'custom_field_values' => {
659 659 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
660 660 }}, user
661 661 assert_equal 'value1', issue.custom_field_value(cf1)
662 662 assert_nil issue.custom_field_value(cf2)
663 663
664 664 issue.send :safe_attributes=, {'custom_fields' => [
665 665 {'id' => cf1.id.to_s, 'value' => 'valuea'},
666 666 {'id' => cf2.id.to_s, 'value' => 'valueb'}
667 667 ]}, user
668 668 assert_equal 'valuea', issue.custom_field_value(cf1)
669 669 assert_nil issue.custom_field_value(cf2)
670 670 end
671 671
672 672 def test_editable_custom_field_values_should_return_non_readonly_custom_values
673 673 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
674 674 :is_for_all => true, :tracker_ids => [1, 2])
675 675 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
676 676 :is_for_all => true, :tracker_ids => [1, 2])
677 677 WorkflowPermission.delete_all
678 678 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
679 679 :field_name => cf2.id.to_s, :rule => 'readonly')
680 680 user = User.find(2)
681 681
682 682 issue = Issue.new(:project_id => 1, :tracker_id => 1)
683 683 values = issue.editable_custom_field_values(user)
684 684 assert values.detect {|value| value.custom_field == cf1}
685 685 assert_nil values.detect {|value| value.custom_field == cf2}
686 686
687 687 issue.tracker_id = 2
688 688 values = issue.editable_custom_field_values(user)
689 689 assert values.detect {|value| value.custom_field == cf1}
690 690 assert values.detect {|value| value.custom_field == cf2}
691 691 end
692 692
693 693 def test_safe_attributes_should_accept_target_tracker_writable_fields
694 694 WorkflowPermission.delete_all
695 695 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
696 696 :role_id => 1, :field_name => 'due_date',
697 697 :rule => 'readonly')
698 698 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
699 699 :role_id => 1, :field_name => 'start_date',
700 700 :rule => 'readonly')
701 701 user = User.find(2)
702 702
703 703 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
704 704
705 705 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
706 706 'due_date' => '2012-07-14'}, user
707 707 assert_equal Date.parse('2012-07-12'), issue.start_date
708 708 assert_nil issue.due_date
709 709
710 710 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
711 711 'due_date' => '2012-07-16',
712 712 'tracker_id' => 2}, user
713 713 assert_equal Date.parse('2012-07-12'), issue.start_date
714 714 assert_equal Date.parse('2012-07-16'), issue.due_date
715 715 end
716 716
717 717 def test_safe_attributes_should_accept_target_status_writable_fields
718 718 WorkflowPermission.delete_all
719 719 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
720 720 :role_id => 1, :field_name => 'due_date',
721 721 :rule => 'readonly')
722 722 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
723 723 :role_id => 1, :field_name => 'start_date',
724 724 :rule => 'readonly')
725 725 user = User.find(2)
726 726
727 727 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
728 728
729 729 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
730 730 'due_date' => '2012-07-14'},
731 731 user
732 732 assert_equal Date.parse('2012-07-12'), issue.start_date
733 733 assert_nil issue.due_date
734 734
735 735 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
736 736 'due_date' => '2012-07-16',
737 737 'status_id' => 2},
738 738 user
739 739 assert_equal Date.parse('2012-07-12'), issue.start_date
740 740 assert_equal Date.parse('2012-07-16'), issue.due_date
741 741 end
742 742
743 743 def test_required_attributes_should_be_validated
744 744 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
745 745 :is_for_all => true, :tracker_ids => [1, 2])
746 746
747 747 WorkflowPermission.delete_all
748 748 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
749 749 :role_id => 1, :field_name => 'due_date',
750 750 :rule => 'required')
751 751 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
752 752 :role_id => 1, :field_name => 'category_id',
753 753 :rule => 'required')
754 754 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
755 755 :role_id => 1, :field_name => cf.id.to_s,
756 756 :rule => 'required')
757 757
758 758 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
759 759 :role_id => 1, :field_name => 'start_date',
760 760 :rule => 'required')
761 761 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
762 762 :role_id => 1, :field_name => cf.id.to_s,
763 763 :rule => 'required')
764 764 user = User.find(2)
765 765
766 766 issue = Issue.new(:project_id => 1, :tracker_id => 1,
767 767 :status_id => 1, :subject => 'Required fields',
768 768 :author => user)
769 769 assert_equal [cf.id.to_s, "category_id", "due_date"],
770 770 issue.required_attribute_names(user).sort
771 771 assert !issue.save, "Issue was saved"
772 772 assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"],
773 773 issue.errors.full_messages.sort
774 774
775 775 issue.tracker_id = 2
776 776 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
777 777 assert !issue.save, "Issue was saved"
778 778 assert_equal ["Foo can't be blank", "Start date can't be blank"],
779 779 issue.errors.full_messages.sort
780 780
781 781 issue.start_date = Date.today
782 782 issue.custom_field_values = {cf.id.to_s => 'bar'}
783 783 assert issue.save
784 784 end
785 785
786 786 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
787 787 WorkflowPermission.delete_all
788 788 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
789 789 :role_id => 1, :field_name => 'due_date',
790 790 :rule => 'required')
791 791 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
792 792 :role_id => 1, :field_name => 'start_date',
793 793 :rule => 'required')
794 794 user = User.find(2)
795 795 member = Member.find(1)
796 796 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
797 797
798 798 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
799 799
800 800 member.role_ids = [1, 2]
801 801 member.save!
802 802 assert_equal [], issue.required_attribute_names(user.reload)
803 803
804 804 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
805 805 :role_id => 2, :field_name => 'due_date',
806 806 :rule => 'required')
807 807 assert_equal %w(due_date), issue.required_attribute_names(user)
808 808
809 809 member.role_ids = [1, 2, 3]
810 810 member.save!
811 811 assert_equal [], issue.required_attribute_names(user.reload)
812 812
813 813 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
814 814 :role_id => 2, :field_name => 'due_date',
815 815 :rule => 'readonly')
816 816 # required + readonly => required
817 817 assert_equal %w(due_date), issue.required_attribute_names(user)
818 818 end
819 819
820 820 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
821 821 WorkflowPermission.delete_all
822 822 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
823 823 :role_id => 1, :field_name => 'due_date',
824 824 :rule => 'readonly')
825 825 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
826 826 :role_id => 1, :field_name => 'start_date',
827 827 :rule => 'readonly')
828 828 user = User.find(2)
829 829 member = Member.find(1)
830 830 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
831 831
832 832 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
833 833
834 834 member.role_ids = [1, 2]
835 835 member.save!
836 836 assert_equal [], issue.read_only_attribute_names(user.reload)
837 837
838 838 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
839 839 :role_id => 2, :field_name => 'due_date',
840 840 :rule => 'readonly')
841 841 assert_equal %w(due_date), issue.read_only_attribute_names(user)
842 842 end
843 843
844 844 def test_copy
845 845 issue = Issue.new.copy_from(1)
846 846 assert issue.copy?
847 847 assert issue.save
848 848 issue.reload
849 849 orig = Issue.find(1)
850 850 assert_equal orig.subject, issue.subject
851 851 assert_equal orig.tracker, issue.tracker
852 852 assert_equal "125", issue.custom_value_for(2).value
853 853 end
854 854
855 855 def test_copy_should_copy_status
856 856 orig = Issue.find(8)
857 857 assert orig.status != IssueStatus.default
858 858
859 859 issue = Issue.new.copy_from(orig)
860 860 assert issue.save
861 861 issue.reload
862 862 assert_equal orig.status, issue.status
863 863 end
864 864
865 865 def test_copy_should_add_relation_with_copied_issue
866 866 copied = Issue.find(1)
867 867 issue = Issue.new.copy_from(copied)
868 868 assert issue.save
869 869 issue.reload
870 870
871 871 assert_equal 1, issue.relations.size
872 872 relation = issue.relations.first
873 873 assert_equal 'copied_to', relation.relation_type
874 874 assert_equal copied, relation.issue_from
875 875 assert_equal issue, relation.issue_to
876 876 end
877 877
878 878 def test_copy_should_copy_subtasks
879 879 issue = Issue.generate_with_descendants!
880 880
881 881 copy = issue.reload.copy
882 882 copy.author = User.find(7)
883 883 assert_difference 'Issue.count', 1+issue.descendants.count do
884 884 assert copy.save
885 885 end
886 886 copy.reload
887 887 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
888 888 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
889 889 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
890 890 assert_equal copy.author, child_copy.author
891 891 end
892 892
893 893 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
894 894 parent = Issue.generate!
895 895 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
896 896 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
897 897
898 898 copy = parent.reload.copy
899 899 copy.parent_issue_id = parent.id
900 900 copy.author = User.find(7)
901 901 assert_difference 'Issue.count', 3 do
902 902 assert copy.save
903 903 end
904 904 parent.reload
905 905 copy.reload
906 906 assert_equal parent, copy.parent
907 907 assert_equal 3, parent.children.count
908 908 assert_equal 5, parent.descendants.count
909 909 assert_equal 2, copy.children.count
910 910 assert_equal 2, copy.descendants.count
911 911 end
912 912
913 913 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
914 914 parent = Issue.generate!
915 915 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
916 916 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
917 917
918 918 copy = parent.reload.copy
919 919 copy.parent_issue_id = child1.id
920 920 copy.author = User.find(7)
921 921 assert_difference 'Issue.count', 3 do
922 922 assert copy.save
923 923 end
924 924 parent.reload
925 925 child1.reload
926 926 copy.reload
927 927 assert_equal child1, copy.parent
928 928 assert_equal 2, parent.children.count
929 929 assert_equal 5, parent.descendants.count
930 930 assert_equal 1, child1.children.count
931 931 assert_equal 3, child1.descendants.count
932 932 assert_equal 2, copy.children.count
933 933 assert_equal 2, copy.descendants.count
934 934 end
935 935
936 936 def test_copy_should_copy_subtasks_to_target_project
937 937 issue = Issue.generate_with_descendants!
938 938
939 939 copy = issue.copy(:project_id => 3)
940 940 assert_difference 'Issue.count', 1+issue.descendants.count do
941 941 assert copy.save
942 942 end
943 943 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
944 944 end
945 945
946 946 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
947 947 issue = Issue.generate_with_descendants!
948 948
949 949 copy = issue.reload.copy
950 950 assert_difference 'Issue.count', 1+issue.descendants.count do
951 951 assert copy.save
952 952 assert copy.save
953 953 end
954 954 end
955 955
956 956 def test_should_not_call_after_project_change_on_creation
957 957 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
958 958 :subject => 'Test', :author_id => 1)
959 959 issue.expects(:after_project_change).never
960 960 issue.save!
961 961 end
962 962
963 963 def test_should_not_call_after_project_change_on_update
964 964 issue = Issue.find(1)
965 965 issue.project = Project.find(1)
966 966 issue.subject = 'No project change'
967 967 issue.expects(:after_project_change).never
968 968 issue.save!
969 969 end
970 970
971 971 def test_should_call_after_project_change_on_project_change
972 972 issue = Issue.find(1)
973 973 issue.project = Project.find(2)
974 974 issue.expects(:after_project_change).once
975 975 issue.save!
976 976 end
977 977
978 978 def test_adding_journal_should_update_timestamp
979 979 issue = Issue.find(1)
980 980 updated_on_was = issue.updated_on
981 981
982 982 issue.init_journal(User.first, "Adding notes")
983 983 assert_difference 'Journal.count' do
984 984 assert issue.save
985 985 end
986 986 issue.reload
987 987
988 988 assert_not_equal updated_on_was, issue.updated_on
989 989 end
990 990
991 991 def test_should_close_duplicates
992 992 # Create 3 issues
993 993 issue1 = Issue.generate!
994 994 issue2 = Issue.generate!
995 995 issue3 = Issue.generate!
996 996
997 997 # 2 is a dupe of 1
998 998 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
999 999 :relation_type => IssueRelation::TYPE_DUPLICATES)
1000 1000 # And 3 is a dupe of 2
1001 1001 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1002 1002 :relation_type => IssueRelation::TYPE_DUPLICATES)
1003 1003 # And 3 is a dupe of 1 (circular duplicates)
1004 1004 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1005 1005 :relation_type => IssueRelation::TYPE_DUPLICATES)
1006 1006
1007 1007 assert issue1.reload.duplicates.include?(issue2)
1008 1008
1009 1009 # Closing issue 1
1010 1010 issue1.init_journal(User.first, "Closing issue1")
1011 1011 issue1.status = IssueStatus.where(:is_closed => true).first
1012 1012 assert issue1.save
1013 1013 # 2 and 3 should be also closed
1014 1014 assert issue2.reload.closed?
1015 1015 assert issue3.reload.closed?
1016 1016 end
1017 1017
1018 1018 def test_should_not_close_duplicated_issue
1019 1019 issue1 = Issue.generate!
1020 1020 issue2 = Issue.generate!
1021 1021
1022 1022 # 2 is a dupe of 1
1023 1023 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1024 1024 :relation_type => IssueRelation::TYPE_DUPLICATES)
1025 1025 # 2 is a dup of 1 but 1 is not a duplicate of 2
1026 1026 assert !issue2.reload.duplicates.include?(issue1)
1027 1027
1028 1028 # Closing issue 2
1029 1029 issue2.init_journal(User.first, "Closing issue2")
1030 1030 issue2.status = IssueStatus.where(:is_closed => true).first
1031 1031 assert issue2.save
1032 1032 # 1 should not be also closed
1033 1033 assert !issue1.reload.closed?
1034 1034 end
1035 1035
1036 1036 def test_assignable_versions
1037 1037 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1038 1038 :status_id => 1, :fixed_version_id => 1,
1039 1039 :subject => 'New issue')
1040 1040 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1041 1041 end
1042 1042
1043 1043 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1044 1044 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1045 1045 :status_id => 1, :fixed_version_id => 1,
1046 1046 :subject => 'New issue')
1047 1047 assert !issue.save
1048 assert_not_nil issue.errors[:fixed_version_id]
1048 assert_not_equal [], issue.errors[:fixed_version_id]
1049 1049 end
1050 1050
1051 1051 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1052 1052 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1053 1053 :status_id => 1, :fixed_version_id => 2,
1054 1054 :subject => 'New issue')
1055 1055 assert !issue.save
1056 assert_not_nil issue.errors[:fixed_version_id]
1056 assert_not_equal [], issue.errors[:fixed_version_id]
1057 1057 end
1058 1058
1059 1059 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1060 1060 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1061 1061 :status_id => 1, :fixed_version_id => 3,
1062 1062 :subject => 'New issue')
1063 1063 assert issue.save
1064 1064 end
1065 1065
1066 1066 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1067 1067 issue = Issue.find(11)
1068 1068 assert_equal 'closed', issue.fixed_version.status
1069 1069 issue.subject = 'Subject changed'
1070 1070 assert issue.save
1071 1071 end
1072 1072
1073 1073 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1074 1074 issue = Issue.find(11)
1075 1075 issue.status_id = 1
1076 1076 assert !issue.save
1077 assert_not_nil issue.errors[:base]
1077 assert_not_equal [], issue.errors[:base]
1078 1078 end
1079 1079
1080 1080 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1081 1081 issue = Issue.find(11)
1082 1082 issue.status_id = 1
1083 1083 issue.fixed_version_id = 3
1084 1084 assert issue.save
1085 1085 end
1086 1086
1087 1087 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1088 1088 issue = Issue.find(12)
1089 1089 assert_equal 'locked', issue.fixed_version.status
1090 1090 issue.status_id = 1
1091 1091 assert issue.save
1092 1092 end
1093 1093
1094 1094 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1095 1095 issue = Issue.find(2)
1096 1096 assert_equal 2, issue.fixed_version_id
1097 1097 issue.project_id = 3
1098 1098 assert_nil issue.fixed_version_id
1099 1099 issue.fixed_version_id = 2
1100 1100 assert !issue.save
1101 1101 assert_include 'Target version is not included in the list', issue.errors.full_messages
1102 1102 end
1103 1103
1104 1104 def test_should_keep_shared_version_when_changing_project
1105 1105 Version.find(2).update_attribute :sharing, 'tree'
1106 1106
1107 1107 issue = Issue.find(2)
1108 1108 assert_equal 2, issue.fixed_version_id
1109 1109 issue.project_id = 3
1110 1110 assert_equal 2, issue.fixed_version_id
1111 1111 assert issue.save
1112 1112 end
1113 1113
1114 1114 def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
1115 1115 assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
1116 1116 end
1117 1117
1118 1118 def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
1119 1119 Project.find(2).disable_module! :issue_tracking
1120 1120 assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
1121 1121 end
1122 1122
1123 1123 def test_move_to_another_project_with_same_category
1124 1124 issue = Issue.find(1)
1125 1125 issue.project = Project.find(2)
1126 1126 assert issue.save
1127 1127 issue.reload
1128 1128 assert_equal 2, issue.project_id
1129 1129 # Category changes
1130 1130 assert_equal 4, issue.category_id
1131 1131 # Make sure time entries were move to the target project
1132 1132 assert_equal 2, issue.time_entries.first.project_id
1133 1133 end
1134 1134
1135 1135 def test_move_to_another_project_without_same_category
1136 1136 issue = Issue.find(2)
1137 1137 issue.project = Project.find(2)
1138 1138 assert issue.save
1139 1139 issue.reload
1140 1140 assert_equal 2, issue.project_id
1141 1141 # Category cleared
1142 1142 assert_nil issue.category_id
1143 1143 end
1144 1144
1145 1145 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1146 1146 issue = Issue.find(1)
1147 1147 issue.update_attribute(:fixed_version_id, 1)
1148 1148 issue.project = Project.find(2)
1149 1149 assert issue.save
1150 1150 issue.reload
1151 1151 assert_equal 2, issue.project_id
1152 1152 # Cleared fixed_version
1153 1153 assert_equal nil, issue.fixed_version
1154 1154 end
1155 1155
1156 1156 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1157 1157 issue = Issue.find(1)
1158 1158 issue.update_attribute(:fixed_version_id, 4)
1159 1159 issue.project = Project.find(5)
1160 1160 assert issue.save
1161 1161 issue.reload
1162 1162 assert_equal 5, issue.project_id
1163 1163 # Keep fixed_version
1164 1164 assert_equal 4, issue.fixed_version_id
1165 1165 end
1166 1166
1167 1167 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1168 1168 issue = Issue.find(1)
1169 1169 issue.update_attribute(:fixed_version_id, 1)
1170 1170 issue.project = Project.find(5)
1171 1171 assert issue.save
1172 1172 issue.reload
1173 1173 assert_equal 5, issue.project_id
1174 1174 # Cleared fixed_version
1175 1175 assert_equal nil, issue.fixed_version
1176 1176 end
1177 1177
1178 1178 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1179 1179 issue = Issue.find(1)
1180 1180 issue.update_attribute(:fixed_version_id, 7)
1181 1181 issue.project = Project.find(2)
1182 1182 assert issue.save
1183 1183 issue.reload
1184 1184 assert_equal 2, issue.project_id
1185 1185 # Keep fixed_version
1186 1186 assert_equal 7, issue.fixed_version_id
1187 1187 end
1188 1188
1189 1189 def test_move_to_another_project_should_keep_parent_if_valid
1190 1190 issue = Issue.find(1)
1191 1191 issue.update_attribute(:parent_issue_id, 2)
1192 1192 issue.project = Project.find(3)
1193 1193 assert issue.save
1194 1194 issue.reload
1195 1195 assert_equal 2, issue.parent_id
1196 1196 end
1197 1197
1198 1198 def test_move_to_another_project_should_clear_parent_if_not_valid
1199 1199 issue = Issue.find(1)
1200 1200 issue.update_attribute(:parent_issue_id, 2)
1201 1201 issue.project = Project.find(2)
1202 1202 assert issue.save
1203 1203 issue.reload
1204 1204 assert_nil issue.parent_id
1205 1205 end
1206 1206
1207 1207 def test_move_to_another_project_with_disabled_tracker
1208 1208 issue = Issue.find(1)
1209 1209 target = Project.find(2)
1210 1210 target.tracker_ids = [3]
1211 1211 target.save
1212 1212 issue.project = target
1213 1213 assert issue.save
1214 1214 issue.reload
1215 1215 assert_equal 2, issue.project_id
1216 1216 assert_equal 3, issue.tracker_id
1217 1217 end
1218 1218
1219 1219 def test_copy_to_the_same_project
1220 1220 issue = Issue.find(1)
1221 1221 copy = issue.copy
1222 1222 assert_difference 'Issue.count' do
1223 1223 copy.save!
1224 1224 end
1225 1225 assert_kind_of Issue, copy
1226 1226 assert_equal issue.project, copy.project
1227 1227 assert_equal "125", copy.custom_value_for(2).value
1228 1228 end
1229 1229
1230 1230 def test_copy_to_another_project_and_tracker
1231 1231 issue = Issue.find(1)
1232 1232 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1233 1233 assert_difference 'Issue.count' do
1234 1234 copy.save!
1235 1235 end
1236 1236 copy.reload
1237 1237 assert_kind_of Issue, copy
1238 1238 assert_equal Project.find(3), copy.project
1239 1239 assert_equal Tracker.find(2), copy.tracker
1240 1240 # Custom field #2 is not associated with target tracker
1241 1241 assert_nil copy.custom_value_for(2)
1242 1242 end
1243 1243
1244 1244 test "#copy should not create a journal" do
1245 1245 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1246 1246 copy.save!
1247 1247 assert_equal 0, copy.reload.journals.size
1248 1248 end
1249 1249
1250 1250 test "#copy should allow assigned_to changes" do
1251 1251 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1252 1252 assert_equal 3, copy.assigned_to_id
1253 1253 end
1254 1254
1255 1255 test "#copy should allow status changes" do
1256 1256 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1257 1257 assert_equal 2, copy.status_id
1258 1258 end
1259 1259
1260 1260 test "#copy should allow start date changes" do
1261 1261 date = Date.today
1262 1262 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1263 1263 assert_equal date, copy.start_date
1264 1264 end
1265 1265
1266 1266 test "#copy should allow due date changes" do
1267 1267 date = Date.today
1268 1268 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1269 1269 assert_equal date, copy.due_date
1270 1270 end
1271 1271
1272 1272 test "#copy should set current user as author" do
1273 1273 User.current = User.find(9)
1274 1274 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1275 1275 assert_equal User.current, copy.author
1276 1276 end
1277 1277
1278 1278 test "#copy should create a journal with notes" do
1279 1279 date = Date.today
1280 1280 notes = "Notes added when copying"
1281 1281 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1282 1282 copy.init_journal(User.current, notes)
1283 1283 copy.save!
1284 1284
1285 1285 assert_equal 1, copy.journals.size
1286 1286 journal = copy.journals.first
1287 1287 assert_equal 0, journal.details.size
1288 1288 assert_equal notes, journal.notes
1289 1289 end
1290 1290
1291 1291 def test_valid_parent_project
1292 1292 issue = Issue.find(1)
1293 1293 issue_in_same_project = Issue.find(2)
1294 1294 issue_in_child_project = Issue.find(5)
1295 1295 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1296 1296 issue_in_other_child_project = Issue.find(6)
1297 1297 issue_in_different_tree = Issue.find(4)
1298 1298
1299 1299 with_settings :cross_project_subtasks => '' do
1300 1300 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1301 1301 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1302 1302 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1303 1303 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1304 1304 end
1305 1305
1306 1306 with_settings :cross_project_subtasks => 'system' do
1307 1307 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1308 1308 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1309 1309 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1310 1310 end
1311 1311
1312 1312 with_settings :cross_project_subtasks => 'tree' do
1313 1313 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1314 1314 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1315 1315 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1316 1316 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1317 1317
1318 1318 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1319 1319 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1320 1320 end
1321 1321
1322 1322 with_settings :cross_project_subtasks => 'descendants' do
1323 1323 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1324 1324 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1325 1325 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1326 1326 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1327 1327
1328 1328 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1329 1329 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1330 1330 end
1331 1331 end
1332 1332
1333 1333 def test_recipients_should_include_previous_assignee
1334 1334 user = User.find(3)
1335 1335 user.members.update_all ["mail_notification = ?", false]
1336 1336 user.update_attribute :mail_notification, 'only_assigned'
1337 1337
1338 1338 issue = Issue.find(2)
1339 1339 issue.assigned_to = nil
1340 1340 assert_include user.mail, issue.recipients
1341 1341 issue.save!
1342 1342 assert !issue.recipients.include?(user.mail)
1343 1343 end
1344 1344
1345 1345 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1346 1346 issue = Issue.find(12)
1347 1347 assert issue.recipients.include?(issue.author.mail)
1348 1348 # copy the issue to a private project
1349 1349 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1350 1350 # author is not a member of project anymore
1351 1351 assert !copy.recipients.include?(copy.author.mail)
1352 1352 end
1353 1353
1354 1354 def test_recipients_should_include_the_assigned_group_members
1355 1355 group_member = User.generate!
1356 1356 group = Group.generate!
1357 1357 group.users << group_member
1358 1358
1359 1359 issue = Issue.find(12)
1360 1360 issue.assigned_to = group
1361 1361 assert issue.recipients.include?(group_member.mail)
1362 1362 end
1363 1363
1364 1364 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1365 1365 user = User.find(3)
1366 1366 issue = Issue.find(9)
1367 1367 Watcher.create!(:user => user, :watchable => issue)
1368 1368 assert issue.watched_by?(user)
1369 1369 assert !issue.watcher_recipients.include?(user.mail)
1370 1370 end
1371 1371
1372 1372 def test_issue_destroy
1373 1373 Issue.find(1).destroy
1374 1374 assert_nil Issue.find_by_id(1)
1375 1375 assert_nil TimeEntry.find_by_issue_id(1)
1376 1376 end
1377 1377
1378 1378 def test_destroying_a_deleted_issue_should_not_raise_an_error
1379 1379 issue = Issue.find(1)
1380 1380 Issue.find(1).destroy
1381 1381
1382 1382 assert_nothing_raised do
1383 1383 assert_no_difference 'Issue.count' do
1384 1384 issue.destroy
1385 1385 end
1386 1386 assert issue.destroyed?
1387 1387 end
1388 1388 end
1389 1389
1390 1390 def test_destroying_a_stale_issue_should_not_raise_an_error
1391 1391 issue = Issue.find(1)
1392 1392 Issue.find(1).update_attribute :subject, "Updated"
1393 1393
1394 1394 assert_nothing_raised do
1395 1395 assert_difference 'Issue.count', -1 do
1396 1396 issue.destroy
1397 1397 end
1398 1398 assert issue.destroyed?
1399 1399 end
1400 1400 end
1401 1401
1402 1402 def test_blocked
1403 1403 blocked_issue = Issue.find(9)
1404 1404 blocking_issue = Issue.find(10)
1405 1405
1406 1406 assert blocked_issue.blocked?
1407 1407 assert !blocking_issue.blocked?
1408 1408 end
1409 1409
1410 1410 def test_blocked_issues_dont_allow_closed_statuses
1411 1411 blocked_issue = Issue.find(9)
1412 1412
1413 1413 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1414 1414 assert !allowed_statuses.empty?
1415 1415 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1416 1416 assert closed_statuses.empty?
1417 1417 end
1418 1418
1419 1419 def test_unblocked_issues_allow_closed_statuses
1420 1420 blocking_issue = Issue.find(10)
1421 1421
1422 1422 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1423 1423 assert !allowed_statuses.empty?
1424 1424 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1425 1425 assert !closed_statuses.empty?
1426 1426 end
1427 1427
1428 1428 def test_reschedule_an_issue_without_dates
1429 1429 with_settings :non_working_week_days => [] do
1430 1430 issue = Issue.new(:start_date => nil, :due_date => nil)
1431 1431 issue.reschedule_on '2012-10-09'.to_date
1432 1432 assert_equal '2012-10-09'.to_date, issue.start_date
1433 1433 assert_equal '2012-10-09'.to_date, issue.due_date
1434 1434 end
1435 1435
1436 1436 with_settings :non_working_week_days => %w(6 7) do
1437 1437 issue = Issue.new(:start_date => nil, :due_date => nil)
1438 1438 issue.reschedule_on '2012-10-09'.to_date
1439 1439 assert_equal '2012-10-09'.to_date, issue.start_date
1440 1440 assert_equal '2012-10-09'.to_date, issue.due_date
1441 1441
1442 1442 issue = Issue.new(:start_date => nil, :due_date => nil)
1443 1443 issue.reschedule_on '2012-10-13'.to_date
1444 1444 assert_equal '2012-10-15'.to_date, issue.start_date
1445 1445 assert_equal '2012-10-15'.to_date, issue.due_date
1446 1446 end
1447 1447 end
1448 1448
1449 1449 def test_reschedule_an_issue_with_start_date
1450 1450 with_settings :non_working_week_days => [] do
1451 1451 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1452 1452 issue.reschedule_on '2012-10-13'.to_date
1453 1453 assert_equal '2012-10-13'.to_date, issue.start_date
1454 1454 assert_equal '2012-10-13'.to_date, issue.due_date
1455 1455 end
1456 1456
1457 1457 with_settings :non_working_week_days => %w(6 7) do
1458 1458 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1459 1459 issue.reschedule_on '2012-10-11'.to_date
1460 1460 assert_equal '2012-10-11'.to_date, issue.start_date
1461 1461 assert_equal '2012-10-11'.to_date, issue.due_date
1462 1462
1463 1463 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1464 1464 issue.reschedule_on '2012-10-13'.to_date
1465 1465 assert_equal '2012-10-15'.to_date, issue.start_date
1466 1466 assert_equal '2012-10-15'.to_date, issue.due_date
1467 1467 end
1468 1468 end
1469 1469
1470 1470 def test_reschedule_an_issue_with_start_and_due_dates
1471 1471 with_settings :non_working_week_days => [] do
1472 1472 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1473 1473 issue.reschedule_on '2012-10-13'.to_date
1474 1474 assert_equal '2012-10-13'.to_date, issue.start_date
1475 1475 assert_equal '2012-10-19'.to_date, issue.due_date
1476 1476 end
1477 1477
1478 1478 with_settings :non_working_week_days => %w(6 7) do
1479 1479 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1480 1480 issue.reschedule_on '2012-10-11'.to_date
1481 1481 assert_equal '2012-10-11'.to_date, issue.start_date
1482 1482 assert_equal '2012-10-23'.to_date, issue.due_date
1483 1483
1484 1484 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1485 1485 issue.reschedule_on '2012-10-13'.to_date
1486 1486 assert_equal '2012-10-15'.to_date, issue.start_date
1487 1487 assert_equal '2012-10-25'.to_date, issue.due_date
1488 1488 end
1489 1489 end
1490 1490
1491 1491 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1492 1492 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1493 1493 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1494 1494 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1495 1495 :relation_type => IssueRelation::TYPE_PRECEDES)
1496 1496 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1497 1497
1498 1498 issue1.reload
1499 1499 issue1.due_date = '2012-10-23'
1500 1500 issue1.save!
1501 1501 issue2.reload
1502 1502 assert_equal Date.parse('2012-10-24'), issue2.start_date
1503 1503 assert_equal Date.parse('2012-10-26'), issue2.due_date
1504 1504 end
1505 1505
1506 1506 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1507 1507 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1508 1508 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1509 1509 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1510 1510 :relation_type => IssueRelation::TYPE_PRECEDES)
1511 1511 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1512 1512
1513 1513 issue1.reload
1514 1514 issue1.start_date = '2012-09-17'
1515 1515 issue1.due_date = '2012-09-18'
1516 1516 issue1.save!
1517 1517 issue2.reload
1518 1518 assert_equal Date.parse('2012-09-19'), issue2.start_date
1519 1519 assert_equal Date.parse('2012-09-21'), issue2.due_date
1520 1520 end
1521 1521
1522 1522 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1523 1523 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1524 1524 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1525 1525 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1526 1526 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1527 1527 :relation_type => IssueRelation::TYPE_PRECEDES)
1528 1528 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1529 1529 :relation_type => IssueRelation::TYPE_PRECEDES)
1530 1530 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1531 1531
1532 1532 issue1.reload
1533 1533 issue1.start_date = '2012-09-17'
1534 1534 issue1.due_date = '2012-09-18'
1535 1535 issue1.save!
1536 1536 issue2.reload
1537 1537 # Issue 2 must start after Issue 3
1538 1538 assert_equal Date.parse('2012-10-03'), issue2.start_date
1539 1539 assert_equal Date.parse('2012-10-05'), issue2.due_date
1540 1540 end
1541 1541
1542 1542 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1543 1543 with_settings :non_working_week_days => [] do
1544 1544 stale = Issue.find(1)
1545 1545 issue = Issue.find(1)
1546 1546 issue.subject = "Updated"
1547 1547 issue.save!
1548 1548 date = 10.days.from_now.to_date
1549 1549 assert_nothing_raised do
1550 1550 stale.reschedule_on!(date)
1551 1551 end
1552 1552 assert_equal date, stale.reload.start_date
1553 1553 end
1554 1554 end
1555 1555
1556 1556 def test_child_issue_should_consider_parent_soonest_start_on_create
1557 1557 set_language_if_valid 'en'
1558 1558 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1559 1559 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
1560 1560 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1561 1561 :relation_type => IssueRelation::TYPE_PRECEDES)
1562 1562 issue1.reload
1563 1563 issue2.reload
1564 1564 assert_equal Date.parse('2012-10-18'), issue2.start_date
1565 1565
1566 1566 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1567 1567 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1568 1568 assert !child.valid?
1569 1569 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1570 1570 assert_equal Date.parse('2012-10-18'), child.soonest_start
1571 1571 child.start_date = '2012-10-18'
1572 1572 assert child.save
1573 1573 end
1574 1574
1575 1575 def test_setting_parent_to_a_dependent_issue_should_not_validate
1576 1576 set_language_if_valid 'en'
1577 1577 issue1 = Issue.generate!
1578 1578 issue2 = Issue.generate!
1579 1579 issue3 = Issue.generate!
1580 1580 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1581 1581 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
1582 1582 issue3.reload
1583 1583 issue3.parent_issue_id = issue2.id
1584 1584 assert !issue3.valid?
1585 1585 assert_include 'Parent task is invalid', issue3.errors.full_messages
1586 1586 end
1587 1587
1588 1588 def test_setting_parent_should_not_allow_circular_dependency
1589 1589 set_language_if_valid 'en'
1590 1590 issue1 = Issue.generate!
1591 1591 issue2 = Issue.generate!
1592 1592 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1593 1593 issue3 = Issue.generate!
1594 1594 issue2.reload
1595 1595 issue2.parent_issue_id = issue3.id
1596 1596 issue2.save!
1597 1597 issue4 = Issue.generate!
1598 1598 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
1599 1599 issue4.reload
1600 1600 issue4.parent_issue_id = issue1.id
1601 1601 assert !issue4.valid?
1602 1602 assert_include 'Parent task is invalid', issue4.errors.full_messages
1603 1603 end
1604 1604
1605 1605 def test_overdue
1606 1606 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1607 1607 assert !Issue.new(:due_date => Date.today).overdue?
1608 1608 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1609 1609 assert !Issue.new(:due_date => nil).overdue?
1610 1610 assert !Issue.new(:due_date => 1.day.ago.to_date,
1611 1611 :status => IssueStatus.where(:is_closed => true).first
1612 1612 ).overdue?
1613 1613 end
1614 1614
1615 1615 test "#behind_schedule? should be false if the issue has no start_date" do
1616 1616 assert !Issue.new(:start_date => nil,
1617 1617 :due_date => 1.day.from_now.to_date,
1618 1618 :done_ratio => 0).behind_schedule?
1619 1619 end
1620 1620
1621 1621 test "#behind_schedule? should be false if the issue has no end_date" do
1622 1622 assert !Issue.new(:start_date => 1.day.from_now.to_date,
1623 1623 :due_date => nil,
1624 1624 :done_ratio => 0).behind_schedule?
1625 1625 end
1626 1626
1627 1627 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
1628 1628 assert !Issue.new(:start_date => 50.days.ago.to_date,
1629 1629 :due_date => 50.days.from_now.to_date,
1630 1630 :done_ratio => 90).behind_schedule?
1631 1631 end
1632 1632
1633 1633 test "#behind_schedule? should be true if the issue hasn't been started at all" do
1634 1634 assert Issue.new(:start_date => 1.day.ago.to_date,
1635 1635 :due_date => 1.day.from_now.to_date,
1636 1636 :done_ratio => 0).behind_schedule?
1637 1637 end
1638 1638
1639 1639 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
1640 1640 assert Issue.new(:start_date => 100.days.ago.to_date,
1641 1641 :due_date => Date.today,
1642 1642 :done_ratio => 90).behind_schedule?
1643 1643 end
1644 1644
1645 1645 test "#assignable_users should be Users" do
1646 1646 assert_kind_of User, Issue.find(1).assignable_users.first
1647 1647 end
1648 1648
1649 1649 test "#assignable_users should include the issue author" do
1650 1650 non_project_member = User.generate!
1651 1651 issue = Issue.generate!(:author => non_project_member)
1652 1652
1653 1653 assert issue.assignable_users.include?(non_project_member)
1654 1654 end
1655 1655
1656 1656 test "#assignable_users should include the current assignee" do
1657 1657 user = User.generate!
1658 1658 issue = Issue.generate!(:assigned_to => user)
1659 1659 user.lock!
1660 1660
1661 1661 assert Issue.find(issue.id).assignable_users.include?(user)
1662 1662 end
1663 1663
1664 1664 test "#assignable_users should not show the issue author twice" do
1665 1665 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1666 1666 assert_equal 2, assignable_user_ids.length
1667 1667
1668 1668 assignable_user_ids.each do |user_id|
1669 1669 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
1670 1670 "User #{user_id} appears more or less than once"
1671 1671 end
1672 1672 end
1673 1673
1674 1674 test "#assignable_users with issue_group_assignment should include groups" do
1675 1675 issue = Issue.new(:project => Project.find(2))
1676 1676
1677 1677 with_settings :issue_group_assignment => '1' do
1678 1678 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1679 1679 assert issue.assignable_users.include?(Group.find(11))
1680 1680 end
1681 1681 end
1682 1682
1683 1683 test "#assignable_users without issue_group_assignment should not include groups" do
1684 1684 issue = Issue.new(:project => Project.find(2))
1685 1685
1686 1686 with_settings :issue_group_assignment => '0' do
1687 1687 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1688 1688 assert !issue.assignable_users.include?(Group.find(11))
1689 1689 end
1690 1690 end
1691 1691
1692 1692 def test_create_should_send_email_notification
1693 1693 ActionMailer::Base.deliveries.clear
1694 1694 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1695 1695 :author_id => 3, :status_id => 1,
1696 1696 :priority => IssuePriority.all.first,
1697 1697 :subject => 'test_create', :estimated_hours => '1:30')
1698 1698
1699 1699 assert issue.save
1700 1700 assert_equal 1, ActionMailer::Base.deliveries.size
1701 1701 end
1702 1702
1703 1703 def test_stale_issue_should_not_send_email_notification
1704 1704 ActionMailer::Base.deliveries.clear
1705 1705 issue = Issue.find(1)
1706 1706 stale = Issue.find(1)
1707 1707
1708 1708 issue.init_journal(User.find(1))
1709 1709 issue.subject = 'Subjet update'
1710 1710 assert issue.save
1711 1711 assert_equal 1, ActionMailer::Base.deliveries.size
1712 1712 ActionMailer::Base.deliveries.clear
1713 1713
1714 1714 stale.init_journal(User.find(1))
1715 1715 stale.subject = 'Another subjet update'
1716 1716 assert_raise ActiveRecord::StaleObjectError do
1717 1717 stale.save
1718 1718 end
1719 1719 assert ActionMailer::Base.deliveries.empty?
1720 1720 end
1721 1721
1722 1722 def test_journalized_description
1723 1723 IssueCustomField.delete_all
1724 1724
1725 1725 i = Issue.first
1726 1726 old_description = i.description
1727 1727 new_description = "This is the new description"
1728 1728
1729 1729 i.init_journal(User.find(2))
1730 1730 i.description = new_description
1731 1731 assert_difference 'Journal.count', 1 do
1732 1732 assert_difference 'JournalDetail.count', 1 do
1733 1733 i.save!
1734 1734 end
1735 1735 end
1736 1736
1737 1737 detail = JournalDetail.first(:order => 'id DESC')
1738 1738 assert_equal i, detail.journal.journalized
1739 1739 assert_equal 'attr', detail.property
1740 1740 assert_equal 'description', detail.prop_key
1741 1741 assert_equal old_description, detail.old_value
1742 1742 assert_equal new_description, detail.value
1743 1743 end
1744 1744
1745 1745 def test_blank_descriptions_should_not_be_journalized
1746 1746 IssueCustomField.delete_all
1747 1747 Issue.update_all("description = NULL", "id=1")
1748 1748
1749 1749 i = Issue.find(1)
1750 1750 i.init_journal(User.find(2))
1751 1751 i.subject = "blank description"
1752 1752 i.description = "\r\n"
1753 1753
1754 1754 assert_difference 'Journal.count', 1 do
1755 1755 assert_difference 'JournalDetail.count', 1 do
1756 1756 i.save!
1757 1757 end
1758 1758 end
1759 1759 end
1760 1760
1761 1761 def test_journalized_multi_custom_field
1762 1762 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
1763 1763 :is_filter => true, :is_for_all => true,
1764 1764 :tracker_ids => [1],
1765 1765 :possible_values => ['value1', 'value2', 'value3'],
1766 1766 :multiple => true)
1767 1767
1768 1768 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
1769 1769 :subject => 'Test', :author_id => 1)
1770 1770
1771 1771 assert_difference 'Journal.count' do
1772 1772 assert_difference 'JournalDetail.count' do
1773 1773 issue.init_journal(User.first)
1774 1774 issue.custom_field_values = {field.id => ['value1']}
1775 1775 issue.save!
1776 1776 end
1777 1777 assert_difference 'JournalDetail.count' do
1778 1778 issue.init_journal(User.first)
1779 1779 issue.custom_field_values = {field.id => ['value1', 'value2']}
1780 1780 issue.save!
1781 1781 end
1782 1782 assert_difference 'JournalDetail.count', 2 do
1783 1783 issue.init_journal(User.first)
1784 1784 issue.custom_field_values = {field.id => ['value3', 'value2']}
1785 1785 issue.save!
1786 1786 end
1787 1787 assert_difference 'JournalDetail.count', 2 do
1788 1788 issue.init_journal(User.first)
1789 1789 issue.custom_field_values = {field.id => nil}
1790 1790 issue.save!
1791 1791 end
1792 1792 end
1793 1793 end
1794 1794
1795 1795 def test_description_eol_should_be_normalized
1796 1796 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1797 1797 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1798 1798 end
1799 1799
1800 1800 def test_saving_twice_should_not_duplicate_journal_details
1801 1801 i = Issue.first
1802 1802 i.init_journal(User.find(2), 'Some notes')
1803 1803 # initial changes
1804 1804 i.subject = 'New subject'
1805 1805 i.done_ratio = i.done_ratio + 10
1806 1806 assert_difference 'Journal.count' do
1807 1807 assert i.save
1808 1808 end
1809 1809 # 1 more change
1810 1810 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
1811 1811 assert_no_difference 'Journal.count' do
1812 1812 assert_difference 'JournalDetail.count', 1 do
1813 1813 i.save
1814 1814 end
1815 1815 end
1816 1816 # no more change
1817 1817 assert_no_difference 'Journal.count' do
1818 1818 assert_no_difference 'JournalDetail.count' do
1819 1819 i.save
1820 1820 end
1821 1821 end
1822 1822 end
1823 1823
1824 1824 def test_all_dependent_issues
1825 1825 IssueRelation.delete_all
1826 1826 assert IssueRelation.create!(:issue_from => Issue.find(1),
1827 1827 :issue_to => Issue.find(2),
1828 1828 :relation_type => IssueRelation::TYPE_PRECEDES)
1829 1829 assert IssueRelation.create!(:issue_from => Issue.find(2),
1830 1830 :issue_to => Issue.find(3),
1831 1831 :relation_type => IssueRelation::TYPE_PRECEDES)
1832 1832 assert IssueRelation.create!(:issue_from => Issue.find(3),
1833 1833 :issue_to => Issue.find(8),
1834 1834 :relation_type => IssueRelation::TYPE_PRECEDES)
1835 1835
1836 1836 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1837 1837 end
1838 1838
1839 1839 def test_all_dependent_issues_with_subtask
1840 1840 IssueRelation.delete_all
1841 1841
1842 1842 project = Project.generate!(:name => "testproject")
1843 1843
1844 1844 parentIssue = Issue.generate!(:project => project)
1845 1845 childIssue1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
1846 1846 childIssue2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
1847 1847
1848 1848 assert_equal [childIssue1.id, childIssue2.id].sort, parentIssue.all_dependent_issues.collect(&:id).uniq.sort
1849 1849 end
1850 1850
1851 1851 def test_all_dependent_issues_does_not_include_self
1852 1852 IssueRelation.delete_all
1853 1853
1854 1854 project = Project.generate!(:name => "testproject")
1855 1855
1856 1856 parentIssue = Issue.generate!(:project => project)
1857 1857 childIssue = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
1858 1858
1859 1859 assert_equal [childIssue.id], parentIssue.all_dependent_issues.collect(&:id)
1860 1860 end
1861 1861
1862 1862 def test_all_dependent_issues_with_parenttask_and_sibling
1863 1863 IssueRelation.delete_all
1864 1864
1865 1865 project = Project.generate!(:name => "testproject")
1866 1866
1867 1867 parentIssue = Issue.generate!(:project => project)
1868 1868 childIssue1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
1869 1869 childIssue2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id)
1870 1870
1871 1871 assert_equal [parentIssue.id].sort, childIssue1.all_dependent_issues.collect(&:id)
1872 1872 end
1873 1873
1874 1874 def test_all_dependent_issues_with_relation_to_leaf_in_other_tree
1875 1875 IssueRelation.delete_all
1876 1876
1877 1877 project = Project.generate!(:name => "testproject")
1878 1878
1879 1879 parentIssue1 = Issue.generate!(:project => project)
1880 1880 childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
1881 1881 childIssue1_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
1882 1882
1883 1883 parentIssue2 = Issue.generate!(:project => project)
1884 1884 childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
1885 1885 childIssue2_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
1886 1886
1887 1887
1888 1888 assert IssueRelation.create(:issue_from => parentIssue1,
1889 1889 :issue_to => childIssue2_2,
1890 1890 :relation_type => IssueRelation::TYPE_BLOCKS)
1891 1891
1892 1892 assert_equal [childIssue1_1.id, childIssue1_2.id, parentIssue2.id, childIssue2_2.id].sort,
1893 1893 parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
1894 1894 end
1895 1895
1896 1896 def test_all_dependent_issues_with_relation_to_parent_in_other_tree
1897 1897 IssueRelation.delete_all
1898 1898
1899 1899 project = Project.generate!(:name => "testproject")
1900 1900
1901 1901 parentIssue1 = Issue.generate!(:project => project)
1902 1902 childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
1903 1903 childIssue1_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
1904 1904
1905 1905 parentIssue2 = Issue.generate!(:project => project)
1906 1906 childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
1907 1907 childIssue2_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
1908 1908
1909 1909
1910 1910 assert IssueRelation.create(:issue_from => parentIssue1,
1911 1911 :issue_to => parentIssue2,
1912 1912 :relation_type => IssueRelation::TYPE_BLOCKS)
1913 1913
1914 1914 assert_equal [childIssue1_1.id, childIssue1_2.id, parentIssue2.id, childIssue2_1.id, childIssue2_2.id].sort,
1915 1915 parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
1916 1916 end
1917 1917
1918 1918 def test_all_dependent_issues_with_transitive_relation
1919 1919 IssueRelation.delete_all
1920 1920
1921 1921 project = Project.generate!(:name => "testproject")
1922 1922
1923 1923 parentIssue1 = Issue.generate!(:project => project)
1924 1924 childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
1925 1925
1926 1926 parentIssue2 = Issue.generate!(:project => project)
1927 1927 childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
1928 1928
1929 1929 independentIssue = Issue.generate!(:project => project)
1930 1930
1931 1931 assert IssueRelation.create(:issue_from => parentIssue1,
1932 1932 :issue_to => childIssue2_1,
1933 1933 :relation_type => IssueRelation::TYPE_RELATES)
1934 1934
1935 1935 assert IssueRelation.create(:issue_from => childIssue2_1,
1936 1936 :issue_to => independentIssue,
1937 1937 :relation_type => IssueRelation::TYPE_RELATES)
1938 1938
1939 1939 assert_equal [childIssue1_1.id, parentIssue2.id, childIssue2_1.id, independentIssue.id].sort,
1940 1940 parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
1941 1941 end
1942 1942
1943 1943 def test_all_dependent_issues_with_transitive_relation2
1944 1944 IssueRelation.delete_all
1945 1945
1946 1946 project = Project.generate!(:name => "testproject")
1947 1947
1948 1948 parentIssue1 = Issue.generate!(:project => project)
1949 1949 childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id)
1950 1950
1951 1951 parentIssue2 = Issue.generate!(:project => project)
1952 1952 childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id)
1953 1953
1954 1954 independentIssue = Issue.generate!(:project => project)
1955 1955
1956 1956 assert IssueRelation.create(:issue_from => parentIssue1,
1957 1957 :issue_to => independentIssue,
1958 1958 :relation_type => IssueRelation::TYPE_RELATES)
1959 1959
1960 1960 assert IssueRelation.create(:issue_from => independentIssue,
1961 1961 :issue_to => childIssue2_1,
1962 1962 :relation_type => IssueRelation::TYPE_RELATES)
1963 1963
1964 1964 assert_equal [childIssue1_1.id, parentIssue2.id, childIssue2_1.id, independentIssue.id].sort,
1965 1965 parentIssue1.all_dependent_issues.collect(&:id).uniq.sort
1966 1966
1967 1967 end
1968 1968
1969 1969 def test_all_dependent_issues_with_persistent_circular_dependency
1970 1970 IssueRelation.delete_all
1971 1971 assert IssueRelation.create!(:issue_from => Issue.find(1),
1972 1972 :issue_to => Issue.find(2),
1973 1973 :relation_type => IssueRelation::TYPE_PRECEDES)
1974 1974 assert IssueRelation.create!(:issue_from => Issue.find(2),
1975 1975 :issue_to => Issue.find(3),
1976 1976 :relation_type => IssueRelation::TYPE_PRECEDES)
1977 1977
1978 1978 r = IssueRelation.create!(:issue_from => Issue.find(3),
1979 1979 :issue_to => Issue.find(7),
1980 1980 :relation_type => IssueRelation::TYPE_PRECEDES)
1981 1981 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1982 1982
1983 1983 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1984 1984 end
1985 1985
1986 1986 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1987 1987 IssueRelation.delete_all
1988 1988 assert IssueRelation.create!(:issue_from => Issue.find(1),
1989 1989 :issue_to => Issue.find(2),
1990 1990 :relation_type => IssueRelation::TYPE_RELATES)
1991 1991 assert IssueRelation.create!(:issue_from => Issue.find(2),
1992 1992 :issue_to => Issue.find(3),
1993 1993 :relation_type => IssueRelation::TYPE_RELATES)
1994 1994 assert IssueRelation.create!(:issue_from => Issue.find(3),
1995 1995 :issue_to => Issue.find(8),
1996 1996 :relation_type => IssueRelation::TYPE_RELATES)
1997 1997
1998 1998 r = IssueRelation.create!(:issue_from => Issue.find(8),
1999 1999 :issue_to => Issue.find(7),
2000 2000 :relation_type => IssueRelation::TYPE_RELATES)
2001 2001 IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
2002 2002
2003 2003 r = IssueRelation.create!(:issue_from => Issue.find(3),
2004 2004 :issue_to => Issue.find(7),
2005 2005 :relation_type => IssueRelation::TYPE_RELATES)
2006 2006 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
2007 2007
2008 2008 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
2009 2009 end
2010 2010
2011 2011 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2012 2012 @issue = Issue.find(1)
2013 2013 @issue_status = IssueStatus.find(1)
2014 2014 @issue_status.update_attribute(:default_done_ratio, 50)
2015 2015 @issue2 = Issue.find(2)
2016 2016 @issue_status2 = IssueStatus.find(2)
2017 2017 @issue_status2.update_attribute(:default_done_ratio, 0)
2018 2018
2019 2019 with_settings :issue_done_ratio => 'issue_field' do
2020 2020 assert_equal 0, @issue.done_ratio
2021 2021 assert_equal 30, @issue2.done_ratio
2022 2022 end
2023 2023
2024 2024 with_settings :issue_done_ratio => 'issue_status' do
2025 2025 assert_equal 50, @issue.done_ratio
2026 2026 assert_equal 0, @issue2.done_ratio
2027 2027 end
2028 2028 end
2029 2029
2030 2030 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2031 2031 @issue = Issue.find(1)
2032 2032 @issue_status = IssueStatus.find(1)
2033 2033 @issue_status.update_attribute(:default_done_ratio, 50)
2034 2034 @issue2 = Issue.find(2)
2035 2035 @issue_status2 = IssueStatus.find(2)
2036 2036 @issue_status2.update_attribute(:default_done_ratio, 0)
2037 2037
2038 2038 with_settings :issue_done_ratio => 'issue_field' do
2039 2039 @issue.update_done_ratio_from_issue_status
2040 2040 @issue2.update_done_ratio_from_issue_status
2041 2041
2042 2042 assert_equal 0, @issue.read_attribute(:done_ratio)
2043 2043 assert_equal 30, @issue2.read_attribute(:done_ratio)
2044 2044 end
2045 2045
2046 2046 with_settings :issue_done_ratio => 'issue_status' do
2047 2047 @issue.update_done_ratio_from_issue_status
2048 2048 @issue2.update_done_ratio_from_issue_status
2049 2049
2050 2050 assert_equal 50, @issue.read_attribute(:done_ratio)
2051 2051 assert_equal 0, @issue2.read_attribute(:done_ratio)
2052 2052 end
2053 2053 end
2054 2054
2055 2055 test "#by_tracker" do
2056 2056 User.current = User.anonymous
2057 2057 groups = Issue.by_tracker(Project.find(1))
2058 2058 assert_equal 3, groups.count
2059 2059 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2060 2060 end
2061 2061
2062 2062 test "#by_version" do
2063 2063 User.current = User.anonymous
2064 2064 groups = Issue.by_version(Project.find(1))
2065 2065 assert_equal 3, groups.count
2066 2066 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2067 2067 end
2068 2068
2069 2069 test "#by_priority" do
2070 2070 User.current = User.anonymous
2071 2071 groups = Issue.by_priority(Project.find(1))
2072 2072 assert_equal 4, groups.count
2073 2073 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2074 2074 end
2075 2075
2076 2076 test "#by_category" do
2077 2077 User.current = User.anonymous
2078 2078 groups = Issue.by_category(Project.find(1))
2079 2079 assert_equal 2, groups.count
2080 2080 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2081 2081 end
2082 2082
2083 2083 test "#by_assigned_to" do
2084 2084 User.current = User.anonymous
2085 2085 groups = Issue.by_assigned_to(Project.find(1))
2086 2086 assert_equal 2, groups.count
2087 2087 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2088 2088 end
2089 2089
2090 2090 test "#by_author" do
2091 2091 User.current = User.anonymous
2092 2092 groups = Issue.by_author(Project.find(1))
2093 2093 assert_equal 4, groups.count
2094 2094 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2095 2095 end
2096 2096
2097 2097 test "#by_subproject" do
2098 2098 User.current = User.anonymous
2099 2099 groups = Issue.by_subproject(Project.find(1))
2100 2100 # Private descendant not visible
2101 2101 assert_equal 1, groups.count
2102 2102 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2103 2103 end
2104 2104
2105 2105 def test_recently_updated_scope
2106 2106 #should return the last updated issue
2107 2107 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2108 2108 end
2109 2109
2110 2110 def test_on_active_projects_scope
2111 2111 assert Project.find(2).archive
2112 2112
2113 2113 before = Issue.on_active_project.length
2114 2114 # test inclusion to results
2115 2115 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2116 2116 assert_equal before + 1, Issue.on_active_project.length
2117 2117
2118 2118 # Move to an archived project
2119 2119 issue.project = Project.find(2)
2120 2120 assert issue.save
2121 2121 assert_equal before, Issue.on_active_project.length
2122 2122 end
2123 2123
2124 2124 test "Issue#recipients should include project recipients" do
2125 2125 issue = Issue.generate!
2126 2126 assert issue.project.recipients.present?
2127 2127 issue.project.recipients.each do |project_recipient|
2128 2128 assert issue.recipients.include?(project_recipient)
2129 2129 end
2130 2130 end
2131 2131
2132 2132 test "Issue#recipients should include the author if the author is active" do
2133 2133 issue = Issue.generate!(:author => User.generate!)
2134 2134 assert issue.author, "No author set for Issue"
2135 2135 assert issue.recipients.include?(issue.author.mail)
2136 2136 end
2137 2137
2138 2138 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2139 2139 issue = Issue.generate!(:assigned_to => User.generate!)
2140 2140 assert issue.assigned_to, "No assigned_to set for Issue"
2141 2141 assert issue.recipients.include?(issue.assigned_to.mail)
2142 2142 end
2143 2143
2144 2144 test "Issue#recipients should not include users who opt out of all email" do
2145 2145 issue = Issue.generate!(:author => User.generate!)
2146 2146 issue.author.update_attribute(:mail_notification, :none)
2147 2147 assert !issue.recipients.include?(issue.author.mail)
2148 2148 end
2149 2149
2150 2150 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2151 2151 issue = Issue.generate!(:author => User.generate!)
2152 2152 issue.author.update_attribute(:mail_notification, :only_assigned)
2153 2153 assert !issue.recipients.include?(issue.author.mail)
2154 2154 end
2155 2155
2156 2156 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2157 2157 issue = Issue.generate!(:assigned_to => User.generate!)
2158 2158 issue.assigned_to.update_attribute(:mail_notification, :only_owner)
2159 2159 assert !issue.recipients.include?(issue.assigned_to.mail)
2160 2160 end
2161 2161
2162 2162 def test_last_journal_id_with_journals_should_return_the_journal_id
2163 2163 assert_equal 2, Issue.find(1).last_journal_id
2164 2164 end
2165 2165
2166 2166 def test_last_journal_id_without_journals_should_return_nil
2167 2167 assert_nil Issue.find(3).last_journal_id
2168 2168 end
2169 2169
2170 2170 def test_journals_after_should_return_journals_with_greater_id
2171 2171 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2172 2172 assert_equal [], Issue.find(1).journals_after('2')
2173 2173 end
2174 2174
2175 2175 def test_journals_after_with_blank_arg_should_return_all_journals
2176 2176 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2177 2177 end
2178 2178
2179 2179 def test_css_classes_should_include_tracker
2180 2180 issue = Issue.new(:tracker => Tracker.find(2))
2181 2181 classes = issue.css_classes.split(' ')
2182 2182 assert_include 'tracker-2', classes
2183 2183 end
2184 2184
2185 2185 def test_css_classes_should_include_priority
2186 2186 issue = Issue.new(:priority => IssuePriority.find(8))
2187 2187 classes = issue.css_classes.split(' ')
2188 2188 assert_include 'priority-8', classes
2189 2189 assert_include 'priority-highest', classes
2190 2190 end
2191 2191
2192 2192 def test_css_classes_should_include_user_assignment
2193 2193 issue = Issue.generate(:assigned_to_id => 2)
2194 2194 assert_include 'assigned-to-me', issue.css_classes(User.find(2))
2195 2195 assert_not_include 'assigned-to-me', issue.css_classes(User.find(3))
2196 2196 end
2197 2197
2198 2198 def test_css_classes_should_include_user_group_assignment
2199 2199 issue = Issue.generate(:assigned_to_id => 10)
2200 2200 assert_include 'assigned-to-my-group', issue.css_classes(Group.find(10).users.first)
2201 2201 assert_not_include 'assigned-to-my-group', issue.css_classes(User.find(3))
2202 2202 end
2203 2203
2204 2204 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2205 2205 set_tmp_attachments_directory
2206 2206 issue = Issue.generate!
2207 2207 issue.save_attachments({
2208 2208 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2209 2209 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2210 2210 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2211 2211 })
2212 2212 issue.attach_saved_attachments
2213 2213
2214 2214 assert_equal 3, issue.reload.attachments.count
2215 2215 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2216 2216 end
2217 2217
2218 2218 def test_closed_on_should_be_nil_when_creating_an_open_issue
2219 2219 issue = Issue.generate!(:status_id => 1).reload
2220 2220 assert !issue.closed?
2221 2221 assert_nil issue.closed_on
2222 2222 end
2223 2223
2224 2224 def test_closed_on_should_be_set_when_creating_a_closed_issue
2225 2225 issue = Issue.generate!(:status_id => 5).reload
2226 2226 assert issue.closed?
2227 2227 assert_not_nil issue.closed_on
2228 2228 assert_equal issue.updated_on, issue.closed_on
2229 2229 assert_equal issue.created_on, issue.closed_on
2230 2230 end
2231 2231
2232 2232 def test_closed_on_should_be_nil_when_updating_an_open_issue
2233 2233 issue = Issue.find(1)
2234 2234 issue.subject = 'Not closed yet'
2235 2235 issue.save!
2236 2236 issue.reload
2237 2237 assert_nil issue.closed_on
2238 2238 end
2239 2239
2240 2240 def test_closed_on_should_be_set_when_closing_an_open_issue
2241 2241 issue = Issue.find(1)
2242 2242 issue.subject = 'Now closed'
2243 2243 issue.status_id = 5
2244 2244 issue.save!
2245 2245 issue.reload
2246 2246 assert_not_nil issue.closed_on
2247 2247 assert_equal issue.updated_on, issue.closed_on
2248 2248 end
2249 2249
2250 2250 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2251 2251 issue = Issue.open(false).first
2252 2252 was_closed_on = issue.closed_on
2253 2253 assert_not_nil was_closed_on
2254 2254 issue.subject = 'Updating a closed issue'
2255 2255 issue.save!
2256 2256 issue.reload
2257 2257 assert_equal was_closed_on, issue.closed_on
2258 2258 end
2259 2259
2260 2260 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2261 2261 issue = Issue.open(false).first
2262 2262 was_closed_on = issue.closed_on
2263 2263 assert_not_nil was_closed_on
2264 2264 issue.subject = 'Reopening a closed issue'
2265 2265 issue.status_id = 1
2266 2266 issue.save!
2267 2267 issue.reload
2268 2268 assert !issue.closed?
2269 2269 assert_equal was_closed_on, issue.closed_on
2270 2270 end
2271 2271
2272 2272 def test_status_was_should_return_nil_for_new_issue
2273 2273 issue = Issue.new
2274 2274 assert_nil issue.status_was
2275 2275 end
2276 2276
2277 2277 def test_status_was_should_return_status_before_change
2278 2278 issue = Issue.find(1)
2279 2279 issue.status = IssueStatus.find(2)
2280 2280 assert_equal IssueStatus.find(1), issue.status_was
2281 2281 end
2282 2282
2283 2283 def test_status_was_should_be_reset_on_save
2284 2284 issue = Issue.find(1)
2285 2285 issue.status = IssueStatus.find(2)
2286 2286 assert_equal IssueStatus.find(1), issue.status_was
2287 2287 assert issue.save!
2288 2288 assert_equal IssueStatus.find(2), issue.status_was
2289 2289 end
2290 2290 end
@@ -1,1135 +1,1135
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 UserTest < ActiveSupport::TestCase
21 21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
22 22 :trackers, :issue_statuses,
23 23 :projects_trackers,
24 24 :watchers,
25 25 :issue_categories, :enumerations, :issues,
26 26 :journals, :journal_details,
27 27 :groups_users,
28 28 :enabled_modules
29 29
30 30 def setup
31 31 @admin = User.find(1)
32 32 @jsmith = User.find(2)
33 33 @dlopper = User.find(3)
34 34 end
35 35
36 36 def test_sorted_scope_should_sort_user_by_display_name
37 37 assert_equal User.all.map(&:name).map(&:downcase).sort, User.sorted.all.map(&:name).map(&:downcase)
38 38 end
39 39
40 40 def test_generate
41 41 User.generate!(:firstname => 'Testing connection')
42 42 User.generate!(:firstname => 'Testing connection')
43 43 assert_equal 2, User.where(:firstname => 'Testing connection').count
44 44 end
45 45
46 46 def test_truth
47 47 assert_kind_of User, @jsmith
48 48 end
49 49
50 50 def test_mail_should_be_stripped
51 51 u = User.new
52 52 u.mail = " foo@bar.com "
53 53 assert_equal "foo@bar.com", u.mail
54 54 end
55 55
56 56 def test_mail_validation
57 57 u = User.new
58 58 u.mail = ''
59 59 assert !u.valid?
60 60 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
61 61 end
62 62
63 63 def test_login_length_validation
64 64 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
65 65 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
66 66 assert !user.valid?
67 67
68 68 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
69 69 assert user.valid?
70 70 assert user.save
71 71 end
72 72
73 73 def test_generate_password_should_respect_minimum_password_length
74 74 with_settings :password_min_length => 15 do
75 75 user = User.generate!(:generate_password => true)
76 76 assert user.password.length >= 15
77 77 end
78 78 end
79 79
80 80 def test_generate_password_should_not_generate_password_with_less_than_10_characters
81 81 with_settings :password_min_length => 4 do
82 82 user = User.generate!(:generate_password => true)
83 83 assert user.password.length >= 10
84 84 end
85 85 end
86 86
87 87 def test_generate_password_on_create_should_set_password
88 88 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
89 89 user.login = "newuser"
90 90 user.generate_password = true
91 91 assert user.save
92 92
93 93 password = user.password
94 94 assert user.check_password?(password)
95 95 end
96 96
97 97 def test_generate_password_on_update_should_update_password
98 98 user = User.find(2)
99 99 hash = user.hashed_password
100 100 user.generate_password = true
101 101 assert user.save
102 102
103 103 password = user.password
104 104 assert user.check_password?(password)
105 105 assert_not_equal hash, user.reload.hashed_password
106 106 end
107 107
108 108 def test_create
109 109 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
110 110
111 111 user.login = "jsmith"
112 112 user.password, user.password_confirmation = "password", "password"
113 113 # login uniqueness
114 114 assert !user.save
115 115 assert_equal 1, user.errors.count
116 116
117 117 user.login = "newuser"
118 118 user.password, user.password_confirmation = "password", "pass"
119 119 # password confirmation
120 120 assert !user.save
121 121 assert_equal 1, user.errors.count
122 122
123 123 user.password, user.password_confirmation = "password", "password"
124 124 assert user.save
125 125 end
126 126
127 127 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
128 128 @user1 = User.generate!
129 129 assert_equal 'only_my_events', @user1.mail_notification
130 130 with_settings :default_notification_option => 'all' do
131 131 @user2 = User.generate!
132 132 assert_equal 'all', @user2.mail_notification
133 133 end
134 134 end
135 135
136 136 def test_user_login_should_be_case_insensitive
137 137 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
138 138 u.login = 'newuser'
139 139 u.password, u.password_confirmation = "password", "password"
140 140 assert u.save
141 141 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
142 142 u.login = 'NewUser'
143 143 u.password, u.password_confirmation = "password", "password"
144 144 assert !u.save
145 145 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
146 146 end
147 147
148 148 def test_mail_uniqueness_should_not_be_case_sensitive
149 149 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
150 150 u.login = 'newuser1'
151 151 u.password, u.password_confirmation = "password", "password"
152 152 assert u.save
153 153
154 154 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
155 155 u.login = 'newuser2'
156 156 u.password, u.password_confirmation = "password", "password"
157 157 assert !u.save
158 158 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
159 159 end
160 160
161 161 def test_update
162 162 assert_equal "admin", @admin.login
163 163 @admin.login = "john"
164 164 assert @admin.save, @admin.errors.full_messages.join("; ")
165 165 @admin.reload
166 166 assert_equal "john", @admin.login
167 167 end
168 168
169 169 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
170 170 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
171 171 u1.login = 'newuser1'
172 172 assert u1.save
173 173
174 174 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
175 175 u2.login = 'newuser1'
176 176 assert u2.save(:validate => false)
177 177
178 178 user = User.find(u2.id)
179 179 user.firstname = "firstname"
180 180 assert user.save, "Save failed"
181 181 end
182 182
183 183 def test_destroy_should_delete_members_and_roles
184 184 members = Member.find_all_by_user_id(2)
185 185 ms = members.size
186 186 rs = members.collect(&:roles).flatten.size
187 187
188 188 assert_difference 'Member.count', - ms do
189 189 assert_difference 'MemberRole.count', - rs do
190 190 User.find(2).destroy
191 191 end
192 192 end
193 193
194 194 assert_nil User.find_by_id(2)
195 195 assert Member.find_all_by_user_id(2).empty?
196 196 end
197 197
198 198 def test_destroy_should_update_attachments
199 199 attachment = Attachment.create!(:container => Project.find(1),
200 200 :file => uploaded_test_file("testfile.txt", "text/plain"),
201 201 :author_id => 2)
202 202
203 203 User.find(2).destroy
204 204 assert_nil User.find_by_id(2)
205 205 assert_equal User.anonymous, attachment.reload.author
206 206 end
207 207
208 208 def test_destroy_should_update_comments
209 209 comment = Comment.create!(
210 210 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
211 211 :author => User.find(2),
212 212 :comments => 'foo'
213 213 )
214 214
215 215 User.find(2).destroy
216 216 assert_nil User.find_by_id(2)
217 217 assert_equal User.anonymous, comment.reload.author
218 218 end
219 219
220 220 def test_destroy_should_update_issues
221 221 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
222 222
223 223 User.find(2).destroy
224 224 assert_nil User.find_by_id(2)
225 225 assert_equal User.anonymous, issue.reload.author
226 226 end
227 227
228 228 def test_destroy_should_unassign_issues
229 229 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
230 230
231 231 User.find(2).destroy
232 232 assert_nil User.find_by_id(2)
233 233 assert_nil issue.reload.assigned_to
234 234 end
235 235
236 236 def test_destroy_should_update_journals
237 237 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
238 238 issue.init_journal(User.find(2), "update")
239 239 issue.save!
240 240
241 241 User.find(2).destroy
242 242 assert_nil User.find_by_id(2)
243 243 assert_equal User.anonymous, issue.journals.first.reload.user
244 244 end
245 245
246 246 def test_destroy_should_update_journal_details_old_value
247 247 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
248 248 issue.init_journal(User.find(1), "update")
249 249 issue.assigned_to_id = nil
250 250 assert_difference 'JournalDetail.count' do
251 251 issue.save!
252 252 end
253 253 journal_detail = JournalDetail.first(:order => 'id DESC')
254 254 assert_equal '2', journal_detail.old_value
255 255
256 256 User.find(2).destroy
257 257 assert_nil User.find_by_id(2)
258 258 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
259 259 end
260 260
261 261 def test_destroy_should_update_journal_details_value
262 262 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
263 263 issue.init_journal(User.find(1), "update")
264 264 issue.assigned_to_id = 2
265 265 assert_difference 'JournalDetail.count' do
266 266 issue.save!
267 267 end
268 268 journal_detail = JournalDetail.first(:order => 'id DESC')
269 269 assert_equal '2', journal_detail.value
270 270
271 271 User.find(2).destroy
272 272 assert_nil User.find_by_id(2)
273 273 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
274 274 end
275 275
276 276 def test_destroy_should_update_messages
277 277 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
278 278 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
279 279
280 280 User.find(2).destroy
281 281 assert_nil User.find_by_id(2)
282 282 assert_equal User.anonymous, message.reload.author
283 283 end
284 284
285 285 def test_destroy_should_update_news
286 286 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
287 287
288 288 User.find(2).destroy
289 289 assert_nil User.find_by_id(2)
290 290 assert_equal User.anonymous, news.reload.author
291 291 end
292 292
293 293 def test_destroy_should_delete_private_queries
294 294 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
295 295 query.project_id = 1
296 296 query.user_id = 2
297 297 query.save!
298 298
299 299 User.find(2).destroy
300 300 assert_nil User.find_by_id(2)
301 301 assert_nil Query.find_by_id(query.id)
302 302 end
303 303
304 304 def test_destroy_should_update_public_queries
305 305 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
306 306 query.project_id = 1
307 307 query.user_id = 2
308 308 query.save!
309 309
310 310 User.find(2).destroy
311 311 assert_nil User.find_by_id(2)
312 312 assert_equal User.anonymous, query.reload.user
313 313 end
314 314
315 315 def test_destroy_should_update_time_entries
316 316 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
317 317 entry.project_id = 1
318 318 entry.user_id = 2
319 319 entry.save!
320 320
321 321 User.find(2).destroy
322 322 assert_nil User.find_by_id(2)
323 323 assert_equal User.anonymous, entry.reload.user
324 324 end
325 325
326 326 def test_destroy_should_delete_tokens
327 327 token = Token.create!(:user_id => 2, :value => 'foo')
328 328
329 329 User.find(2).destroy
330 330 assert_nil User.find_by_id(2)
331 331 assert_nil Token.find_by_id(token.id)
332 332 end
333 333
334 334 def test_destroy_should_delete_watchers
335 335 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
336 336 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
337 337
338 338 User.find(2).destroy
339 339 assert_nil User.find_by_id(2)
340 340 assert_nil Watcher.find_by_id(watcher.id)
341 341 end
342 342
343 343 def test_destroy_should_update_wiki_contents
344 344 wiki_content = WikiContent.create!(
345 345 :text => 'foo',
346 346 :author_id => 2,
347 347 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
348 348 )
349 349 wiki_content.text = 'bar'
350 350 assert_difference 'WikiContent::Version.count' do
351 351 wiki_content.save!
352 352 end
353 353
354 354 User.find(2).destroy
355 355 assert_nil User.find_by_id(2)
356 356 assert_equal User.anonymous, wiki_content.reload.author
357 357 wiki_content.versions.each do |version|
358 358 assert_equal User.anonymous, version.reload.author
359 359 end
360 360 end
361 361
362 362 def test_destroy_should_nullify_issue_categories
363 363 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
364 364
365 365 User.find(2).destroy
366 366 assert_nil User.find_by_id(2)
367 367 assert_nil category.reload.assigned_to_id
368 368 end
369 369
370 370 def test_destroy_should_nullify_changesets
371 371 changeset = Changeset.create!(
372 372 :repository => Repository::Subversion.create!(
373 373 :project_id => 1,
374 374 :url => 'file:///tmp',
375 375 :identifier => 'tmp'
376 376 ),
377 377 :revision => '12',
378 378 :committed_on => Time.now,
379 379 :committer => 'jsmith'
380 380 )
381 381 assert_equal 2, changeset.user_id
382 382
383 383 User.find(2).destroy
384 384 assert_nil User.find_by_id(2)
385 385 assert_nil changeset.reload.user_id
386 386 end
387 387
388 388 def test_anonymous_user_should_not_be_destroyable
389 389 assert_no_difference 'User.count' do
390 390 assert_equal false, User.anonymous.destroy
391 391 end
392 392 end
393 393
394 394 def test_validate_login_presence
395 395 @admin.login = ""
396 396 assert !@admin.save
397 397 assert_equal 1, @admin.errors.count
398 398 end
399 399
400 400 def test_validate_mail_notification_inclusion
401 401 u = User.new
402 402 u.mail_notification = 'foo'
403 403 u.save
404 assert_not_nil u.errors[:mail_notification]
404 assert_not_equal [], u.errors[:mail_notification]
405 405 end
406 406
407 407 def test_password
408 408 user = User.try_to_login("admin", "admin")
409 409 assert_kind_of User, user
410 410 assert_equal "admin", user.login
411 411 user.password = "hello123"
412 412 assert user.save
413 413
414 414 user = User.try_to_login("admin", "hello123")
415 415 assert_kind_of User, user
416 416 assert_equal "admin", user.login
417 417 end
418 418
419 419 def test_validate_password_length
420 420 with_settings :password_min_length => '100' do
421 421 user = User.new(:firstname => "new100", :lastname => "user100", :mail => "newuser100@somenet.foo")
422 422 user.login = "newuser100"
423 423 user.password, user.password_confirmation = "password100", "password100"
424 424 assert !user.save
425 425 assert_equal 1, user.errors.count
426 426 end
427 427 end
428 428
429 429 def test_name_format
430 430 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
431 431 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
432 432 with_settings :user_format => :firstname_lastname do
433 433 assert_equal 'John Smith', @jsmith.reload.name
434 434 end
435 435 with_settings :user_format => :username do
436 436 assert_equal 'jsmith', @jsmith.reload.name
437 437 end
438 438 with_settings :user_format => :lastname do
439 439 assert_equal 'Smith', @jsmith.reload.name
440 440 end
441 441 end
442 442
443 443 def test_today_should_return_the_day_according_to_user_time_zone
444 444 preference = User.find(1).pref
445 445 date = Date.new(2012, 05, 15)
446 446 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
447 447 Date.stubs(:today).returns(date)
448 448 Time.stubs(:now).returns(time)
449 449
450 450 preference.update_attribute :time_zone, 'Baku' # UTC+4
451 451 assert_equal '2012-05-16', User.find(1).today.to_s
452 452
453 453 preference.update_attribute :time_zone, 'La Paz' # UTC-4
454 454 assert_equal '2012-05-15', User.find(1).today.to_s
455 455
456 456 preference.update_attribute :time_zone, ''
457 457 assert_equal '2012-05-15', User.find(1).today.to_s
458 458 end
459 459
460 460 def test_time_to_date_should_return_the_date_according_to_user_time_zone
461 461 preference = User.find(1).pref
462 462 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
463 463
464 464 preference.update_attribute :time_zone, 'Baku' # UTC+4
465 465 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
466 466
467 467 preference.update_attribute :time_zone, 'La Paz' # UTC-4
468 468 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
469 469
470 470 preference.update_attribute :time_zone, ''
471 471 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
472 472 end
473 473
474 474 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
475 475 with_settings :user_format => 'lastname_coma_firstname' do
476 476 assert_equal ['users.lastname', 'users.firstname', 'users.id'], User.fields_for_order_statement
477 477 end
478 478 end
479 479
480 480 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
481 481 with_settings :user_format => 'lastname_firstname' do
482 482 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'], User.fields_for_order_statement('authors')
483 483 end
484 484 end
485 485
486 486 def test_fields_for_order_statement_with_blank_format_should_return_default
487 487 with_settings :user_format => '' do
488 488 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
489 489 end
490 490 end
491 491
492 492 def test_fields_for_order_statement_with_invalid_format_should_return_default
493 493 with_settings :user_format => 'foo' do
494 494 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
495 495 end
496 496 end
497 497
498 498 test ".try_to_login with good credentials should return the user" do
499 499 user = User.try_to_login("admin", "admin")
500 500 assert_kind_of User, user
501 501 assert_equal "admin", user.login
502 502 end
503 503
504 504 test ".try_to_login with wrong credentials should return nil" do
505 505 assert_nil User.try_to_login("admin", "foo")
506 506 end
507 507
508 508 def test_try_to_login_with_locked_user_should_return_nil
509 509 @jsmith.status = User::STATUS_LOCKED
510 510 @jsmith.save!
511 511
512 512 user = User.try_to_login("jsmith", "jsmith")
513 513 assert_equal nil, user
514 514 end
515 515
516 516 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
517 517 @jsmith.status = User::STATUS_LOCKED
518 518 @jsmith.save!
519 519
520 520 user = User.try_to_login("jsmith", "jsmith", false)
521 521 assert_equal @jsmith, user
522 522 end
523 523
524 524 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
525 525 user = User.try_to_login("AdMin", "admin")
526 526 assert_kind_of User, user
527 527 assert_equal "admin", user.login
528 528 end
529 529
530 530 test ".try_to_login should select the exact matching user first" do
531 531 case_sensitive_user = User.generate! do |user|
532 532 user.password = "admin123"
533 533 end
534 534 # bypass validations to make it appear like existing data
535 535 case_sensitive_user.update_attribute(:login, 'ADMIN')
536 536
537 537 user = User.try_to_login("ADMIN", "admin123")
538 538 assert_kind_of User, user
539 539 assert_equal "ADMIN", user.login
540 540 end
541 541
542 542 if ldap_configured?
543 543 context "#try_to_login using LDAP" do
544 544 context "with failed connection to the LDAP server" do
545 545 should "return nil" do
546 546 @auth_source = AuthSourceLdap.find(1)
547 547 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
548 548
549 549 assert_equal nil, User.try_to_login('edavis', 'wrong')
550 550 end
551 551 end
552 552
553 553 context "with an unsuccessful authentication" do
554 554 should "return nil" do
555 555 assert_equal nil, User.try_to_login('edavis', 'wrong')
556 556 end
557 557 end
558 558
559 559 context "binding with user's account" do
560 560 setup do
561 561 @auth_source = AuthSourceLdap.find(1)
562 562 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
563 563 @auth_source.account_password = ''
564 564 @auth_source.save!
565 565
566 566 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
567 567 @ldap_user.login = 'example1'
568 568 @ldap_user.save!
569 569 end
570 570
571 571 context "with a successful authentication" do
572 572 should "return the user" do
573 573 assert_equal @ldap_user, User.try_to_login('example1', '123456')
574 574 end
575 575 end
576 576
577 577 context "with an unsuccessful authentication" do
578 578 should "return nil" do
579 579 assert_nil User.try_to_login('example1', '11111')
580 580 end
581 581 end
582 582 end
583 583
584 584 context "on the fly registration" do
585 585 setup do
586 586 @auth_source = AuthSourceLdap.find(1)
587 587 @auth_source.update_attribute :onthefly_register, true
588 588 end
589 589
590 590 context "with a successful authentication" do
591 591 should "create a new user account if it doesn't exist" do
592 592 assert_difference('User.count') do
593 593 user = User.try_to_login('edavis', '123456')
594 594 assert !user.admin?
595 595 end
596 596 end
597 597
598 598 should "retrieve existing user" do
599 599 user = User.try_to_login('edavis', '123456')
600 600 user.admin = true
601 601 user.save!
602 602
603 603 assert_no_difference('User.count') do
604 604 user = User.try_to_login('edavis', '123456')
605 605 assert user.admin?
606 606 end
607 607 end
608 608 end
609 609
610 610 context "binding with user's account" do
611 611 setup do
612 612 @auth_source = AuthSourceLdap.find(1)
613 613 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
614 614 @auth_source.account_password = ''
615 615 @auth_source.save!
616 616 end
617 617
618 618 context "with a successful authentication" do
619 619 should "create a new user account if it doesn't exist" do
620 620 assert_difference('User.count') do
621 621 user = User.try_to_login('example1', '123456')
622 622 assert_kind_of User, user
623 623 end
624 624 end
625 625 end
626 626
627 627 context "with an unsuccessful authentication" do
628 628 should "return nil" do
629 629 assert_nil User.try_to_login('example1', '11111')
630 630 end
631 631 end
632 632 end
633 633 end
634 634 end
635 635
636 636 else
637 637 puts "Skipping LDAP tests."
638 638 end
639 639
640 640 def test_create_anonymous
641 641 AnonymousUser.delete_all
642 642 anon = User.anonymous
643 643 assert !anon.new_record?
644 644 assert_kind_of AnonymousUser, anon
645 645 end
646 646
647 647 def test_ensure_single_anonymous_user
648 648 AnonymousUser.delete_all
649 649 anon1 = User.anonymous
650 650 assert !anon1.new_record?
651 651 assert_kind_of AnonymousUser, anon1
652 652 anon2 = AnonymousUser.create(
653 653 :lastname => 'Anonymous', :firstname => '',
654 654 :mail => '', :login => '', :status => 0)
655 655 assert_equal 1, anon2.errors.count
656 656 end
657 657
658 658 def test_rss_key
659 659 assert_nil @jsmith.rss_token
660 660 key = @jsmith.rss_key
661 661 assert_equal 40, key.length
662 662
663 663 @jsmith.reload
664 664 assert_equal key, @jsmith.rss_key
665 665 end
666 666
667 667 def test_rss_key_should_not_be_generated_twice
668 668 assert_difference 'Token.count', 1 do
669 669 key1 = @jsmith.rss_key
670 670 key2 = @jsmith.rss_key
671 671 assert_equal key1, key2
672 672 end
673 673 end
674 674
675 675 def test_api_key_should_not_be_generated_twice
676 676 assert_difference 'Token.count', 1 do
677 677 key1 = @jsmith.api_key
678 678 key2 = @jsmith.api_key
679 679 assert_equal key1, key2
680 680 end
681 681 end
682 682
683 683 test "#api_key should generate a new one if the user doesn't have one" do
684 684 user = User.generate!(:api_token => nil)
685 685 assert_nil user.api_token
686 686
687 687 key = user.api_key
688 688 assert_equal 40, key.length
689 689 user.reload
690 690 assert_equal key, user.api_key
691 691 end
692 692
693 693 test "#api_key should return the existing api token value" do
694 694 user = User.generate!
695 695 token = Token.create!(:action => 'api')
696 696 user.api_token = token
697 697 assert user.save
698 698
699 699 assert_equal token.value, user.api_key
700 700 end
701 701
702 702 test "#find_by_api_key should return nil if no matching key is found" do
703 703 assert_nil User.find_by_api_key('zzzzzzzzz')
704 704 end
705 705
706 706 test "#find_by_api_key should return nil if the key is found for an inactive user" do
707 707 user = User.generate!
708 708 user.status = User::STATUS_LOCKED
709 709 token = Token.create!(:action => 'api')
710 710 user.api_token = token
711 711 user.save
712 712
713 713 assert_nil User.find_by_api_key(token.value)
714 714 end
715 715
716 716 test "#find_by_api_key should return the user if the key is found for an active user" do
717 717 user = User.generate!
718 718 token = Token.create!(:action => 'api')
719 719 user.api_token = token
720 720 user.save
721 721
722 722 assert_equal user, User.find_by_api_key(token.value)
723 723 end
724 724
725 725 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
726 726 user = User.find_by_login("admin")
727 727 user.password = "admin"
728 728 assert user.save(:validate => false)
729 729
730 730 assert_equal false, User.default_admin_account_changed?
731 731 end
732 732
733 733 def test_default_admin_account_changed_should_return_true_if_password_was_changed
734 734 user = User.find_by_login("admin")
735 735 user.password = "newpassword"
736 736 user.save!
737 737
738 738 assert_equal true, User.default_admin_account_changed?
739 739 end
740 740
741 741 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
742 742 user = User.find_by_login("admin")
743 743 user.password = "admin"
744 744 user.status = User::STATUS_LOCKED
745 745 assert user.save(:validate => false)
746 746
747 747 assert_equal true, User.default_admin_account_changed?
748 748 end
749 749
750 750 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
751 751 user = User.find_by_login("admin")
752 752 user.destroy
753 753
754 754 assert_equal true, User.default_admin_account_changed?
755 755 end
756 756
757 757 def test_membership_with_project_should_return_membership
758 758 project = Project.find(1)
759 759
760 760 membership = @jsmith.membership(project)
761 761 assert_kind_of Member, membership
762 762 assert_equal @jsmith, membership.user
763 763 assert_equal project, membership.project
764 764 end
765 765
766 766 def test_membership_with_project_id_should_return_membership
767 767 project = Project.find(1)
768 768
769 769 membership = @jsmith.membership(1)
770 770 assert_kind_of Member, membership
771 771 assert_equal @jsmith, membership.user
772 772 assert_equal project, membership.project
773 773 end
774 774
775 775 def test_membership_for_non_member_should_return_nil
776 776 project = Project.find(1)
777 777
778 778 user = User.generate!
779 779 membership = user.membership(1)
780 780 assert_nil membership
781 781 end
782 782
783 783 def test_roles_for_project
784 784 # user with a role
785 785 roles = @jsmith.roles_for_project(Project.find(1))
786 786 assert_kind_of Role, roles.first
787 787 assert_equal "Manager", roles.first.name
788 788
789 789 # user with no role
790 790 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
791 791 end
792 792
793 793 def test_projects_by_role_for_user_with_role
794 794 user = User.find(2)
795 795 assert_kind_of Hash, user.projects_by_role
796 796 assert_equal 2, user.projects_by_role.size
797 797 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
798 798 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
799 799 end
800 800
801 801 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
802 802 user = User.find(2)
803 803 assert_equal [], user.projects_by_role[Role.find(3)]
804 804 # should not update the hash
805 805 assert_nil user.projects_by_role.values.detect(&:blank?)
806 806 end
807 807
808 808 def test_projects_by_role_for_user_with_no_role
809 809 user = User.generate!
810 810 assert_equal({}, user.projects_by_role)
811 811 end
812 812
813 813 def test_projects_by_role_for_anonymous
814 814 assert_equal({}, User.anonymous.projects_by_role)
815 815 end
816 816
817 817 def test_valid_notification_options
818 818 # without memberships
819 819 assert_equal 5, User.find(7).valid_notification_options.size
820 820 # with memberships
821 821 assert_equal 6, User.find(2).valid_notification_options.size
822 822 end
823 823
824 824 def test_valid_notification_options_class_method
825 825 assert_equal 5, User.valid_notification_options.size
826 826 assert_equal 5, User.valid_notification_options(User.find(7)).size
827 827 assert_equal 6, User.valid_notification_options(User.find(2)).size
828 828 end
829 829
830 830 def test_mail_notification_all
831 831 @jsmith.mail_notification = 'all'
832 832 @jsmith.notified_project_ids = []
833 833 @jsmith.save
834 834 @jsmith.reload
835 835 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
836 836 end
837 837
838 838 def test_mail_notification_selected
839 839 @jsmith.mail_notification = 'selected'
840 840 @jsmith.notified_project_ids = [1]
841 841 @jsmith.save
842 842 @jsmith.reload
843 843 assert Project.find(1).recipients.include?(@jsmith.mail)
844 844 end
845 845
846 846 def test_mail_notification_only_my_events
847 847 @jsmith.mail_notification = 'only_my_events'
848 848 @jsmith.notified_project_ids = []
849 849 @jsmith.save
850 850 @jsmith.reload
851 851 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
852 852 end
853 853
854 854 def test_comments_sorting_preference
855 855 assert !@jsmith.wants_comments_in_reverse_order?
856 856 @jsmith.pref.comments_sorting = 'asc'
857 857 assert !@jsmith.wants_comments_in_reverse_order?
858 858 @jsmith.pref.comments_sorting = 'desc'
859 859 assert @jsmith.wants_comments_in_reverse_order?
860 860 end
861 861
862 862 def test_find_by_mail_should_be_case_insensitive
863 863 u = User.find_by_mail('JSmith@somenet.foo')
864 864 assert_not_nil u
865 865 assert_equal 'jsmith@somenet.foo', u.mail
866 866 end
867 867
868 868 def test_random_password
869 869 u = User.new
870 870 u.random_password
871 871 assert !u.password.blank?
872 872 assert !u.password_confirmation.blank?
873 873 end
874 874
875 875 test "#change_password_allowed? should be allowed if no auth source is set" do
876 876 user = User.generate!
877 877 assert user.change_password_allowed?
878 878 end
879 879
880 880 test "#change_password_allowed? should delegate to the auth source" do
881 881 user = User.generate!
882 882
883 883 allowed_auth_source = AuthSource.generate!
884 884 def allowed_auth_source.allow_password_changes?; true; end
885 885
886 886 denied_auth_source = AuthSource.generate!
887 887 def denied_auth_source.allow_password_changes?; false; end
888 888
889 889 assert user.change_password_allowed?
890 890
891 891 user.auth_source = allowed_auth_source
892 892 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
893 893
894 894 user.auth_source = denied_auth_source
895 895 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
896 896 end
897 897
898 898 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
899 899 with_settings :unsubscribe => '1' do
900 900 assert_equal true, User.find(2).own_account_deletable?
901 901 end
902 902 end
903 903
904 904 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
905 905 with_settings :unsubscribe => '0' do
906 906 assert_equal false, User.find(2).own_account_deletable?
907 907 end
908 908 end
909 909
910 910 def test_own_account_deletable_should_be_false_for_a_single_admin
911 911 User.delete_all(["admin = ? AND id <> ?", true, 1])
912 912
913 913 with_settings :unsubscribe => '1' do
914 914 assert_equal false, User.find(1).own_account_deletable?
915 915 end
916 916 end
917 917
918 918 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
919 919 User.generate! do |user|
920 920 user.admin = true
921 921 end
922 922
923 923 with_settings :unsubscribe => '1' do
924 924 assert_equal true, User.find(1).own_account_deletable?
925 925 end
926 926 end
927 927
928 928 context "#allowed_to?" do
929 929 context "with a unique project" do
930 930 should "return false if project is archived" do
931 931 project = Project.find(1)
932 932 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
933 933 assert_equal false, @admin.allowed_to?(:view_issues, Project.find(1))
934 934 end
935 935
936 936 should "return false for write action if project is closed" do
937 937 project = Project.find(1)
938 938 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
939 939 assert_equal false, @admin.allowed_to?(:edit_project, Project.find(1))
940 940 end
941 941
942 942 should "return true for read action if project is closed" do
943 943 project = Project.find(1)
944 944 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
945 945 assert_equal true, @admin.allowed_to?(:view_project, Project.find(1))
946 946 end
947 947
948 948 should "return false if related module is disabled" do
949 949 project = Project.find(1)
950 950 project.enabled_module_names = ["issue_tracking"]
951 951 assert_equal true, @admin.allowed_to?(:add_issues, project)
952 952 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
953 953 end
954 954
955 955 should "authorize nearly everything for admin users" do
956 956 project = Project.find(1)
957 957 assert ! @admin.member_of?(project)
958 958 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
959 959 assert_equal true, @admin.allowed_to?(p.to_sym, project)
960 960 end
961 961 end
962 962
963 963 should "authorize normal users depending on their roles" do
964 964 project = Project.find(1)
965 965 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
966 966 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
967 967 end
968 968 end
969 969
970 970 context "with multiple projects" do
971 971 should "return false if array is empty" do
972 972 assert_equal false, @admin.allowed_to?(:view_project, [])
973 973 end
974 974
975 975 should "return true only if user has permission on all these projects" do
976 976 assert_equal true, @admin.allowed_to?(:view_project, Project.all)
977 977 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
978 978 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
979 979 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
980 980 end
981 981
982 982 should "behave correctly with arrays of 1 project" do
983 983 assert_equal false, User.anonymous.allowed_to?(:delete_issues, [Project.first])
984 984 end
985 985 end
986 986
987 987 context "with options[:global]" do
988 988 should "authorize if user has at least one role that has this permission" do
989 989 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
990 990 @anonymous = User.find(6)
991 991 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
992 992 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
993 993 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
994 994 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
995 995 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
996 996 end
997 997 end
998 998 end
999 999
1000 1000 context "User#notify_about?" do
1001 1001 context "Issues" do
1002 1002 setup do
1003 1003 @project = Project.find(1)
1004 1004 @author = User.generate!
1005 1005 @assignee = User.generate!
1006 1006 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1007 1007 end
1008 1008
1009 1009 should "be true for a user with :all" do
1010 1010 @author.update_attribute(:mail_notification, 'all')
1011 1011 assert @author.notify_about?(@issue)
1012 1012 end
1013 1013
1014 1014 should "be false for a user with :none" do
1015 1015 @author.update_attribute(:mail_notification, 'none')
1016 1016 assert ! @author.notify_about?(@issue)
1017 1017 end
1018 1018
1019 1019 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
1020 1020 @user = User.generate!(:mail_notification => 'only_my_events')
1021 1021 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1022 1022 assert ! @user.notify_about?(@issue)
1023 1023 end
1024 1024
1025 1025 should "be true for a user with :only_my_events and is the author" do
1026 1026 @author.update_attribute(:mail_notification, 'only_my_events')
1027 1027 assert @author.notify_about?(@issue)
1028 1028 end
1029 1029
1030 1030 should "be true for a user with :only_my_events and is the assignee" do
1031 1031 @assignee.update_attribute(:mail_notification, 'only_my_events')
1032 1032 assert @assignee.notify_about?(@issue)
1033 1033 end
1034 1034
1035 1035 should "be true for a user with :only_assigned and is the assignee" do
1036 1036 @assignee.update_attribute(:mail_notification, 'only_assigned')
1037 1037 assert @assignee.notify_about?(@issue)
1038 1038 end
1039 1039
1040 1040 should "be false for a user with :only_assigned and is not the assignee" do
1041 1041 @author.update_attribute(:mail_notification, 'only_assigned')
1042 1042 assert ! @author.notify_about?(@issue)
1043 1043 end
1044 1044
1045 1045 should "be true for a user with :only_owner and is the author" do
1046 1046 @author.update_attribute(:mail_notification, 'only_owner')
1047 1047 assert @author.notify_about?(@issue)
1048 1048 end
1049 1049
1050 1050 should "be false for a user with :only_owner and is not the author" do
1051 1051 @assignee.update_attribute(:mail_notification, 'only_owner')
1052 1052 assert ! @assignee.notify_about?(@issue)
1053 1053 end
1054 1054
1055 1055 should "be true for a user with :selected and is the author" do
1056 1056 @author.update_attribute(:mail_notification, 'selected')
1057 1057 assert @author.notify_about?(@issue)
1058 1058 end
1059 1059
1060 1060 should "be true for a user with :selected and is the assignee" do
1061 1061 @assignee.update_attribute(:mail_notification, 'selected')
1062 1062 assert @assignee.notify_about?(@issue)
1063 1063 end
1064 1064
1065 1065 should "be false for a user with :selected and is not the author or assignee" do
1066 1066 @user = User.generate!(:mail_notification => 'selected')
1067 1067 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1068 1068 assert ! @user.notify_about?(@issue)
1069 1069 end
1070 1070 end
1071 1071 end
1072 1072
1073 1073 def test_notify_about_news
1074 1074 user = User.generate!
1075 1075 news = News.new
1076 1076
1077 1077 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1078 1078 user.mail_notification = option
1079 1079 assert_equal (option != 'none'), user.notify_about?(news)
1080 1080 end
1081 1081 end
1082 1082
1083 1083 def test_salt_unsalted_passwords
1084 1084 # Restore a user with an unsalted password
1085 1085 user = User.find(1)
1086 1086 user.salt = nil
1087 1087 user.hashed_password = User.hash_password("unsalted")
1088 1088 user.save!
1089 1089
1090 1090 User.salt_unsalted_passwords!
1091 1091
1092 1092 user.reload
1093 1093 # Salt added
1094 1094 assert !user.salt.blank?
1095 1095 # Password still valid
1096 1096 assert user.check_password?("unsalted")
1097 1097 assert_equal user, User.try_to_login(user.login, "unsalted")
1098 1098 end
1099 1099
1100 1100 if Object.const_defined?(:OpenID)
1101 1101
1102 1102 def test_setting_identity_url
1103 1103 normalized_open_id_url = 'http://example.com/'
1104 1104 u = User.new( :identity_url => 'http://example.com/' )
1105 1105 assert_equal normalized_open_id_url, u.identity_url
1106 1106 end
1107 1107
1108 1108 def test_setting_identity_url_without_trailing_slash
1109 1109 normalized_open_id_url = 'http://example.com/'
1110 1110 u = User.new( :identity_url => 'http://example.com' )
1111 1111 assert_equal normalized_open_id_url, u.identity_url
1112 1112 end
1113 1113
1114 1114 def test_setting_identity_url_without_protocol
1115 1115 normalized_open_id_url = 'http://example.com/'
1116 1116 u = User.new( :identity_url => 'example.com' )
1117 1117 assert_equal normalized_open_id_url, u.identity_url
1118 1118 end
1119 1119
1120 1120 def test_setting_blank_identity_url
1121 1121 u = User.new( :identity_url => 'example.com' )
1122 1122 u.identity_url = ''
1123 1123 assert u.identity_url.blank?
1124 1124 end
1125 1125
1126 1126 def test_setting_invalid_identity_url
1127 1127 u = User.new( :identity_url => 'this is not an openid url' )
1128 1128 assert u.identity_url.blank?
1129 1129 end
1130 1130
1131 1131 else
1132 1132 puts "Skipping openid tests."
1133 1133 end
1134 1134
1135 1135 end
General Comments 0
You need to be logged in to leave comments. Login now