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