##// END OF EJS Templates
Remove Issue.generate_for_project! test helper and use Issue.generate! instead....
Jean-Philippe Lang -
r10400:8bde60dc56e4
parent child
Show More
@@ -1,132 +1,121
1 module ObjectHelpers
1 module ObjectHelpers
2 def User.generate!(attributes={})
2 def User.generate!(attributes={})
3 @generated_user_login ||= 'user0'
3 @generated_user_login ||= 'user0'
4 @generated_user_login.succ!
4 @generated_user_login.succ!
5 user = User.new(attributes)
5 user = User.new(attributes)
6 user.login = @generated_user_login if user.login.blank?
6 user.login = @generated_user_login if user.login.blank?
7 user.mail = "#{@generated_user_login}@example.com" if user.mail.blank?
7 user.mail = "#{@generated_user_login}@example.com" if user.mail.blank?
8 user.firstname = "Bob" if user.firstname.blank?
8 user.firstname = "Bob" if user.firstname.blank?
9 user.lastname = "Doe" if user.lastname.blank?
9 user.lastname = "Doe" if user.lastname.blank?
10 yield user if block_given?
10 yield user if block_given?
11 user.save!
11 user.save!
12 user
12 user
13 end
13 end
14
14
15 def User.add_to_project(user, project, roles=nil)
15 def User.add_to_project(user, project, roles=nil)
16 roles = Role.find(1) if roles.nil?
16 roles = Role.find(1) if roles.nil?
17 roles = [roles] unless roles.is_a?(Array)
17 roles = [roles] unless roles.is_a?(Array)
18 Member.create!(:principal => user, :project => project, :roles => roles)
18 Member.create!(:principal => user, :project => project, :roles => roles)
19 end
19 end
20
20
21 def Group.generate!(attributes={})
21 def Group.generate!(attributes={})
22 @generated_group_name ||= 'Group 0'
22 @generated_group_name ||= 'Group 0'
23 @generated_group_name.succ!
23 @generated_group_name.succ!
24 group = Group.new(attributes)
24 group = Group.new(attributes)
25 group.name = @generated_group_name if group.name.blank?
25 group.name = @generated_group_name if group.name.blank?
26 yield group if block_given?
26 yield group if block_given?
27 group.save!
27 group.save!
28 group
28 group
29 end
29 end
30
30
31 def Project.generate!(attributes={})
31 def Project.generate!(attributes={})
32 @generated_project_identifier ||= 'project-0000'
32 @generated_project_identifier ||= 'project-0000'
33 @generated_project_identifier.succ!
33 @generated_project_identifier.succ!
34 project = Project.new(attributes)
34 project = Project.new(attributes)
35 project.name = @generated_project_identifier if project.name.blank?
35 project.name = @generated_project_identifier if project.name.blank?
36 project.identifier = @generated_project_identifier if project.identifier.blank?
36 project.identifier = @generated_project_identifier if project.identifier.blank?
37 yield project if block_given?
37 yield project if block_given?
38 project.save!
38 project.save!
39 project
39 project
40 end
40 end
41
41
42 def Tracker.generate!(attributes={})
42 def Tracker.generate!(attributes={})
43 @generated_tracker_name ||= 'Tracker 0'
43 @generated_tracker_name ||= 'Tracker 0'
44 @generated_tracker_name.succ!
44 @generated_tracker_name.succ!
45 tracker = Tracker.new(attributes)
45 tracker = Tracker.new(attributes)
46 tracker.name = @generated_tracker_name if tracker.name.blank?
46 tracker.name = @generated_tracker_name if tracker.name.blank?
47 yield tracker if block_given?
47 yield tracker if block_given?
48 tracker.save!
48 tracker.save!
49 tracker
49 tracker
50 end
50 end
51
51
52 def Role.generate!(attributes={})
52 def Role.generate!(attributes={})
53 @generated_role_name ||= 'Role 0'
53 @generated_role_name ||= 'Role 0'
54 @generated_role_name.succ!
54 @generated_role_name.succ!
55 role = Role.new(attributes)
55 role = Role.new(attributes)
56 role.name = @generated_role_name if role.name.blank?
56 role.name = @generated_role_name if role.name.blank?
57 yield role if block_given?
57 yield role if block_given?
58 role.save!
58 role.save!
59 role
59 role
60 end
60 end
61
61
62 def Issue.generate!(attributes={})
62 def Issue.generate!(attributes={})
63 issue = Issue.new(attributes)
63 issue = Issue.new(attributes)
64 issue.project ||= Project.find(1)
65 issue.tracker ||= issue.project.trackers.first
64 issue.subject = 'Generated' if issue.subject.blank?
66 issue.subject = 'Generated' if issue.subject.blank?
65 issue.author ||= User.find(2)
67 issue.author ||= User.find(2)
66 yield issue if block_given?
68 yield issue if block_given?
67 issue.save!
69 issue.save!
68 issue
70 issue
69 end
71 end
70
72
71 # Generate an issue for a project, using its trackers
72 def Issue.generate_for_project!(project, attributes={})
73 issue = Issue.new(attributes) do |issue|
74 issue.project = project
75 issue.tracker = project.trackers.first unless project.trackers.empty?
76 issue.subject = 'Generated' if issue.subject.blank?
77 issue.author ||= User.find(2)
78 yield issue if block_given?
79 end
80 issue.save!
81 issue
82 end
83
84 # Generates an issue with some children and a grandchild
73 # Generates an issue with some children and a grandchild
85 def Issue.generate_with_descendants!(project, attributes={})
74 def Issue.generate_with_descendants!(project, attributes={})
86 issue = Issue.generate_for_project!(project, attributes)
75 issue = Issue.generate!(attributes.merge(:project => project))
87 child = Issue.generate_for_project!(project, :subject => 'Child1', :parent_issue_id => issue.id)
76 child = Issue.generate!(:project => project, :subject => 'Child1', :parent_issue_id => issue.id)
88 Issue.generate_for_project!(project, :subject => 'Child2', :parent_issue_id => issue.id)
77 Issue.generate!(:project => project, :subject => 'Child2', :parent_issue_id => issue.id)
89 Issue.generate_for_project!(project, :subject => 'Child11', :parent_issue_id => child.id)
78 Issue.generate!(:project => project, :subject => 'Child11', :parent_issue_id => child.id)
90 issue.reload
79 issue.reload
91 end
80 end
92
81
93 def Journal.generate!(attributes={})
82 def Journal.generate!(attributes={})
94 journal = Journal.new(attributes)
83 journal = Journal.new(attributes)
95 journal.user ||= User.first
84 journal.user ||= User.first
96 journal.journalized ||= Issue.first
85 journal.journalized ||= Issue.first
97 yield journal if block_given?
86 yield journal if block_given?
98 journal.save!
87 journal.save!
99 journal
88 journal
100 end
89 end
101
90
102 def Version.generate!(attributes={})
91 def Version.generate!(attributes={})
103 @generated_version_name ||= 'Version 0'
92 @generated_version_name ||= 'Version 0'
104 @generated_version_name.succ!
93 @generated_version_name.succ!
105 version = Version.new(attributes)
94 version = Version.new(attributes)
106 version.name = @generated_version_name if version.name.blank?
95 version.name = @generated_version_name if version.name.blank?
107 yield version if block_given?
96 yield version if block_given?
108 version.save!
97 version.save!
109 version
98 version
110 end
99 end
111
100
112 def AuthSource.generate!(attributes={})
101 def AuthSource.generate!(attributes={})
113 @generated_auth_source_name ||= 'Auth 0'
102 @generated_auth_source_name ||= 'Auth 0'
114 @generated_auth_source_name.succ!
103 @generated_auth_source_name.succ!
115 source = AuthSource.new(attributes)
104 source = AuthSource.new(attributes)
116 source.name = @generated_auth_source_name if source.name.blank?
105 source.name = @generated_auth_source_name if source.name.blank?
117 yield source if block_given?
106 yield source if block_given?
118 source.save!
107 source.save!
119 source
108 source
120 end
109 end
121
110
122 def Board.generate!(attributes={})
111 def Board.generate!(attributes={})
123 @generated_board_name ||= 'Forum 0'
112 @generated_board_name ||= 'Forum 0'
124 @generated_board_name.succ!
113 @generated_board_name.succ!
125 board = Board.new(attributes)
114 board = Board.new(attributes)
126 board.name = @generated_board_name if board.name.blank?
115 board.name = @generated_board_name if board.name.blank?
127 board.description = @generated_board_name if board.description.blank?
116 board.description = @generated_board_name if board.description.blank?
128 yield board if block_given?
117 yield board if block_given?
129 board.save!
118 board.save!
130 board
119 board
131 end
120 end
132 end
121 end
@@ -1,1652 +1,1648
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssueTest < ActiveSupport::TestCase
20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :members, :member_roles, :roles,
21 fixtures :projects, :users, :members, :member_roles, :roles,
22 :groups_users,
22 :groups_users,
23 :trackers, :projects_trackers,
23 :trackers, :projects_trackers,
24 :enabled_modules,
24 :enabled_modules,
25 :versions,
25 :versions,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 :enumerations,
27 :enumerations,
28 :issues, :journals, :journal_details,
28 :issues, :journals, :journal_details,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 :time_entries
30 :time_entries
31
31
32 include Redmine::I18n
32 include Redmine::I18n
33
33
34 def teardown
34 def teardown
35 User.current = nil
35 User.current = nil
36 end
36 end
37
37
38 def test_create
38 def test_create
39 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
39 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
40 :status_id => 1, :priority => IssuePriority.all.first,
40 :status_id => 1, :priority => IssuePriority.all.first,
41 :subject => 'test_create',
41 :subject => 'test_create',
42 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
42 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
43 assert issue.save
43 assert issue.save
44 issue.reload
44 issue.reload
45 assert_equal 1.5, issue.estimated_hours
45 assert_equal 1.5, issue.estimated_hours
46 end
46 end
47
47
48 def test_create_minimal
48 def test_create_minimal
49 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
49 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
50 :status_id => 1, :priority => IssuePriority.all.first,
50 :status_id => 1, :priority => IssuePriority.all.first,
51 :subject => 'test_create')
51 :subject => 'test_create')
52 assert issue.save
52 assert issue.save
53 assert issue.description.nil?
53 assert issue.description.nil?
54 assert_nil issue.estimated_hours
54 assert_nil issue.estimated_hours
55 end
55 end
56
56
57 def test_create_with_required_custom_field
57 def test_create_with_required_custom_field
58 set_language_if_valid 'en'
58 set_language_if_valid 'en'
59 field = IssueCustomField.find_by_name('Database')
59 field = IssueCustomField.find_by_name('Database')
60 field.update_attribute(:is_required, true)
60 field.update_attribute(:is_required, true)
61
61
62 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
62 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
63 :status_id => 1, :subject => 'test_create',
63 :status_id => 1, :subject => 'test_create',
64 :description => 'IssueTest#test_create_with_required_custom_field')
64 :description => 'IssueTest#test_create_with_required_custom_field')
65 assert issue.available_custom_fields.include?(field)
65 assert issue.available_custom_fields.include?(field)
66 # No value for the custom field
66 # No value for the custom field
67 assert !issue.save
67 assert !issue.save
68 assert_equal ["Database can't be blank"], issue.errors.full_messages
68 assert_equal ["Database can't be blank"], issue.errors.full_messages
69 # Blank value
69 # Blank value
70 issue.custom_field_values = { field.id => '' }
70 issue.custom_field_values = { field.id => '' }
71 assert !issue.save
71 assert !issue.save
72 assert_equal ["Database can't be blank"], issue.errors.full_messages
72 assert_equal ["Database can't be blank"], issue.errors.full_messages
73 # Invalid value
73 # Invalid value
74 issue.custom_field_values = { field.id => 'SQLServer' }
74 issue.custom_field_values = { field.id => 'SQLServer' }
75 assert !issue.save
75 assert !issue.save
76 assert_equal ["Database is not included in the list"], issue.errors.full_messages
76 assert_equal ["Database is not included in the list"], issue.errors.full_messages
77 # Valid value
77 # Valid value
78 issue.custom_field_values = { field.id => 'PostgreSQL' }
78 issue.custom_field_values = { field.id => 'PostgreSQL' }
79 assert issue.save
79 assert issue.save
80 issue.reload
80 issue.reload
81 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
81 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
82 end
82 end
83
83
84 def test_create_with_group_assignment
84 def test_create_with_group_assignment
85 with_settings :issue_group_assignment => '1' do
85 with_settings :issue_group_assignment => '1' do
86 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
86 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
87 :subject => 'Group assignment',
87 :subject => 'Group assignment',
88 :assigned_to_id => 11).save
88 :assigned_to_id => 11).save
89 issue = Issue.first(:order => 'id DESC')
89 issue = Issue.first(:order => 'id DESC')
90 assert_kind_of Group, issue.assigned_to
90 assert_kind_of Group, issue.assigned_to
91 assert_equal Group.find(11), issue.assigned_to
91 assert_equal Group.find(11), issue.assigned_to
92 end
92 end
93 end
93 end
94
94
95 def assert_visibility_match(user, issues)
95 def assert_visibility_match(user, issues)
96 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
96 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
97 end
97 end
98
98
99 def test_visible_scope_for_anonymous
99 def test_visible_scope_for_anonymous
100 # Anonymous user should see issues of public projects only
100 # Anonymous user should see issues of public projects only
101 issues = Issue.visible(User.anonymous).all
101 issues = Issue.visible(User.anonymous).all
102 assert issues.any?
102 assert issues.any?
103 assert_nil issues.detect {|issue| !issue.project.is_public?}
103 assert_nil issues.detect {|issue| !issue.project.is_public?}
104 assert_nil issues.detect {|issue| issue.is_private?}
104 assert_nil issues.detect {|issue| issue.is_private?}
105 assert_visibility_match User.anonymous, issues
105 assert_visibility_match User.anonymous, issues
106 end
106 end
107
107
108 def test_visible_scope_for_anonymous_without_view_issues_permissions
108 def test_visible_scope_for_anonymous_without_view_issues_permissions
109 # Anonymous user should not see issues without permission
109 # Anonymous user should not see issues without permission
110 Role.anonymous.remove_permission!(:view_issues)
110 Role.anonymous.remove_permission!(:view_issues)
111 issues = Issue.visible(User.anonymous).all
111 issues = Issue.visible(User.anonymous).all
112 assert issues.empty?
112 assert issues.empty?
113 assert_visibility_match User.anonymous, issues
113 assert_visibility_match User.anonymous, issues
114 end
114 end
115
115
116 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
116 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
117 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
117 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
118 issue = Issue.generate_for_project!(Project.find(1), :author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
118 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
119 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
119 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
120 assert !issue.visible?(User.anonymous)
120 assert !issue.visible?(User.anonymous)
121 end
121 end
122
122
123 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
123 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
124 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
124 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
125 issue = Issue.generate_for_project!(Project.find(1), :author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
125 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
126 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
126 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
127 assert !issue.visible?(User.anonymous)
127 assert !issue.visible?(User.anonymous)
128 end
128 end
129
129
130 def test_visible_scope_for_non_member
130 def test_visible_scope_for_non_member
131 user = User.find(9)
131 user = User.find(9)
132 assert user.projects.empty?
132 assert user.projects.empty?
133 # Non member user should see issues of public projects only
133 # Non member user should see issues of public projects only
134 issues = Issue.visible(user).all
134 issues = Issue.visible(user).all
135 assert issues.any?
135 assert issues.any?
136 assert_nil issues.detect {|issue| !issue.project.is_public?}
136 assert_nil issues.detect {|issue| !issue.project.is_public?}
137 assert_nil issues.detect {|issue| issue.is_private?}
137 assert_nil issues.detect {|issue| issue.is_private?}
138 assert_visibility_match user, issues
138 assert_visibility_match user, issues
139 end
139 end
140
140
141 def test_visible_scope_for_non_member_with_own_issues_visibility
141 def test_visible_scope_for_non_member_with_own_issues_visibility
142 Role.non_member.update_attribute :issues_visibility, 'own'
142 Role.non_member.update_attribute :issues_visibility, 'own'
143 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
143 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
144 user = User.find(9)
144 user = User.find(9)
145
145
146 issues = Issue.visible(user).all
146 issues = Issue.visible(user).all
147 assert issues.any?
147 assert issues.any?
148 assert_nil issues.detect {|issue| issue.author != user}
148 assert_nil issues.detect {|issue| issue.author != user}
149 assert_visibility_match user, issues
149 assert_visibility_match user, issues
150 end
150 end
151
151
152 def test_visible_scope_for_non_member_without_view_issues_permissions
152 def test_visible_scope_for_non_member_without_view_issues_permissions
153 # Non member user should not see issues without permission
153 # Non member user should not see issues without permission
154 Role.non_member.remove_permission!(:view_issues)
154 Role.non_member.remove_permission!(:view_issues)
155 user = User.find(9)
155 user = User.find(9)
156 assert user.projects.empty?
156 assert user.projects.empty?
157 issues = Issue.visible(user).all
157 issues = Issue.visible(user).all
158 assert issues.empty?
158 assert issues.empty?
159 assert_visibility_match user, issues
159 assert_visibility_match user, issues
160 end
160 end
161
161
162 def test_visible_scope_for_member
162 def test_visible_scope_for_member
163 user = User.find(9)
163 user = User.find(9)
164 # User should see issues of projects for which he has view_issues permissions only
164 # User should see issues of projects for which he has view_issues permissions only
165 Role.non_member.remove_permission!(:view_issues)
165 Role.non_member.remove_permission!(:view_issues)
166 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
166 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
167 issues = Issue.visible(user).all
167 issues = Issue.visible(user).all
168 assert issues.any?
168 assert issues.any?
169 assert_nil issues.detect {|issue| issue.project_id != 3}
169 assert_nil issues.detect {|issue| issue.project_id != 3}
170 assert_nil issues.detect {|issue| issue.is_private?}
170 assert_nil issues.detect {|issue| issue.is_private?}
171 assert_visibility_match user, issues
171 assert_visibility_match user, issues
172 end
172 end
173
173
174 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
174 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
175 user = User.find(8)
175 user = User.find(8)
176 assert user.groups.any?
176 assert user.groups.any?
177 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
177 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
178 Role.non_member.remove_permission!(:view_issues)
178 Role.non_member.remove_permission!(:view_issues)
179
179
180 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
180 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
181 :status_id => 1, :priority => IssuePriority.all.first,
181 :status_id => 1, :priority => IssuePriority.all.first,
182 :subject => 'Assignment test',
182 :subject => 'Assignment test',
183 :assigned_to => user.groups.first,
183 :assigned_to => user.groups.first,
184 :is_private => true)
184 :is_private => true)
185
185
186 Role.find(2).update_attribute :issues_visibility, 'default'
186 Role.find(2).update_attribute :issues_visibility, 'default'
187 issues = Issue.visible(User.find(8)).all
187 issues = Issue.visible(User.find(8)).all
188 assert issues.any?
188 assert issues.any?
189 assert issues.include?(issue)
189 assert issues.include?(issue)
190
190
191 Role.find(2).update_attribute :issues_visibility, 'own'
191 Role.find(2).update_attribute :issues_visibility, 'own'
192 issues = Issue.visible(User.find(8)).all
192 issues = Issue.visible(User.find(8)).all
193 assert issues.any?
193 assert issues.any?
194 assert issues.include?(issue)
194 assert issues.include?(issue)
195 end
195 end
196
196
197 def test_visible_scope_for_admin
197 def test_visible_scope_for_admin
198 user = User.find(1)
198 user = User.find(1)
199 user.members.each(&:destroy)
199 user.members.each(&:destroy)
200 assert user.projects.empty?
200 assert user.projects.empty?
201 issues = Issue.visible(user).all
201 issues = Issue.visible(user).all
202 assert issues.any?
202 assert issues.any?
203 # Admin should see issues on private projects that he does not belong to
203 # Admin should see issues on private projects that he does not belong to
204 assert issues.detect {|issue| !issue.project.is_public?}
204 assert issues.detect {|issue| !issue.project.is_public?}
205 # Admin should see private issues of other users
205 # Admin should see private issues of other users
206 assert issues.detect {|issue| issue.is_private? && issue.author != user}
206 assert issues.detect {|issue| issue.is_private? && issue.author != user}
207 assert_visibility_match user, issues
207 assert_visibility_match user, issues
208 end
208 end
209
209
210 def test_visible_scope_with_project
210 def test_visible_scope_with_project
211 project = Project.find(1)
211 project = Project.find(1)
212 issues = Issue.visible(User.find(2), :project => project).all
212 issues = Issue.visible(User.find(2), :project => project).all
213 projects = issues.collect(&:project).uniq
213 projects = issues.collect(&:project).uniq
214 assert_equal 1, projects.size
214 assert_equal 1, projects.size
215 assert_equal project, projects.first
215 assert_equal project, projects.first
216 end
216 end
217
217
218 def test_visible_scope_with_project_and_subprojects
218 def test_visible_scope_with_project_and_subprojects
219 project = Project.find(1)
219 project = Project.find(1)
220 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
220 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
221 projects = issues.collect(&:project).uniq
221 projects = issues.collect(&:project).uniq
222 assert projects.size > 1
222 assert projects.size > 1
223 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
223 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
224 end
224 end
225
225
226 def test_visible_and_nested_set_scopes
226 def test_visible_and_nested_set_scopes
227 assert_equal 0, Issue.find(1).descendants.visible.all.size
227 assert_equal 0, Issue.find(1).descendants.visible.all.size
228 end
228 end
229
229
230 def test_open_scope
230 def test_open_scope
231 issues = Issue.open.all
231 issues = Issue.open.all
232 assert_nil issues.detect(&:closed?)
232 assert_nil issues.detect(&:closed?)
233 end
233 end
234
234
235 def test_open_scope_with_arg
235 def test_open_scope_with_arg
236 issues = Issue.open(false).all
236 issues = Issue.open(false).all
237 assert_equal issues, issues.select(&:closed?)
237 assert_equal issues, issues.select(&:closed?)
238 end
238 end
239
239
240 def test_errors_full_messages_should_include_custom_fields_errors
240 def test_errors_full_messages_should_include_custom_fields_errors
241 field = IssueCustomField.find_by_name('Database')
241 field = IssueCustomField.find_by_name('Database')
242
242
243 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
243 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
244 :status_id => 1, :subject => 'test_create',
244 :status_id => 1, :subject => 'test_create',
245 :description => 'IssueTest#test_create_with_required_custom_field')
245 :description => 'IssueTest#test_create_with_required_custom_field')
246 assert issue.available_custom_fields.include?(field)
246 assert issue.available_custom_fields.include?(field)
247 # Invalid value
247 # Invalid value
248 issue.custom_field_values = { field.id => 'SQLServer' }
248 issue.custom_field_values = { field.id => 'SQLServer' }
249
249
250 assert !issue.valid?
250 assert !issue.valid?
251 assert_equal 1, issue.errors.full_messages.size
251 assert_equal 1, issue.errors.full_messages.size
252 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
252 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
253 issue.errors.full_messages.first
253 issue.errors.full_messages.first
254 end
254 end
255
255
256 def test_update_issue_with_required_custom_field
256 def test_update_issue_with_required_custom_field
257 field = IssueCustomField.find_by_name('Database')
257 field = IssueCustomField.find_by_name('Database')
258 field.update_attribute(:is_required, true)
258 field.update_attribute(:is_required, true)
259
259
260 issue = Issue.find(1)
260 issue = Issue.find(1)
261 assert_nil issue.custom_value_for(field)
261 assert_nil issue.custom_value_for(field)
262 assert issue.available_custom_fields.include?(field)
262 assert issue.available_custom_fields.include?(field)
263 # No change to custom values, issue can be saved
263 # No change to custom values, issue can be saved
264 assert issue.save
264 assert issue.save
265 # Blank value
265 # Blank value
266 issue.custom_field_values = { field.id => '' }
266 issue.custom_field_values = { field.id => '' }
267 assert !issue.save
267 assert !issue.save
268 # Valid value
268 # Valid value
269 issue.custom_field_values = { field.id => 'PostgreSQL' }
269 issue.custom_field_values = { field.id => 'PostgreSQL' }
270 assert issue.save
270 assert issue.save
271 issue.reload
271 issue.reload
272 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
272 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
273 end
273 end
274
274
275 def test_should_not_update_attributes_if_custom_fields_validation_fails
275 def test_should_not_update_attributes_if_custom_fields_validation_fails
276 issue = Issue.find(1)
276 issue = Issue.find(1)
277 field = IssueCustomField.find_by_name('Database')
277 field = IssueCustomField.find_by_name('Database')
278 assert issue.available_custom_fields.include?(field)
278 assert issue.available_custom_fields.include?(field)
279
279
280 issue.custom_field_values = { field.id => 'Invalid' }
280 issue.custom_field_values = { field.id => 'Invalid' }
281 issue.subject = 'Should be not be saved'
281 issue.subject = 'Should be not be saved'
282 assert !issue.save
282 assert !issue.save
283
283
284 issue.reload
284 issue.reload
285 assert_equal "Can't print recipes", issue.subject
285 assert_equal "Can't print recipes", issue.subject
286 end
286 end
287
287
288 def test_should_not_recreate_custom_values_objects_on_update
288 def test_should_not_recreate_custom_values_objects_on_update
289 field = IssueCustomField.find_by_name('Database')
289 field = IssueCustomField.find_by_name('Database')
290
290
291 issue = Issue.find(1)
291 issue = Issue.find(1)
292 issue.custom_field_values = { field.id => 'PostgreSQL' }
292 issue.custom_field_values = { field.id => 'PostgreSQL' }
293 assert issue.save
293 assert issue.save
294 custom_value = issue.custom_value_for(field)
294 custom_value = issue.custom_value_for(field)
295 issue.reload
295 issue.reload
296 issue.custom_field_values = { field.id => 'MySQL' }
296 issue.custom_field_values = { field.id => 'MySQL' }
297 assert issue.save
297 assert issue.save
298 issue.reload
298 issue.reload
299 assert_equal custom_value.id, issue.custom_value_for(field).id
299 assert_equal custom_value.id, issue.custom_value_for(field).id
300 end
300 end
301
301
302 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
302 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
303 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'Test', :custom_field_values => {'2' => 'Test'})
303 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'Test', :custom_field_values => {'2' => 'Test'})
304 assert !Tracker.find(2).custom_field_ids.include?(2)
304 assert !Tracker.find(2).custom_field_ids.include?(2)
305
305
306 issue = Issue.find(issue.id)
306 issue = Issue.find(issue.id)
307 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
307 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
308
308
309 issue = Issue.find(issue.id)
309 issue = Issue.find(issue.id)
310 custom_value = issue.custom_value_for(2)
310 custom_value = issue.custom_value_for(2)
311 assert_not_nil custom_value
311 assert_not_nil custom_value
312 assert_equal 'Test', custom_value.value
312 assert_equal 'Test', custom_value.value
313 end
313 end
314
314
315 def test_assigning_tracker_id_should_reload_custom_fields_values
315 def test_assigning_tracker_id_should_reload_custom_fields_values
316 issue = Issue.new(:project => Project.find(1))
316 issue = Issue.new(:project => Project.find(1))
317 assert issue.custom_field_values.empty?
317 assert issue.custom_field_values.empty?
318 issue.tracker_id = 1
318 issue.tracker_id = 1
319 assert issue.custom_field_values.any?
319 assert issue.custom_field_values.any?
320 end
320 end
321
321
322 def test_assigning_attributes_should_assign_project_and_tracker_first
322 def test_assigning_attributes_should_assign_project_and_tracker_first
323 seq = sequence('seq')
323 seq = sequence('seq')
324 issue = Issue.new
324 issue = Issue.new
325 issue.expects(:project_id=).in_sequence(seq)
325 issue.expects(:project_id=).in_sequence(seq)
326 issue.expects(:tracker_id=).in_sequence(seq)
326 issue.expects(:tracker_id=).in_sequence(seq)
327 issue.expects(:subject=).in_sequence(seq)
327 issue.expects(:subject=).in_sequence(seq)
328 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
328 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
329 end
329 end
330
330
331 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
331 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
332 attributes = ActiveSupport::OrderedHash.new
332 attributes = ActiveSupport::OrderedHash.new
333 attributes['custom_field_values'] = { '1' => 'MySQL' }
333 attributes['custom_field_values'] = { '1' => 'MySQL' }
334 attributes['tracker_id'] = '1'
334 attributes['tracker_id'] = '1'
335 issue = Issue.new(:project => Project.find(1))
335 issue = Issue.new(:project => Project.find(1))
336 issue.attributes = attributes
336 issue.attributes = attributes
337 assert_equal 'MySQL', issue.custom_field_value(1)
337 assert_equal 'MySQL', issue.custom_field_value(1)
338 end
338 end
339
339
340 def test_should_update_issue_with_disabled_tracker
340 def test_should_update_issue_with_disabled_tracker
341 p = Project.find(1)
341 p = Project.find(1)
342 issue = Issue.find(1)
342 issue = Issue.find(1)
343
343
344 p.trackers.delete(issue.tracker)
344 p.trackers.delete(issue.tracker)
345 assert !p.trackers.include?(issue.tracker)
345 assert !p.trackers.include?(issue.tracker)
346
346
347 issue.reload
347 issue.reload
348 issue.subject = 'New subject'
348 issue.subject = 'New subject'
349 assert issue.save
349 assert issue.save
350 end
350 end
351
351
352 def test_should_not_set_a_disabled_tracker
352 def test_should_not_set_a_disabled_tracker
353 p = Project.find(1)
353 p = Project.find(1)
354 p.trackers.delete(Tracker.find(2))
354 p.trackers.delete(Tracker.find(2))
355
355
356 issue = Issue.find(1)
356 issue = Issue.find(1)
357 issue.tracker_id = 2
357 issue.tracker_id = 2
358 issue.subject = 'New subject'
358 issue.subject = 'New subject'
359 assert !issue.save
359 assert !issue.save
360 assert_not_nil issue.errors[:tracker_id]
360 assert_not_nil issue.errors[:tracker_id]
361 end
361 end
362
362
363 def test_category_based_assignment
363 def test_category_based_assignment
364 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
364 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
365 :status_id => 1, :priority => IssuePriority.all.first,
365 :status_id => 1, :priority => IssuePriority.all.first,
366 :subject => 'Assignment test',
366 :subject => 'Assignment test',
367 :description => 'Assignment test', :category_id => 1)
367 :description => 'Assignment test', :category_id => 1)
368 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
368 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
369 end
369 end
370
370
371 def test_new_statuses_allowed_to
371 def test_new_statuses_allowed_to
372 WorkflowTransition.delete_all
372 WorkflowTransition.delete_all
373
373
374 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
374 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
375 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
375 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
376 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
376 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
377 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
377 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
378 status = IssueStatus.find(1)
378 status = IssueStatus.find(1)
379 role = Role.find(1)
379 role = Role.find(1)
380 tracker = Tracker.find(1)
380 tracker = Tracker.find(1)
381 user = User.find(2)
381 user = User.find(2)
382
382
383 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author_id => 1)
383 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author_id => 1)
384 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
384 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
385
385
386 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user)
386 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user)
387 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
387 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
388
388
389 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author_id => 1, :assigned_to => user)
389 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author_id => 1, :assigned_to => user)
390 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
390 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
391
391
392 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user, :assigned_to => user)
392 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user, :assigned_to => user)
393 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
393 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
394 end
394 end
395
395
396 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
396 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
397 admin = User.find(1)
397 admin = User.find(1)
398 issue = Issue.find(1)
398 issue = Issue.find(1)
399 assert !admin.member_of?(issue.project)
399 assert !admin.member_of?(issue.project)
400 expected_statuses = [issue.status] + WorkflowTransition.find_all_by_old_status_id(issue.status_id).map(&:new_status).uniq.sort
400 expected_statuses = [issue.status] + WorkflowTransition.find_all_by_old_status_id(issue.status_id).map(&:new_status).uniq.sort
401
401
402 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
402 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
403 end
403 end
404
404
405 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
405 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
406 issue = Issue.find(1).copy
406 issue = Issue.find(1).copy
407 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
407 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
408
408
409 issue = Issue.find(2).copy
409 issue = Issue.find(2).copy
410 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
410 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
411 end
411 end
412
412
413 def test_safe_attributes_names_should_not_include_disabled_field
413 def test_safe_attributes_names_should_not_include_disabled_field
414 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
414 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
415
415
416 issue = Issue.new(:tracker => tracker)
416 issue = Issue.new(:tracker => tracker)
417 assert_include 'tracker_id', issue.safe_attribute_names
417 assert_include 'tracker_id', issue.safe_attribute_names
418 assert_include 'status_id', issue.safe_attribute_names
418 assert_include 'status_id', issue.safe_attribute_names
419 assert_include 'subject', issue.safe_attribute_names
419 assert_include 'subject', issue.safe_attribute_names
420 assert_include 'description', issue.safe_attribute_names
420 assert_include 'description', issue.safe_attribute_names
421 assert_include 'custom_field_values', issue.safe_attribute_names
421 assert_include 'custom_field_values', issue.safe_attribute_names
422 assert_include 'custom_fields', issue.safe_attribute_names
422 assert_include 'custom_fields', issue.safe_attribute_names
423 assert_include 'lock_version', issue.safe_attribute_names
423 assert_include 'lock_version', issue.safe_attribute_names
424
424
425 tracker.core_fields.each do |field|
425 tracker.core_fields.each do |field|
426 assert_include field, issue.safe_attribute_names
426 assert_include field, issue.safe_attribute_names
427 end
427 end
428
428
429 tracker.disabled_core_fields.each do |field|
429 tracker.disabled_core_fields.each do |field|
430 assert_not_include field, issue.safe_attribute_names
430 assert_not_include field, issue.safe_attribute_names
431 end
431 end
432 end
432 end
433
433
434 def test_safe_attributes_should_ignore_disabled_fields
434 def test_safe_attributes_should_ignore_disabled_fields
435 tracker = Tracker.find(1)
435 tracker = Tracker.find(1)
436 tracker.core_fields = %w(assigned_to_id due_date)
436 tracker.core_fields = %w(assigned_to_id due_date)
437 tracker.save!
437 tracker.save!
438
438
439 issue = Issue.new(:tracker => tracker)
439 issue = Issue.new(:tracker => tracker)
440 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
440 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
441 assert_nil issue.start_date
441 assert_nil issue.start_date
442 assert_equal Date.parse('2012-07-14'), issue.due_date
442 assert_equal Date.parse('2012-07-14'), issue.due_date
443 end
443 end
444
444
445 def test_safe_attributes_should_accept_target_tracker_enabled_fields
445 def test_safe_attributes_should_accept_target_tracker_enabled_fields
446 source = Tracker.find(1)
446 source = Tracker.find(1)
447 source.core_fields = []
447 source.core_fields = []
448 source.save!
448 source.save!
449 target = Tracker.find(2)
449 target = Tracker.find(2)
450 target.core_fields = %w(assigned_to_id due_date)
450 target.core_fields = %w(assigned_to_id due_date)
451 target.save!
451 target.save!
452
452
453 issue = Issue.new(:tracker => source)
453 issue = Issue.new(:tracker => source)
454 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
454 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
455 assert_equal target, issue.tracker
455 assert_equal target, issue.tracker
456 assert_equal Date.parse('2012-07-14'), issue.due_date
456 assert_equal Date.parse('2012-07-14'), issue.due_date
457 end
457 end
458
458
459 def test_safe_attributes_should_not_include_readonly_fields
459 def test_safe_attributes_should_not_include_readonly_fields
460 WorkflowPermission.delete_all
460 WorkflowPermission.delete_all
461 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
461 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
462 user = User.find(2)
462 user = User.find(2)
463
463
464 issue = Issue.new(:project_id => 1, :tracker_id => 1)
464 issue = Issue.new(:project_id => 1, :tracker_id => 1)
465 assert_equal %w(due_date), issue.read_only_attribute_names(user)
465 assert_equal %w(due_date), issue.read_only_attribute_names(user)
466 assert_not_include 'due_date', issue.safe_attribute_names(user)
466 assert_not_include 'due_date', issue.safe_attribute_names(user)
467
467
468 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
468 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
469 assert_equal Date.parse('2012-07-14'), issue.start_date
469 assert_equal Date.parse('2012-07-14'), issue.start_date
470 assert_nil issue.due_date
470 assert_nil issue.due_date
471 end
471 end
472
472
473 def test_safe_attributes_should_not_include_readonly_custom_fields
473 def test_safe_attributes_should_not_include_readonly_custom_fields
474 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1])
474 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1])
475 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1])
475 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1])
476
476
477 WorkflowPermission.delete_all
477 WorkflowPermission.delete_all
478 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
478 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
479 user = User.find(2)
479 user = User.find(2)
480
480
481 issue = Issue.new(:project_id => 1, :tracker_id => 1)
481 issue = Issue.new(:project_id => 1, :tracker_id => 1)
482 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
482 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
483 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
483 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
484
484
485 issue.send :safe_attributes=, {'custom_field_values' => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}}, user
485 issue.send :safe_attributes=, {'custom_field_values' => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}}, user
486 assert_equal 'value1', issue.custom_field_value(cf1)
486 assert_equal 'value1', issue.custom_field_value(cf1)
487 assert_nil issue.custom_field_value(cf2)
487 assert_nil issue.custom_field_value(cf2)
488
488
489 issue.send :safe_attributes=, {'custom_fields' => [{'id' => cf1.id.to_s, 'value' => 'valuea'}, {'id' => cf2.id.to_s, 'value' => 'valueb'}]}, user
489 issue.send :safe_attributes=, {'custom_fields' => [{'id' => cf1.id.to_s, 'value' => 'valuea'}, {'id' => cf2.id.to_s, 'value' => 'valueb'}]}, user
490 assert_equal 'valuea', issue.custom_field_value(cf1)
490 assert_equal 'valuea', issue.custom_field_value(cf1)
491 assert_nil issue.custom_field_value(cf2)
491 assert_nil issue.custom_field_value(cf2)
492 end
492 end
493
493
494 def test_editable_custom_field_values_should_return_non_readonly_custom_values
494 def test_editable_custom_field_values_should_return_non_readonly_custom_values
495 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
495 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
496 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
496 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
497
497
498 WorkflowPermission.delete_all
498 WorkflowPermission.delete_all
499 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
499 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
500 user = User.find(2)
500 user = User.find(2)
501
501
502 issue = Issue.new(:project_id => 1, :tracker_id => 1)
502 issue = Issue.new(:project_id => 1, :tracker_id => 1)
503 values = issue.editable_custom_field_values(user)
503 values = issue.editable_custom_field_values(user)
504 assert values.detect {|value| value.custom_field == cf1}
504 assert values.detect {|value| value.custom_field == cf1}
505 assert_nil values.detect {|value| value.custom_field == cf2}
505 assert_nil values.detect {|value| value.custom_field == cf2}
506
506
507 issue.tracker_id = 2
507 issue.tracker_id = 2
508 values = issue.editable_custom_field_values(user)
508 values = issue.editable_custom_field_values(user)
509 assert values.detect {|value| value.custom_field == cf1}
509 assert values.detect {|value| value.custom_field == cf1}
510 assert values.detect {|value| value.custom_field == cf2}
510 assert values.detect {|value| value.custom_field == cf2}
511 end
511 end
512
512
513 def test_safe_attributes_should_accept_target_tracker_writable_fields
513 def test_safe_attributes_should_accept_target_tracker_writable_fields
514 WorkflowPermission.delete_all
514 WorkflowPermission.delete_all
515 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
515 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
516 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
516 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
517 user = User.find(2)
517 user = User.find(2)
518
518
519 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
519 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
520
520
521 issue.send :safe_attributes=, {'start_date' => '2012-07-12', 'due_date' => '2012-07-14'}, user
521 issue.send :safe_attributes=, {'start_date' => '2012-07-12', 'due_date' => '2012-07-14'}, user
522 assert_equal Date.parse('2012-07-12'), issue.start_date
522 assert_equal Date.parse('2012-07-12'), issue.start_date
523 assert_nil issue.due_date
523 assert_nil issue.due_date
524
524
525 issue.send :safe_attributes=, {'start_date' => '2012-07-15', 'due_date' => '2012-07-16', 'tracker_id' => 2}, user
525 issue.send :safe_attributes=, {'start_date' => '2012-07-15', 'due_date' => '2012-07-16', 'tracker_id' => 2}, user
526 assert_equal Date.parse('2012-07-12'), issue.start_date
526 assert_equal Date.parse('2012-07-12'), issue.start_date
527 assert_equal Date.parse('2012-07-16'), issue.due_date
527 assert_equal Date.parse('2012-07-16'), issue.due_date
528 end
528 end
529
529
530 def test_safe_attributes_should_accept_target_status_writable_fields
530 def test_safe_attributes_should_accept_target_status_writable_fields
531 WorkflowPermission.delete_all
531 WorkflowPermission.delete_all
532 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
532 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
533 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
533 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
534 user = User.find(2)
534 user = User.find(2)
535
535
536 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
536 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
537
537
538 issue.send :safe_attributes=, {'start_date' => '2012-07-12', 'due_date' => '2012-07-14'}, user
538 issue.send :safe_attributes=, {'start_date' => '2012-07-12', 'due_date' => '2012-07-14'}, user
539 assert_equal Date.parse('2012-07-12'), issue.start_date
539 assert_equal Date.parse('2012-07-12'), issue.start_date
540 assert_nil issue.due_date
540 assert_nil issue.due_date
541
541
542 issue.send :safe_attributes=, {'start_date' => '2012-07-15', 'due_date' => '2012-07-16', 'status_id' => 2}, user
542 issue.send :safe_attributes=, {'start_date' => '2012-07-15', 'due_date' => '2012-07-16', 'status_id' => 2}, user
543 assert_equal Date.parse('2012-07-12'), issue.start_date
543 assert_equal Date.parse('2012-07-12'), issue.start_date
544 assert_equal Date.parse('2012-07-16'), issue.due_date
544 assert_equal Date.parse('2012-07-16'), issue.due_date
545 end
545 end
546
546
547 def test_required_attributes_should_be_validated
547 def test_required_attributes_should_be_validated
548 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
548 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
549
549
550 WorkflowPermission.delete_all
550 WorkflowPermission.delete_all
551 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
551 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
552 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'category_id', :rule => 'required')
552 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'category_id', :rule => 'required')
553 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf.id.to_s, :rule => 'required')
553 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf.id.to_s, :rule => 'required')
554
554
555 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'start_date', :rule => 'required')
555 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'start_date', :rule => 'required')
556 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf.id.to_s, :rule => 'required')
556 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf.id.to_s, :rule => 'required')
557 user = User.find(2)
557 user = User.find(2)
558
558
559 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Required fields', :author => user)
559 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Required fields', :author => user)
560 assert_equal [cf.id.to_s, "category_id", "due_date"], issue.required_attribute_names(user).sort
560 assert_equal [cf.id.to_s, "category_id", "due_date"], issue.required_attribute_names(user).sort
561 assert !issue.save, "Issue was saved"
561 assert !issue.save, "Issue was saved"
562 assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"], issue.errors.full_messages.sort
562 assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"], issue.errors.full_messages.sort
563
563
564 issue.tracker_id = 2
564 issue.tracker_id = 2
565 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
565 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
566 assert !issue.save, "Issue was saved"
566 assert !issue.save, "Issue was saved"
567 assert_equal ["Foo can't be blank", "Start date can't be blank"], issue.errors.full_messages.sort
567 assert_equal ["Foo can't be blank", "Start date can't be blank"], issue.errors.full_messages.sort
568
568
569 issue.start_date = Date.today
569 issue.start_date = Date.today
570 issue.custom_field_values = {cf.id.to_s => 'bar'}
570 issue.custom_field_values = {cf.id.to_s => 'bar'}
571 assert issue.save
571 assert issue.save
572 end
572 end
573
573
574 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
574 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
575 WorkflowPermission.delete_all
575 WorkflowPermission.delete_all
576 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
576 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
577 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'required')
577 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'required')
578 user = User.find(2)
578 user = User.find(2)
579 member = Member.find(1)
579 member = Member.find(1)
580 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
580 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
581
581
582 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
582 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
583
583
584 member.role_ids = [1, 2]
584 member.role_ids = [1, 2]
585 member.save!
585 member.save!
586 assert_equal [], issue.required_attribute_names(user.reload)
586 assert_equal [], issue.required_attribute_names(user.reload)
587
587
588 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'required')
588 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'required')
589 assert_equal %w(due_date), issue.required_attribute_names(user)
589 assert_equal %w(due_date), issue.required_attribute_names(user)
590
590
591 member.role_ids = [1, 2, 3]
591 member.role_ids = [1, 2, 3]
592 member.save!
592 member.save!
593 assert_equal [], issue.required_attribute_names(user.reload)
593 assert_equal [], issue.required_attribute_names(user.reload)
594
594
595 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'readonly')
595 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'readonly')
596 # required + readonly => required
596 # required + readonly => required
597 assert_equal %w(due_date), issue.required_attribute_names(user)
597 assert_equal %w(due_date), issue.required_attribute_names(user)
598 end
598 end
599
599
600 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
600 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
601 WorkflowPermission.delete_all
601 WorkflowPermission.delete_all
602 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
602 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
603 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
603 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
604 user = User.find(2)
604 user = User.find(2)
605 member = Member.find(1)
605 member = Member.find(1)
606 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
606 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
607
607
608 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
608 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
609
609
610 member.role_ids = [1, 2]
610 member.role_ids = [1, 2]
611 member.save!
611 member.save!
612 assert_equal [], issue.read_only_attribute_names(user.reload)
612 assert_equal [], issue.read_only_attribute_names(user.reload)
613
613
614 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'readonly')
614 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'readonly')
615 assert_equal %w(due_date), issue.read_only_attribute_names(user)
615 assert_equal %w(due_date), issue.read_only_attribute_names(user)
616 end
616 end
617
617
618 def test_copy
618 def test_copy
619 issue = Issue.new.copy_from(1)
619 issue = Issue.new.copy_from(1)
620 assert issue.copy?
620 assert issue.copy?
621 assert issue.save
621 assert issue.save
622 issue.reload
622 issue.reload
623 orig = Issue.find(1)
623 orig = Issue.find(1)
624 assert_equal orig.subject, issue.subject
624 assert_equal orig.subject, issue.subject
625 assert_equal orig.tracker, issue.tracker
625 assert_equal orig.tracker, issue.tracker
626 assert_equal "125", issue.custom_value_for(2).value
626 assert_equal "125", issue.custom_value_for(2).value
627 end
627 end
628
628
629 def test_copy_should_copy_status
629 def test_copy_should_copy_status
630 orig = Issue.find(8)
630 orig = Issue.find(8)
631 assert orig.status != IssueStatus.default
631 assert orig.status != IssueStatus.default
632
632
633 issue = Issue.new.copy_from(orig)
633 issue = Issue.new.copy_from(orig)
634 assert issue.save
634 assert issue.save
635 issue.reload
635 issue.reload
636 assert_equal orig.status, issue.status
636 assert_equal orig.status, issue.status
637 end
637 end
638
638
639 def test_copy_should_add_relation_with_copied_issue
639 def test_copy_should_add_relation_with_copied_issue
640 copied = Issue.find(1)
640 copied = Issue.find(1)
641 issue = Issue.new.copy_from(copied)
641 issue = Issue.new.copy_from(copied)
642 assert issue.save
642 assert issue.save
643 issue.reload
643 issue.reload
644
644
645 assert_equal 1, issue.relations.size
645 assert_equal 1, issue.relations.size
646 relation = issue.relations.first
646 relation = issue.relations.first
647 assert_equal 'copied_to', relation.relation_type
647 assert_equal 'copied_to', relation.relation_type
648 assert_equal copied, relation.issue_from
648 assert_equal copied, relation.issue_from
649 assert_equal issue, relation.issue_to
649 assert_equal issue, relation.issue_to
650 end
650 end
651
651
652 def test_copy_should_copy_subtasks
652 def test_copy_should_copy_subtasks
653 issue = Issue.generate_with_descendants!(Project.find(1), :subject => 'Parent')
653 issue = Issue.generate_with_descendants!(Project.find(1), :subject => 'Parent')
654
654
655 copy = issue.reload.copy
655 copy = issue.reload.copy
656 copy.author = User.find(7)
656 copy.author = User.find(7)
657 assert_difference 'Issue.count', 1+issue.descendants.count do
657 assert_difference 'Issue.count', 1+issue.descendants.count do
658 assert copy.save
658 assert copy.save
659 end
659 end
660 copy.reload
660 copy.reload
661 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
661 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
662 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
662 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
663 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
663 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
664 assert_equal copy.author, child_copy.author
664 assert_equal copy.author, child_copy.author
665 end
665 end
666
666
667 def test_copy_should_copy_subtasks_to_target_project
667 def test_copy_should_copy_subtasks_to_target_project
668 issue = Issue.generate_with_descendants!(Project.find(1), :subject => 'Parent')
668 issue = Issue.generate_with_descendants!(Project.find(1), :subject => 'Parent')
669
669
670 copy = issue.copy(:project_id => 3)
670 copy = issue.copy(:project_id => 3)
671 assert_difference 'Issue.count', 1+issue.descendants.count do
671 assert_difference 'Issue.count', 1+issue.descendants.count do
672 assert copy.save
672 assert copy.save
673 end
673 end
674 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
674 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
675 end
675 end
676
676
677 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
677 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
678 issue = Issue.generate_with_descendants!(Project.find(1), :subject => 'Parent')
678 issue = Issue.generate_with_descendants!(Project.find(1), :subject => 'Parent')
679
679
680 copy = issue.reload.copy
680 copy = issue.reload.copy
681 assert_difference 'Issue.count', 1+issue.descendants.count do
681 assert_difference 'Issue.count', 1+issue.descendants.count do
682 assert copy.save
682 assert copy.save
683 assert copy.save
683 assert copy.save
684 end
684 end
685 end
685 end
686
686
687 def test_should_not_call_after_project_change_on_creation
687 def test_should_not_call_after_project_change_on_creation
688 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Test', :author_id => 1)
688 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Test', :author_id => 1)
689 issue.expects(:after_project_change).never
689 issue.expects(:after_project_change).never
690 issue.save!
690 issue.save!
691 end
691 end
692
692
693 def test_should_not_call_after_project_change_on_update
693 def test_should_not_call_after_project_change_on_update
694 issue = Issue.find(1)
694 issue = Issue.find(1)
695 issue.project = Project.find(1)
695 issue.project = Project.find(1)
696 issue.subject = 'No project change'
696 issue.subject = 'No project change'
697 issue.expects(:after_project_change).never
697 issue.expects(:after_project_change).never
698 issue.save!
698 issue.save!
699 end
699 end
700
700
701 def test_should_call_after_project_change_on_project_change
701 def test_should_call_after_project_change_on_project_change
702 issue = Issue.find(1)
702 issue = Issue.find(1)
703 issue.project = Project.find(2)
703 issue.project = Project.find(2)
704 issue.expects(:after_project_change).once
704 issue.expects(:after_project_change).once
705 issue.save!
705 issue.save!
706 end
706 end
707
707
708 def test_adding_journal_should_update_timestamp
708 def test_adding_journal_should_update_timestamp
709 issue = Issue.find(1)
709 issue = Issue.find(1)
710 updated_on_was = issue.updated_on
710 updated_on_was = issue.updated_on
711
711
712 issue.init_journal(User.first, "Adding notes")
712 issue.init_journal(User.first, "Adding notes")
713 assert_difference 'Journal.count' do
713 assert_difference 'Journal.count' do
714 assert issue.save
714 assert issue.save
715 end
715 end
716 issue.reload
716 issue.reload
717
717
718 assert_not_equal updated_on_was, issue.updated_on
718 assert_not_equal updated_on_was, issue.updated_on
719 end
719 end
720
720
721 def test_should_close_duplicates
721 def test_should_close_duplicates
722 # Create 3 issues
722 # Create 3 issues
723 project = Project.find(1)
723 issue1 = Issue.generate!
724 issue1 = Issue.generate_for_project!(project)
724 issue2 = Issue.generate!
725 issue2 = Issue.generate_for_project!(project)
725 issue3 = Issue.generate!
726 issue3 = Issue.generate_for_project!(project)
727
726
728 # 2 is a dupe of 1
727 # 2 is a dupe of 1
729 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
728 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
730 # And 3 is a dupe of 2
729 # And 3 is a dupe of 2
731 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
730 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
732 # And 3 is a dupe of 1 (circular duplicates)
731 # And 3 is a dupe of 1 (circular duplicates)
733 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
732 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
734
733
735 assert issue1.reload.duplicates.include?(issue2)
734 assert issue1.reload.duplicates.include?(issue2)
736
735
737 # Closing issue 1
736 # Closing issue 1
738 issue1.init_journal(User.find(:first), "Closing issue1")
737 issue1.init_journal(User.find(:first), "Closing issue1")
739 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
738 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
740 assert issue1.save
739 assert issue1.save
741 # 2 and 3 should be also closed
740 # 2 and 3 should be also closed
742 assert issue2.reload.closed?
741 assert issue2.reload.closed?
743 assert issue3.reload.closed?
742 assert issue3.reload.closed?
744 end
743 end
745
744
746 def test_should_not_close_duplicated_issue
745 def test_should_not_close_duplicated_issue
747 project = Project.find(1)
746 issue1 = Issue.generate!
748 issue1 = Issue.generate_for_project!(project)
747 issue2 = Issue.generate!
749 issue2 = Issue.generate_for_project!(project)
750
748
751 # 2 is a dupe of 1
749 # 2 is a dupe of 1
752 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
750 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
753 # 2 is a dup of 1 but 1 is not a duplicate of 2
751 # 2 is a dup of 1 but 1 is not a duplicate of 2
754 assert !issue2.reload.duplicates.include?(issue1)
752 assert !issue2.reload.duplicates.include?(issue1)
755
753
756 # Closing issue 2
754 # Closing issue 2
757 issue2.init_journal(User.find(:first), "Closing issue2")
755 issue2.init_journal(User.find(:first), "Closing issue2")
758 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
756 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
759 assert issue2.save
757 assert issue2.save
760 # 1 should not be also closed
758 # 1 should not be also closed
761 assert !issue1.reload.closed?
759 assert !issue1.reload.closed?
762 end
760 end
763
761
764 def test_assignable_versions
762 def test_assignable_versions
765 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
763 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
766 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
764 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
767 end
765 end
768
766
769 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
767 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
770 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
768 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
771 assert !issue.save
769 assert !issue.save
772 assert_not_nil issue.errors[:fixed_version_id]
770 assert_not_nil issue.errors[:fixed_version_id]
773 end
771 end
774
772
775 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
773 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
776 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
774 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
777 assert !issue.save
775 assert !issue.save
778 assert_not_nil issue.errors[:fixed_version_id]
776 assert_not_nil issue.errors[:fixed_version_id]
779 end
777 end
780
778
781 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
779 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
782 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
780 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
783 assert issue.save
781 assert issue.save
784 end
782 end
785
783
786 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
784 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
787 issue = Issue.find(11)
785 issue = Issue.find(11)
788 assert_equal 'closed', issue.fixed_version.status
786 assert_equal 'closed', issue.fixed_version.status
789 issue.subject = 'Subject changed'
787 issue.subject = 'Subject changed'
790 assert issue.save
788 assert issue.save
791 end
789 end
792
790
793 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
791 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
794 issue = Issue.find(11)
792 issue = Issue.find(11)
795 issue.status_id = 1
793 issue.status_id = 1
796 assert !issue.save
794 assert !issue.save
797 assert_not_nil issue.errors[:base]
795 assert_not_nil issue.errors[:base]
798 end
796 end
799
797
800 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
798 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
801 issue = Issue.find(11)
799 issue = Issue.find(11)
802 issue.status_id = 1
800 issue.status_id = 1
803 issue.fixed_version_id = 3
801 issue.fixed_version_id = 3
804 assert issue.save
802 assert issue.save
805 end
803 end
806
804
807 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
805 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
808 issue = Issue.find(12)
806 issue = Issue.find(12)
809 assert_equal 'locked', issue.fixed_version.status
807 assert_equal 'locked', issue.fixed_version.status
810 issue.status_id = 1
808 issue.status_id = 1
811 assert issue.save
809 assert issue.save
812 end
810 end
813
811
814 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
812 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
815 issue = Issue.find(2)
813 issue = Issue.find(2)
816 assert_equal 2, issue.fixed_version_id
814 assert_equal 2, issue.fixed_version_id
817 issue.project_id = 3
815 issue.project_id = 3
818 assert_nil issue.fixed_version_id
816 assert_nil issue.fixed_version_id
819 issue.fixed_version_id = 2
817 issue.fixed_version_id = 2
820 assert !issue.save
818 assert !issue.save
821 assert_include 'Target version is not included in the list', issue.errors.full_messages
819 assert_include 'Target version is not included in the list', issue.errors.full_messages
822 end
820 end
823
821
824 def test_should_keep_shared_version_when_changing_project
822 def test_should_keep_shared_version_when_changing_project
825 Version.find(2).update_attribute :sharing, 'tree'
823 Version.find(2).update_attribute :sharing, 'tree'
826
824
827 issue = Issue.find(2)
825 issue = Issue.find(2)
828 assert_equal 2, issue.fixed_version_id
826 assert_equal 2, issue.fixed_version_id
829 issue.project_id = 3
827 issue.project_id = 3
830 assert_equal 2, issue.fixed_version_id
828 assert_equal 2, issue.fixed_version_id
831 assert issue.save
829 assert issue.save
832 end
830 end
833
831
834 def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
832 def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
835 assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
833 assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
836 end
834 end
837
835
838 def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
836 def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
839 Project.find(2).disable_module! :issue_tracking
837 Project.find(2).disable_module! :issue_tracking
840 assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
838 assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
841 end
839 end
842
840
843 def test_move_to_another_project_with_same_category
841 def test_move_to_another_project_with_same_category
844 issue = Issue.find(1)
842 issue = Issue.find(1)
845 issue.project = Project.find(2)
843 issue.project = Project.find(2)
846 assert issue.save
844 assert issue.save
847 issue.reload
845 issue.reload
848 assert_equal 2, issue.project_id
846 assert_equal 2, issue.project_id
849 # Category changes
847 # Category changes
850 assert_equal 4, issue.category_id
848 assert_equal 4, issue.category_id
851 # Make sure time entries were move to the target project
849 # Make sure time entries were move to the target project
852 assert_equal 2, issue.time_entries.first.project_id
850 assert_equal 2, issue.time_entries.first.project_id
853 end
851 end
854
852
855 def test_move_to_another_project_without_same_category
853 def test_move_to_another_project_without_same_category
856 issue = Issue.find(2)
854 issue = Issue.find(2)
857 issue.project = Project.find(2)
855 issue.project = Project.find(2)
858 assert issue.save
856 assert issue.save
859 issue.reload
857 issue.reload
860 assert_equal 2, issue.project_id
858 assert_equal 2, issue.project_id
861 # Category cleared
859 # Category cleared
862 assert_nil issue.category_id
860 assert_nil issue.category_id
863 end
861 end
864
862
865 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
863 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
866 issue = Issue.find(1)
864 issue = Issue.find(1)
867 issue.update_attribute(:fixed_version_id, 1)
865 issue.update_attribute(:fixed_version_id, 1)
868 issue.project = Project.find(2)
866 issue.project = Project.find(2)
869 assert issue.save
867 assert issue.save
870 issue.reload
868 issue.reload
871 assert_equal 2, issue.project_id
869 assert_equal 2, issue.project_id
872 # Cleared fixed_version
870 # Cleared fixed_version
873 assert_equal nil, issue.fixed_version
871 assert_equal nil, issue.fixed_version
874 end
872 end
875
873
876 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
874 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
877 issue = Issue.find(1)
875 issue = Issue.find(1)
878 issue.update_attribute(:fixed_version_id, 4)
876 issue.update_attribute(:fixed_version_id, 4)
879 issue.project = Project.find(5)
877 issue.project = Project.find(5)
880 assert issue.save
878 assert issue.save
881 issue.reload
879 issue.reload
882 assert_equal 5, issue.project_id
880 assert_equal 5, issue.project_id
883 # Keep fixed_version
881 # Keep fixed_version
884 assert_equal 4, issue.fixed_version_id
882 assert_equal 4, issue.fixed_version_id
885 end
883 end
886
884
887 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
885 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
888 issue = Issue.find(1)
886 issue = Issue.find(1)
889 issue.update_attribute(:fixed_version_id, 1)
887 issue.update_attribute(:fixed_version_id, 1)
890 issue.project = Project.find(5)
888 issue.project = Project.find(5)
891 assert issue.save
889 assert issue.save
892 issue.reload
890 issue.reload
893 assert_equal 5, issue.project_id
891 assert_equal 5, issue.project_id
894 # Cleared fixed_version
892 # Cleared fixed_version
895 assert_equal nil, issue.fixed_version
893 assert_equal nil, issue.fixed_version
896 end
894 end
897
895
898 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
896 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
899 issue = Issue.find(1)
897 issue = Issue.find(1)
900 issue.update_attribute(:fixed_version_id, 7)
898 issue.update_attribute(:fixed_version_id, 7)
901 issue.project = Project.find(2)
899 issue.project = Project.find(2)
902 assert issue.save
900 assert issue.save
903 issue.reload
901 issue.reload
904 assert_equal 2, issue.project_id
902 assert_equal 2, issue.project_id
905 # Keep fixed_version
903 # Keep fixed_version
906 assert_equal 7, issue.fixed_version_id
904 assert_equal 7, issue.fixed_version_id
907 end
905 end
908
906
909 def test_move_to_another_project_should_keep_parent_if_valid
907 def test_move_to_another_project_should_keep_parent_if_valid
910 issue = Issue.find(1)
908 issue = Issue.find(1)
911 issue.update_attribute(:parent_issue_id, 2)
909 issue.update_attribute(:parent_issue_id, 2)
912 issue.project = Project.find(3)
910 issue.project = Project.find(3)
913 assert issue.save
911 assert issue.save
914 issue.reload
912 issue.reload
915 assert_equal 2, issue.parent_id
913 assert_equal 2, issue.parent_id
916 end
914 end
917
915
918 def test_move_to_another_project_should_clear_parent_if_not_valid
916 def test_move_to_another_project_should_clear_parent_if_not_valid
919 issue = Issue.find(1)
917 issue = Issue.find(1)
920 issue.update_attribute(:parent_issue_id, 2)
918 issue.update_attribute(:parent_issue_id, 2)
921 issue.project = Project.find(2)
919 issue.project = Project.find(2)
922 assert issue.save
920 assert issue.save
923 issue.reload
921 issue.reload
924 assert_nil issue.parent_id
922 assert_nil issue.parent_id
925 end
923 end
926
924
927 def test_move_to_another_project_with_disabled_tracker
925 def test_move_to_another_project_with_disabled_tracker
928 issue = Issue.find(1)
926 issue = Issue.find(1)
929 target = Project.find(2)
927 target = Project.find(2)
930 target.tracker_ids = [3]
928 target.tracker_ids = [3]
931 target.save
929 target.save
932 issue.project = target
930 issue.project = target
933 assert issue.save
931 assert issue.save
934 issue.reload
932 issue.reload
935 assert_equal 2, issue.project_id
933 assert_equal 2, issue.project_id
936 assert_equal 3, issue.tracker_id
934 assert_equal 3, issue.tracker_id
937 end
935 end
938
936
939 def test_copy_to_the_same_project
937 def test_copy_to_the_same_project
940 issue = Issue.find(1)
938 issue = Issue.find(1)
941 copy = issue.copy
939 copy = issue.copy
942 assert_difference 'Issue.count' do
940 assert_difference 'Issue.count' do
943 copy.save!
941 copy.save!
944 end
942 end
945 assert_kind_of Issue, copy
943 assert_kind_of Issue, copy
946 assert_equal issue.project, copy.project
944 assert_equal issue.project, copy.project
947 assert_equal "125", copy.custom_value_for(2).value
945 assert_equal "125", copy.custom_value_for(2).value
948 end
946 end
949
947
950 def test_copy_to_another_project_and_tracker
948 def test_copy_to_another_project_and_tracker
951 issue = Issue.find(1)
949 issue = Issue.find(1)
952 copy = issue.copy(:project_id => 3, :tracker_id => 2)
950 copy = issue.copy(:project_id => 3, :tracker_id => 2)
953 assert_difference 'Issue.count' do
951 assert_difference 'Issue.count' do
954 copy.save!
952 copy.save!
955 end
953 end
956 copy.reload
954 copy.reload
957 assert_kind_of Issue, copy
955 assert_kind_of Issue, copy
958 assert_equal Project.find(3), copy.project
956 assert_equal Project.find(3), copy.project
959 assert_equal Tracker.find(2), copy.tracker
957 assert_equal Tracker.find(2), copy.tracker
960 # Custom field #2 is not associated with target tracker
958 # Custom field #2 is not associated with target tracker
961 assert_nil copy.custom_value_for(2)
959 assert_nil copy.custom_value_for(2)
962 end
960 end
963
961
964 context "#copy" do
962 context "#copy" do
965 setup do
963 setup do
966 @issue = Issue.find(1)
964 @issue = Issue.find(1)
967 end
965 end
968
966
969 should "not create a journal" do
967 should "not create a journal" do
970 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
968 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
971 copy.save!
969 copy.save!
972 assert_equal 0, copy.reload.journals.size
970 assert_equal 0, copy.reload.journals.size
973 end
971 end
974
972
975 should "allow assigned_to changes" do
973 should "allow assigned_to changes" do
976 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
974 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
977 assert_equal 3, copy.assigned_to_id
975 assert_equal 3, copy.assigned_to_id
978 end
976 end
979
977
980 should "allow status changes" do
978 should "allow status changes" do
981 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
979 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
982 assert_equal 2, copy.status_id
980 assert_equal 2, copy.status_id
983 end
981 end
984
982
985 should "allow start date changes" do
983 should "allow start date changes" do
986 date = Date.today
984 date = Date.today
987 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
985 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
988 assert_equal date, copy.start_date
986 assert_equal date, copy.start_date
989 end
987 end
990
988
991 should "allow due date changes" do
989 should "allow due date changes" do
992 date = Date.today
990 date = Date.today
993 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
991 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
994 assert_equal date, copy.due_date
992 assert_equal date, copy.due_date
995 end
993 end
996
994
997 should "set current user as author" do
995 should "set current user as author" do
998 User.current = User.find(9)
996 User.current = User.find(9)
999 copy = @issue.copy(:project_id => 3, :tracker_id => 2)
997 copy = @issue.copy(:project_id => 3, :tracker_id => 2)
1000 assert_equal User.current, copy.author
998 assert_equal User.current, copy.author
1001 end
999 end
1002
1000
1003 should "create a journal with notes" do
1001 should "create a journal with notes" do
1004 date = Date.today
1002 date = Date.today
1005 notes = "Notes added when copying"
1003 notes = "Notes added when copying"
1006 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1004 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1007 copy.init_journal(User.current, notes)
1005 copy.init_journal(User.current, notes)
1008 copy.save!
1006 copy.save!
1009
1007
1010 assert_equal 1, copy.journals.size
1008 assert_equal 1, copy.journals.size
1011 journal = copy.journals.first
1009 journal = copy.journals.first
1012 assert_equal 0, journal.details.size
1010 assert_equal 0, journal.details.size
1013 assert_equal notes, journal.notes
1011 assert_equal notes, journal.notes
1014 end
1012 end
1015 end
1013 end
1016
1014
1017 def test_valid_parent_project
1015 def test_valid_parent_project
1018 issue = Issue.find(1)
1016 issue = Issue.find(1)
1019 issue_in_same_project = Issue.find(2)
1017 issue_in_same_project = Issue.find(2)
1020 issue_in_child_project = Issue.find(5)
1018 issue_in_child_project = Issue.find(5)
1021 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1019 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1022 issue_in_other_child_project = Issue.find(6)
1020 issue_in_other_child_project = Issue.find(6)
1023 issue_in_different_tree = Issue.find(4)
1021 issue_in_different_tree = Issue.find(4)
1024
1022
1025 with_settings :cross_project_subtasks => '' do
1023 with_settings :cross_project_subtasks => '' do
1026 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1024 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1027 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1025 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1028 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1026 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1029 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1027 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1030 end
1028 end
1031
1029
1032 with_settings :cross_project_subtasks => 'system' do
1030 with_settings :cross_project_subtasks => 'system' do
1033 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1031 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1034 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1032 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1035 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1033 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1036 end
1034 end
1037
1035
1038 with_settings :cross_project_subtasks => 'tree' do
1036 with_settings :cross_project_subtasks => 'tree' do
1039 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1037 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1040 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1038 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1041 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1039 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1042 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1040 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1043
1041
1044 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1042 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1045 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1043 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1046 end
1044 end
1047
1045
1048 with_settings :cross_project_subtasks => 'descendants' do
1046 with_settings :cross_project_subtasks => 'descendants' do
1049 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1047 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1050 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1048 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1051 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1049 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1052 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1050 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1053
1051
1054 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1052 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1055 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1053 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1056 end
1054 end
1057 end
1055 end
1058
1056
1059 def test_recipients_should_include_previous_assignee
1057 def test_recipients_should_include_previous_assignee
1060 user = User.find(3)
1058 user = User.find(3)
1061 user.members.update_all ["mail_notification = ?", false]
1059 user.members.update_all ["mail_notification = ?", false]
1062 user.update_attribute :mail_notification, 'only_assigned'
1060 user.update_attribute :mail_notification, 'only_assigned'
1063
1061
1064 issue = Issue.find(2)
1062 issue = Issue.find(2)
1065 issue.assigned_to = nil
1063 issue.assigned_to = nil
1066 assert_include user.mail, issue.recipients
1064 assert_include user.mail, issue.recipients
1067 issue.save!
1065 issue.save!
1068 assert !issue.recipients.include?(user.mail)
1066 assert !issue.recipients.include?(user.mail)
1069 end
1067 end
1070
1068
1071 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1069 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1072 issue = Issue.find(12)
1070 issue = Issue.find(12)
1073 assert issue.recipients.include?(issue.author.mail)
1071 assert issue.recipients.include?(issue.author.mail)
1074 # copy the issue to a private project
1072 # copy the issue to a private project
1075 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1073 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1076 # author is not a member of project anymore
1074 # author is not a member of project anymore
1077 assert !copy.recipients.include?(copy.author.mail)
1075 assert !copy.recipients.include?(copy.author.mail)
1078 end
1076 end
1079
1077
1080 def test_recipients_should_include_the_assigned_group_members
1078 def test_recipients_should_include_the_assigned_group_members
1081 group_member = User.generate!
1079 group_member = User.generate!
1082 group = Group.generate!
1080 group = Group.generate!
1083 group.users << group_member
1081 group.users << group_member
1084
1082
1085 issue = Issue.find(12)
1083 issue = Issue.find(12)
1086 issue.assigned_to = group
1084 issue.assigned_to = group
1087 assert issue.recipients.include?(group_member.mail)
1085 assert issue.recipients.include?(group_member.mail)
1088 end
1086 end
1089
1087
1090 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1088 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1091 user = User.find(3)
1089 user = User.find(3)
1092 issue = Issue.find(9)
1090 issue = Issue.find(9)
1093 Watcher.create!(:user => user, :watchable => issue)
1091 Watcher.create!(:user => user, :watchable => issue)
1094 assert issue.watched_by?(user)
1092 assert issue.watched_by?(user)
1095 assert !issue.watcher_recipients.include?(user.mail)
1093 assert !issue.watcher_recipients.include?(user.mail)
1096 end
1094 end
1097
1095
1098 def test_issue_destroy
1096 def test_issue_destroy
1099 Issue.find(1).destroy
1097 Issue.find(1).destroy
1100 assert_nil Issue.find_by_id(1)
1098 assert_nil Issue.find_by_id(1)
1101 assert_nil TimeEntry.find_by_issue_id(1)
1099 assert_nil TimeEntry.find_by_issue_id(1)
1102 end
1100 end
1103
1101
1104 def test_destroying_a_deleted_issue_should_not_raise_an_error
1102 def test_destroying_a_deleted_issue_should_not_raise_an_error
1105 issue = Issue.find(1)
1103 issue = Issue.find(1)
1106 Issue.find(1).destroy
1104 Issue.find(1).destroy
1107
1105
1108 assert_nothing_raised do
1106 assert_nothing_raised do
1109 assert_no_difference 'Issue.count' do
1107 assert_no_difference 'Issue.count' do
1110 issue.destroy
1108 issue.destroy
1111 end
1109 end
1112 assert issue.destroyed?
1110 assert issue.destroyed?
1113 end
1111 end
1114 end
1112 end
1115
1113
1116 def test_destroying_a_stale_issue_should_not_raise_an_error
1114 def test_destroying_a_stale_issue_should_not_raise_an_error
1117 issue = Issue.find(1)
1115 issue = Issue.find(1)
1118 Issue.find(1).update_attribute :subject, "Updated"
1116 Issue.find(1).update_attribute :subject, "Updated"
1119
1117
1120 assert_nothing_raised do
1118 assert_nothing_raised do
1121 assert_difference 'Issue.count', -1 do
1119 assert_difference 'Issue.count', -1 do
1122 issue.destroy
1120 issue.destroy
1123 end
1121 end
1124 assert issue.destroyed?
1122 assert issue.destroyed?
1125 end
1123 end
1126 end
1124 end
1127
1125
1128 def test_blocked
1126 def test_blocked
1129 blocked_issue = Issue.find(9)
1127 blocked_issue = Issue.find(9)
1130 blocking_issue = Issue.find(10)
1128 blocking_issue = Issue.find(10)
1131
1129
1132 assert blocked_issue.blocked?
1130 assert blocked_issue.blocked?
1133 assert !blocking_issue.blocked?
1131 assert !blocking_issue.blocked?
1134 end
1132 end
1135
1133
1136 def test_blocked_issues_dont_allow_closed_statuses
1134 def test_blocked_issues_dont_allow_closed_statuses
1137 blocked_issue = Issue.find(9)
1135 blocked_issue = Issue.find(9)
1138
1136
1139 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1137 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1140 assert !allowed_statuses.empty?
1138 assert !allowed_statuses.empty?
1141 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1139 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1142 assert closed_statuses.empty?
1140 assert closed_statuses.empty?
1143 end
1141 end
1144
1142
1145 def test_unblocked_issues_allow_closed_statuses
1143 def test_unblocked_issues_allow_closed_statuses
1146 blocking_issue = Issue.find(10)
1144 blocking_issue = Issue.find(10)
1147
1145
1148 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1146 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1149 assert !allowed_statuses.empty?
1147 assert !allowed_statuses.empty?
1150 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1148 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1151 assert !closed_statuses.empty?
1149 assert !closed_statuses.empty?
1152 end
1150 end
1153
1151
1154 def test_rescheduling_an_issue_should_reschedule_following_issue
1152 def test_rescheduling_an_issue_should_reschedule_following_issue
1155 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
1153 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
1156 issue2 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
1154 issue2 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
1157 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1155 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1158 assert_equal issue1.due_date + 1, issue2.reload.start_date
1156 assert_equal issue1.due_date + 1, issue2.reload.start_date
1159
1157
1160 issue1.due_date = Date.today + 5
1158 issue1.due_date = Date.today + 5
1161 issue1.save!
1159 issue1.save!
1162 assert_equal issue1.due_date + 1, issue2.reload.start_date
1160 assert_equal issue1.due_date + 1, issue2.reload.start_date
1163 end
1161 end
1164
1162
1165 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1163 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1166 stale = Issue.find(1)
1164 stale = Issue.find(1)
1167 issue = Issue.find(1)
1165 issue = Issue.find(1)
1168 issue.subject = "Updated"
1166 issue.subject = "Updated"
1169 issue.save!
1167 issue.save!
1170
1168
1171 date = 10.days.from_now.to_date
1169 date = 10.days.from_now.to_date
1172 assert_nothing_raised do
1170 assert_nothing_raised do
1173 stale.reschedule_after(date)
1171 stale.reschedule_after(date)
1174 end
1172 end
1175 assert_equal date, stale.reload.start_date
1173 assert_equal date, stale.reload.start_date
1176 end
1174 end
1177
1175
1178 def test_overdue
1176 def test_overdue
1179 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1177 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1180 assert !Issue.new(:due_date => Date.today).overdue?
1178 assert !Issue.new(:due_date => Date.today).overdue?
1181 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1179 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1182 assert !Issue.new(:due_date => nil).overdue?
1180 assert !Issue.new(:due_date => nil).overdue?
1183 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
1181 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
1184 end
1182 end
1185
1183
1186 context "#behind_schedule?" do
1184 context "#behind_schedule?" do
1187 should "be false if the issue has no start_date" do
1185 should "be false if the issue has no start_date" do
1188 assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
1186 assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
1189 end
1187 end
1190
1188
1191 should "be false if the issue has no end_date" do
1189 should "be false if the issue has no end_date" do
1192 assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule?
1190 assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule?
1193 end
1191 end
1194
1192
1195 should "be false if the issue has more done than it's calendar time" do
1193 should "be false if the issue has more done than it's calendar time" do
1196 assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule?
1194 assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule?
1197 end
1195 end
1198
1196
1199 should "be true if the issue hasn't been started at all" do
1197 should "be true if the issue hasn't been started at all" do
1200 assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
1198 assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
1201 end
1199 end
1202
1200
1203 should "be true if the issue has used more calendar time than it's done ratio" do
1201 should "be true if the issue has used more calendar time than it's done ratio" do
1204 assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule?
1202 assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule?
1205 end
1203 end
1206 end
1204 end
1207
1205
1208 context "#assignable_users" do
1206 context "#assignable_users" do
1209 should "be Users" do
1207 should "be Users" do
1210 assert_kind_of User, Issue.find(1).assignable_users.first
1208 assert_kind_of User, Issue.find(1).assignable_users.first
1211 end
1209 end
1212
1210
1213 should "include the issue author" do
1211 should "include the issue author" do
1214 project = Project.find(1)
1215 non_project_member = User.generate!
1212 non_project_member = User.generate!
1216 issue = Issue.generate_for_project!(project, :author => non_project_member)
1213 issue = Issue.generate!(:author => non_project_member)
1217
1214
1218 assert issue.assignable_users.include?(non_project_member)
1215 assert issue.assignable_users.include?(non_project_member)
1219 end
1216 end
1220
1217
1221 should "include the current assignee" do
1218 should "include the current assignee" do
1222 project = Project.find(1)
1223 user = User.generate!
1219 user = User.generate!
1224 issue = Issue.generate_for_project!(project, :assigned_to => user)
1220 issue = Issue.generate!(:assigned_to => user)
1225 user.lock!
1221 user.lock!
1226
1222
1227 assert Issue.find(issue.id).assignable_users.include?(user)
1223 assert Issue.find(issue.id).assignable_users.include?(user)
1228 end
1224 end
1229
1225
1230 should "not show the issue author twice" do
1226 should "not show the issue author twice" do
1231 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1227 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1232 assert_equal 2, assignable_user_ids.length
1228 assert_equal 2, assignable_user_ids.length
1233
1229
1234 assignable_user_ids.each do |user_id|
1230 assignable_user_ids.each do |user_id|
1235 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length, "User #{user_id} appears more or less than once"
1231 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length, "User #{user_id} appears more or less than once"
1236 end
1232 end
1237 end
1233 end
1238
1234
1239 context "with issue_group_assignment" do
1235 context "with issue_group_assignment" do
1240 should "include groups" do
1236 should "include groups" do
1241 issue = Issue.new(:project => Project.find(2))
1237 issue = Issue.new(:project => Project.find(2))
1242
1238
1243 with_settings :issue_group_assignment => '1' do
1239 with_settings :issue_group_assignment => '1' do
1244 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1240 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1245 assert issue.assignable_users.include?(Group.find(11))
1241 assert issue.assignable_users.include?(Group.find(11))
1246 end
1242 end
1247 end
1243 end
1248 end
1244 end
1249
1245
1250 context "without issue_group_assignment" do
1246 context "without issue_group_assignment" do
1251 should "not include groups" do
1247 should "not include groups" do
1252 issue = Issue.new(:project => Project.find(2))
1248 issue = Issue.new(:project => Project.find(2))
1253
1249
1254 with_settings :issue_group_assignment => '0' do
1250 with_settings :issue_group_assignment => '0' do
1255 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1251 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1256 assert !issue.assignable_users.include?(Group.find(11))
1252 assert !issue.assignable_users.include?(Group.find(11))
1257 end
1253 end
1258 end
1254 end
1259 end
1255 end
1260 end
1256 end
1261
1257
1262 def test_create_should_send_email_notification
1258 def test_create_should_send_email_notification
1263 ActionMailer::Base.deliveries.clear
1259 ActionMailer::Base.deliveries.clear
1264 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1260 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1265 :author_id => 3, :status_id => 1,
1261 :author_id => 3, :status_id => 1,
1266 :priority => IssuePriority.all.first,
1262 :priority => IssuePriority.all.first,
1267 :subject => 'test_create', :estimated_hours => '1:30')
1263 :subject => 'test_create', :estimated_hours => '1:30')
1268
1264
1269 assert issue.save
1265 assert issue.save
1270 assert_equal 1, ActionMailer::Base.deliveries.size
1266 assert_equal 1, ActionMailer::Base.deliveries.size
1271 end
1267 end
1272
1268
1273 def test_stale_issue_should_not_send_email_notification
1269 def test_stale_issue_should_not_send_email_notification
1274 ActionMailer::Base.deliveries.clear
1270 ActionMailer::Base.deliveries.clear
1275 issue = Issue.find(1)
1271 issue = Issue.find(1)
1276 stale = Issue.find(1)
1272 stale = Issue.find(1)
1277
1273
1278 issue.init_journal(User.find(1))
1274 issue.init_journal(User.find(1))
1279 issue.subject = 'Subjet update'
1275 issue.subject = 'Subjet update'
1280 assert issue.save
1276 assert issue.save
1281 assert_equal 1, ActionMailer::Base.deliveries.size
1277 assert_equal 1, ActionMailer::Base.deliveries.size
1282 ActionMailer::Base.deliveries.clear
1278 ActionMailer::Base.deliveries.clear
1283
1279
1284 stale.init_journal(User.find(1))
1280 stale.init_journal(User.find(1))
1285 stale.subject = 'Another subjet update'
1281 stale.subject = 'Another subjet update'
1286 assert_raise ActiveRecord::StaleObjectError do
1282 assert_raise ActiveRecord::StaleObjectError do
1287 stale.save
1283 stale.save
1288 end
1284 end
1289 assert ActionMailer::Base.deliveries.empty?
1285 assert ActionMailer::Base.deliveries.empty?
1290 end
1286 end
1291
1287
1292 def test_journalized_description
1288 def test_journalized_description
1293 IssueCustomField.delete_all
1289 IssueCustomField.delete_all
1294
1290
1295 i = Issue.first
1291 i = Issue.first
1296 old_description = i.description
1292 old_description = i.description
1297 new_description = "This is the new description"
1293 new_description = "This is the new description"
1298
1294
1299 i.init_journal(User.find(2))
1295 i.init_journal(User.find(2))
1300 i.description = new_description
1296 i.description = new_description
1301 assert_difference 'Journal.count', 1 do
1297 assert_difference 'Journal.count', 1 do
1302 assert_difference 'JournalDetail.count', 1 do
1298 assert_difference 'JournalDetail.count', 1 do
1303 i.save!
1299 i.save!
1304 end
1300 end
1305 end
1301 end
1306
1302
1307 detail = JournalDetail.first(:order => 'id DESC')
1303 detail = JournalDetail.first(:order => 'id DESC')
1308 assert_equal i, detail.journal.journalized
1304 assert_equal i, detail.journal.journalized
1309 assert_equal 'attr', detail.property
1305 assert_equal 'attr', detail.property
1310 assert_equal 'description', detail.prop_key
1306 assert_equal 'description', detail.prop_key
1311 assert_equal old_description, detail.old_value
1307 assert_equal old_description, detail.old_value
1312 assert_equal new_description, detail.value
1308 assert_equal new_description, detail.value
1313 end
1309 end
1314
1310
1315 def test_blank_descriptions_should_not_be_journalized
1311 def test_blank_descriptions_should_not_be_journalized
1316 IssueCustomField.delete_all
1312 IssueCustomField.delete_all
1317 Issue.update_all("description = NULL", "id=1")
1313 Issue.update_all("description = NULL", "id=1")
1318
1314
1319 i = Issue.find(1)
1315 i = Issue.find(1)
1320 i.init_journal(User.find(2))
1316 i.init_journal(User.find(2))
1321 i.subject = "blank description"
1317 i.subject = "blank description"
1322 i.description = "\r\n"
1318 i.description = "\r\n"
1323
1319
1324 assert_difference 'Journal.count', 1 do
1320 assert_difference 'Journal.count', 1 do
1325 assert_difference 'JournalDetail.count', 1 do
1321 assert_difference 'JournalDetail.count', 1 do
1326 i.save!
1322 i.save!
1327 end
1323 end
1328 end
1324 end
1329 end
1325 end
1330
1326
1331 def test_journalized_multi_custom_field
1327 def test_journalized_multi_custom_field
1332 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
1328 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
1333 :tracker_ids => [1], :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
1329 :tracker_ids => [1], :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
1334
1330
1335 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'Test', :author_id => 1)
1331 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'Test', :author_id => 1)
1336
1332
1337 assert_difference 'Journal.count' do
1333 assert_difference 'Journal.count' do
1338 assert_difference 'JournalDetail.count' do
1334 assert_difference 'JournalDetail.count' do
1339 issue.init_journal(User.first)
1335 issue.init_journal(User.first)
1340 issue.custom_field_values = {field.id => ['value1']}
1336 issue.custom_field_values = {field.id => ['value1']}
1341 issue.save!
1337 issue.save!
1342 end
1338 end
1343 assert_difference 'JournalDetail.count' do
1339 assert_difference 'JournalDetail.count' do
1344 issue.init_journal(User.first)
1340 issue.init_journal(User.first)
1345 issue.custom_field_values = {field.id => ['value1', 'value2']}
1341 issue.custom_field_values = {field.id => ['value1', 'value2']}
1346 issue.save!
1342 issue.save!
1347 end
1343 end
1348 assert_difference 'JournalDetail.count', 2 do
1344 assert_difference 'JournalDetail.count', 2 do
1349 issue.init_journal(User.first)
1345 issue.init_journal(User.first)
1350 issue.custom_field_values = {field.id => ['value3', 'value2']}
1346 issue.custom_field_values = {field.id => ['value3', 'value2']}
1351 issue.save!
1347 issue.save!
1352 end
1348 end
1353 assert_difference 'JournalDetail.count', 2 do
1349 assert_difference 'JournalDetail.count', 2 do
1354 issue.init_journal(User.first)
1350 issue.init_journal(User.first)
1355 issue.custom_field_values = {field.id => nil}
1351 issue.custom_field_values = {field.id => nil}
1356 issue.save!
1352 issue.save!
1357 end
1353 end
1358 end
1354 end
1359 end
1355 end
1360
1356
1361 def test_description_eol_should_be_normalized
1357 def test_description_eol_should_be_normalized
1362 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1358 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1363 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1359 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1364 end
1360 end
1365
1361
1366 def test_saving_twice_should_not_duplicate_journal_details
1362 def test_saving_twice_should_not_duplicate_journal_details
1367 i = Issue.find(:first)
1363 i = Issue.find(:first)
1368 i.init_journal(User.find(2), 'Some notes')
1364 i.init_journal(User.find(2), 'Some notes')
1369 # initial changes
1365 # initial changes
1370 i.subject = 'New subject'
1366 i.subject = 'New subject'
1371 i.done_ratio = i.done_ratio + 10
1367 i.done_ratio = i.done_ratio + 10
1372 assert_difference 'Journal.count' do
1368 assert_difference 'Journal.count' do
1373 assert i.save
1369 assert i.save
1374 end
1370 end
1375 # 1 more change
1371 # 1 more change
1376 i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
1372 i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
1377 assert_no_difference 'Journal.count' do
1373 assert_no_difference 'Journal.count' do
1378 assert_difference 'JournalDetail.count', 1 do
1374 assert_difference 'JournalDetail.count', 1 do
1379 i.save
1375 i.save
1380 end
1376 end
1381 end
1377 end
1382 # no more change
1378 # no more change
1383 assert_no_difference 'Journal.count' do
1379 assert_no_difference 'Journal.count' do
1384 assert_no_difference 'JournalDetail.count' do
1380 assert_no_difference 'JournalDetail.count' do
1385 i.save
1381 i.save
1386 end
1382 end
1387 end
1383 end
1388 end
1384 end
1389
1385
1390 def test_all_dependent_issues
1386 def test_all_dependent_issues
1391 IssueRelation.delete_all
1387 IssueRelation.delete_all
1392 assert IssueRelation.create!(:issue_from => Issue.find(1),
1388 assert IssueRelation.create!(:issue_from => Issue.find(1),
1393 :issue_to => Issue.find(2),
1389 :issue_to => Issue.find(2),
1394 :relation_type => IssueRelation::TYPE_PRECEDES)
1390 :relation_type => IssueRelation::TYPE_PRECEDES)
1395 assert IssueRelation.create!(:issue_from => Issue.find(2),
1391 assert IssueRelation.create!(:issue_from => Issue.find(2),
1396 :issue_to => Issue.find(3),
1392 :issue_to => Issue.find(3),
1397 :relation_type => IssueRelation::TYPE_PRECEDES)
1393 :relation_type => IssueRelation::TYPE_PRECEDES)
1398 assert IssueRelation.create!(:issue_from => Issue.find(3),
1394 assert IssueRelation.create!(:issue_from => Issue.find(3),
1399 :issue_to => Issue.find(8),
1395 :issue_to => Issue.find(8),
1400 :relation_type => IssueRelation::TYPE_PRECEDES)
1396 :relation_type => IssueRelation::TYPE_PRECEDES)
1401
1397
1402 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1398 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1403 end
1399 end
1404
1400
1405 def test_all_dependent_issues_with_persistent_circular_dependency
1401 def test_all_dependent_issues_with_persistent_circular_dependency
1406 IssueRelation.delete_all
1402 IssueRelation.delete_all
1407 assert IssueRelation.create!(:issue_from => Issue.find(1),
1403 assert IssueRelation.create!(:issue_from => Issue.find(1),
1408 :issue_to => Issue.find(2),
1404 :issue_to => Issue.find(2),
1409 :relation_type => IssueRelation::TYPE_PRECEDES)
1405 :relation_type => IssueRelation::TYPE_PRECEDES)
1410 assert IssueRelation.create!(:issue_from => Issue.find(2),
1406 assert IssueRelation.create!(:issue_from => Issue.find(2),
1411 :issue_to => Issue.find(3),
1407 :issue_to => Issue.find(3),
1412 :relation_type => IssueRelation::TYPE_PRECEDES)
1408 :relation_type => IssueRelation::TYPE_PRECEDES)
1413
1409
1414 r = IssueRelation.create!(:issue_from => Issue.find(3),
1410 r = IssueRelation.create!(:issue_from => Issue.find(3),
1415 :issue_to => Issue.find(7),
1411 :issue_to => Issue.find(7),
1416 :relation_type => IssueRelation::TYPE_PRECEDES)
1412 :relation_type => IssueRelation::TYPE_PRECEDES)
1417 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1413 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1418
1414
1419 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1415 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1420 end
1416 end
1421
1417
1422 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1418 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1423 IssueRelation.delete_all
1419 IssueRelation.delete_all
1424 assert IssueRelation.create!(:issue_from => Issue.find(1),
1420 assert IssueRelation.create!(:issue_from => Issue.find(1),
1425 :issue_to => Issue.find(2),
1421 :issue_to => Issue.find(2),
1426 :relation_type => IssueRelation::TYPE_RELATES)
1422 :relation_type => IssueRelation::TYPE_RELATES)
1427 assert IssueRelation.create!(:issue_from => Issue.find(2),
1423 assert IssueRelation.create!(:issue_from => Issue.find(2),
1428 :issue_to => Issue.find(3),
1424 :issue_to => Issue.find(3),
1429 :relation_type => IssueRelation::TYPE_RELATES)
1425 :relation_type => IssueRelation::TYPE_RELATES)
1430 assert IssueRelation.create!(:issue_from => Issue.find(3),
1426 assert IssueRelation.create!(:issue_from => Issue.find(3),
1431 :issue_to => Issue.find(8),
1427 :issue_to => Issue.find(8),
1432 :relation_type => IssueRelation::TYPE_RELATES)
1428 :relation_type => IssueRelation::TYPE_RELATES)
1433
1429
1434 r = IssueRelation.create!(:issue_from => Issue.find(8),
1430 r = IssueRelation.create!(:issue_from => Issue.find(8),
1435 :issue_to => Issue.find(7),
1431 :issue_to => Issue.find(7),
1436 :relation_type => IssueRelation::TYPE_RELATES)
1432 :relation_type => IssueRelation::TYPE_RELATES)
1437 IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
1433 IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
1438
1434
1439 r = IssueRelation.create!(:issue_from => Issue.find(3),
1435 r = IssueRelation.create!(:issue_from => Issue.find(3),
1440 :issue_to => Issue.find(7),
1436 :issue_to => Issue.find(7),
1441 :relation_type => IssueRelation::TYPE_RELATES)
1437 :relation_type => IssueRelation::TYPE_RELATES)
1442 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1438 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1443
1439
1444 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1440 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1445 end
1441 end
1446
1442
1447 context "#done_ratio" do
1443 context "#done_ratio" do
1448 setup do
1444 setup do
1449 @issue = Issue.find(1)
1445 @issue = Issue.find(1)
1450 @issue_status = IssueStatus.find(1)
1446 @issue_status = IssueStatus.find(1)
1451 @issue_status.update_attribute(:default_done_ratio, 50)
1447 @issue_status.update_attribute(:default_done_ratio, 50)
1452 @issue2 = Issue.find(2)
1448 @issue2 = Issue.find(2)
1453 @issue_status2 = IssueStatus.find(2)
1449 @issue_status2 = IssueStatus.find(2)
1454 @issue_status2.update_attribute(:default_done_ratio, 0)
1450 @issue_status2.update_attribute(:default_done_ratio, 0)
1455 end
1451 end
1456
1452
1457 teardown do
1453 teardown do
1458 Setting.issue_done_ratio = 'issue_field'
1454 Setting.issue_done_ratio = 'issue_field'
1459 end
1455 end
1460
1456
1461 context "with Setting.issue_done_ratio using the issue_field" do
1457 context "with Setting.issue_done_ratio using the issue_field" do
1462 setup do
1458 setup do
1463 Setting.issue_done_ratio = 'issue_field'
1459 Setting.issue_done_ratio = 'issue_field'
1464 end
1460 end
1465
1461
1466 should "read the issue's field" do
1462 should "read the issue's field" do
1467 assert_equal 0, @issue.done_ratio
1463 assert_equal 0, @issue.done_ratio
1468 assert_equal 30, @issue2.done_ratio
1464 assert_equal 30, @issue2.done_ratio
1469 end
1465 end
1470 end
1466 end
1471
1467
1472 context "with Setting.issue_done_ratio using the issue_status" do
1468 context "with Setting.issue_done_ratio using the issue_status" do
1473 setup do
1469 setup do
1474 Setting.issue_done_ratio = 'issue_status'
1470 Setting.issue_done_ratio = 'issue_status'
1475 end
1471 end
1476
1472
1477 should "read the Issue Status's default done ratio" do
1473 should "read the Issue Status's default done ratio" do
1478 assert_equal 50, @issue.done_ratio
1474 assert_equal 50, @issue.done_ratio
1479 assert_equal 0, @issue2.done_ratio
1475 assert_equal 0, @issue2.done_ratio
1480 end
1476 end
1481 end
1477 end
1482 end
1478 end
1483
1479
1484 context "#update_done_ratio_from_issue_status" do
1480 context "#update_done_ratio_from_issue_status" do
1485 setup do
1481 setup do
1486 @issue = Issue.find(1)
1482 @issue = Issue.find(1)
1487 @issue_status = IssueStatus.find(1)
1483 @issue_status = IssueStatus.find(1)
1488 @issue_status.update_attribute(:default_done_ratio, 50)
1484 @issue_status.update_attribute(:default_done_ratio, 50)
1489 @issue2 = Issue.find(2)
1485 @issue2 = Issue.find(2)
1490 @issue_status2 = IssueStatus.find(2)
1486 @issue_status2 = IssueStatus.find(2)
1491 @issue_status2.update_attribute(:default_done_ratio, 0)
1487 @issue_status2.update_attribute(:default_done_ratio, 0)
1492 end
1488 end
1493
1489
1494 context "with Setting.issue_done_ratio using the issue_field" do
1490 context "with Setting.issue_done_ratio using the issue_field" do
1495 setup do
1491 setup do
1496 Setting.issue_done_ratio = 'issue_field'
1492 Setting.issue_done_ratio = 'issue_field'
1497 end
1493 end
1498
1494
1499 should "not change the issue" do
1495 should "not change the issue" do
1500 @issue.update_done_ratio_from_issue_status
1496 @issue.update_done_ratio_from_issue_status
1501 @issue2.update_done_ratio_from_issue_status
1497 @issue2.update_done_ratio_from_issue_status
1502
1498
1503 assert_equal 0, @issue.read_attribute(:done_ratio)
1499 assert_equal 0, @issue.read_attribute(:done_ratio)
1504 assert_equal 30, @issue2.read_attribute(:done_ratio)
1500 assert_equal 30, @issue2.read_attribute(:done_ratio)
1505 end
1501 end
1506 end
1502 end
1507
1503
1508 context "with Setting.issue_done_ratio using the issue_status" do
1504 context "with Setting.issue_done_ratio using the issue_status" do
1509 setup do
1505 setup do
1510 Setting.issue_done_ratio = 'issue_status'
1506 Setting.issue_done_ratio = 'issue_status'
1511 end
1507 end
1512
1508
1513 should "change the issue's done ratio" do
1509 should "change the issue's done ratio" do
1514 @issue.update_done_ratio_from_issue_status
1510 @issue.update_done_ratio_from_issue_status
1515 @issue2.update_done_ratio_from_issue_status
1511 @issue2.update_done_ratio_from_issue_status
1516
1512
1517 assert_equal 50, @issue.read_attribute(:done_ratio)
1513 assert_equal 50, @issue.read_attribute(:done_ratio)
1518 assert_equal 0, @issue2.read_attribute(:done_ratio)
1514 assert_equal 0, @issue2.read_attribute(:done_ratio)
1519 end
1515 end
1520 end
1516 end
1521 end
1517 end
1522
1518
1523 test "#by_tracker" do
1519 test "#by_tracker" do
1524 User.current = User.anonymous
1520 User.current = User.anonymous
1525 groups = Issue.by_tracker(Project.find(1))
1521 groups = Issue.by_tracker(Project.find(1))
1526 assert_equal 3, groups.size
1522 assert_equal 3, groups.size
1527 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1523 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1528 end
1524 end
1529
1525
1530 test "#by_version" do
1526 test "#by_version" do
1531 User.current = User.anonymous
1527 User.current = User.anonymous
1532 groups = Issue.by_version(Project.find(1))
1528 groups = Issue.by_version(Project.find(1))
1533 assert_equal 3, groups.size
1529 assert_equal 3, groups.size
1534 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1530 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1535 end
1531 end
1536
1532
1537 test "#by_priority" do
1533 test "#by_priority" do
1538 User.current = User.anonymous
1534 User.current = User.anonymous
1539 groups = Issue.by_priority(Project.find(1))
1535 groups = Issue.by_priority(Project.find(1))
1540 assert_equal 4, groups.size
1536 assert_equal 4, groups.size
1541 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1537 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1542 end
1538 end
1543
1539
1544 test "#by_category" do
1540 test "#by_category" do
1545 User.current = User.anonymous
1541 User.current = User.anonymous
1546 groups = Issue.by_category(Project.find(1))
1542 groups = Issue.by_category(Project.find(1))
1547 assert_equal 2, groups.size
1543 assert_equal 2, groups.size
1548 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1544 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1549 end
1545 end
1550
1546
1551 test "#by_assigned_to" do
1547 test "#by_assigned_to" do
1552 User.current = User.anonymous
1548 User.current = User.anonymous
1553 groups = Issue.by_assigned_to(Project.find(1))
1549 groups = Issue.by_assigned_to(Project.find(1))
1554 assert_equal 2, groups.size
1550 assert_equal 2, groups.size
1555 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1551 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1556 end
1552 end
1557
1553
1558 test "#by_author" do
1554 test "#by_author" do
1559 User.current = User.anonymous
1555 User.current = User.anonymous
1560 groups = Issue.by_author(Project.find(1))
1556 groups = Issue.by_author(Project.find(1))
1561 assert_equal 4, groups.size
1557 assert_equal 4, groups.size
1562 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1558 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1563 end
1559 end
1564
1560
1565 test "#by_subproject" do
1561 test "#by_subproject" do
1566 User.current = User.anonymous
1562 User.current = User.anonymous
1567 groups = Issue.by_subproject(Project.find(1))
1563 groups = Issue.by_subproject(Project.find(1))
1568 # Private descendant not visible
1564 # Private descendant not visible
1569 assert_equal 1, groups.size
1565 assert_equal 1, groups.size
1570 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1566 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1571 end
1567 end
1572
1568
1573 def test_recently_updated_scope
1569 def test_recently_updated_scope
1574 #should return the last updated issue
1570 #should return the last updated issue
1575 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
1571 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
1576 end
1572 end
1577
1573
1578 def test_on_active_projects_scope
1574 def test_on_active_projects_scope
1579 assert Project.find(2).archive
1575 assert Project.find(2).archive
1580
1576
1581 before = Issue.on_active_project.length
1577 before = Issue.on_active_project.length
1582 # test inclusion to results
1578 # test inclusion to results
1583 issue = Issue.generate_for_project!(Project.find(1), :tracker => Project.find(2).trackers.first)
1579 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
1584 assert_equal before + 1, Issue.on_active_project.length
1580 assert_equal before + 1, Issue.on_active_project.length
1585
1581
1586 # Move to an archived project
1582 # Move to an archived project
1587 issue.project = Project.find(2)
1583 issue.project = Project.find(2)
1588 assert issue.save
1584 assert issue.save
1589 assert_equal before, Issue.on_active_project.length
1585 assert_equal before, Issue.on_active_project.length
1590 end
1586 end
1591
1587
1592 context "Issue#recipients" do
1588 context "Issue#recipients" do
1593 setup do
1589 setup do
1594 @project = Project.find(1)
1590 @project = Project.find(1)
1595 @author = User.generate!
1591 @author = User.generate!
1596 @assignee = User.generate!
1592 @assignee = User.generate!
1597 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
1593 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1598 end
1594 end
1599
1595
1600 should "include project recipients" do
1596 should "include project recipients" do
1601 assert @project.recipients.present?
1597 assert @project.recipients.present?
1602 @project.recipients.each do |project_recipient|
1598 @project.recipients.each do |project_recipient|
1603 assert @issue.recipients.include?(project_recipient)
1599 assert @issue.recipients.include?(project_recipient)
1604 end
1600 end
1605 end
1601 end
1606
1602
1607 should "include the author if the author is active" do
1603 should "include the author if the author is active" do
1608 assert @issue.author, "No author set for Issue"
1604 assert @issue.author, "No author set for Issue"
1609 assert @issue.recipients.include?(@issue.author.mail)
1605 assert @issue.recipients.include?(@issue.author.mail)
1610 end
1606 end
1611
1607
1612 should "include the assigned to user if the assigned to user is active" do
1608 should "include the assigned to user if the assigned to user is active" do
1613 assert @issue.assigned_to, "No assigned_to set for Issue"
1609 assert @issue.assigned_to, "No assigned_to set for Issue"
1614 assert @issue.recipients.include?(@issue.assigned_to.mail)
1610 assert @issue.recipients.include?(@issue.assigned_to.mail)
1615 end
1611 end
1616
1612
1617 should "not include users who opt out of all email" do
1613 should "not include users who opt out of all email" do
1618 @author.update_attribute(:mail_notification, :none)
1614 @author.update_attribute(:mail_notification, :none)
1619
1615
1620 assert !@issue.recipients.include?(@issue.author.mail)
1616 assert !@issue.recipients.include?(@issue.author.mail)
1621 end
1617 end
1622
1618
1623 should "not include the issue author if they are only notified of assigned issues" do
1619 should "not include the issue author if they are only notified of assigned issues" do
1624 @author.update_attribute(:mail_notification, :only_assigned)
1620 @author.update_attribute(:mail_notification, :only_assigned)
1625
1621
1626 assert !@issue.recipients.include?(@issue.author.mail)
1622 assert !@issue.recipients.include?(@issue.author.mail)
1627 end
1623 end
1628
1624
1629 should "not include the assigned user if they are only notified of owned issues" do
1625 should "not include the assigned user if they are only notified of owned issues" do
1630 @assignee.update_attribute(:mail_notification, :only_owner)
1626 @assignee.update_attribute(:mail_notification, :only_owner)
1631
1627
1632 assert !@issue.recipients.include?(@issue.assigned_to.mail)
1628 assert !@issue.recipients.include?(@issue.assigned_to.mail)
1633 end
1629 end
1634 end
1630 end
1635
1631
1636 def test_last_journal_id_with_journals_should_return_the_journal_id
1632 def test_last_journal_id_with_journals_should_return_the_journal_id
1637 assert_equal 2, Issue.find(1).last_journal_id
1633 assert_equal 2, Issue.find(1).last_journal_id
1638 end
1634 end
1639
1635
1640 def test_last_journal_id_without_journals_should_return_nil
1636 def test_last_journal_id_without_journals_should_return_nil
1641 assert_nil Issue.find(3).last_journal_id
1637 assert_nil Issue.find(3).last_journal_id
1642 end
1638 end
1643
1639
1644 def test_journals_after_should_return_journals_with_greater_id
1640 def test_journals_after_should_return_journals_with_greater_id
1645 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
1641 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
1646 assert_equal [], Issue.find(1).journals_after('2')
1642 assert_equal [], Issue.find(1).journals_after('2')
1647 end
1643 end
1648
1644
1649 def test_journals_after_with_blank_arg_should_return_all_journals
1645 def test_journals_after_with_blank_arg_should_return_all_journals
1650 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
1646 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
1651 end
1647 end
1652 end
1648 end
@@ -1,749 +1,749
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../test_helper', __FILE__)
19
19
20 class Redmine::Helpers::GanttHelperTest < ActionView::TestCase
20 class Redmine::Helpers::GanttHelperTest < ActionView::TestCase
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :journals, :journal_details,
22 :journals, :journal_details,
23 :enumerations, :users, :issue_categories,
23 :enumerations, :users, :issue_categories,
24 :projects_trackers,
24 :projects_trackers,
25 :roles,
25 :roles,
26 :member_roles,
26 :member_roles,
27 :members,
27 :members,
28 :enabled_modules,
28 :enabled_modules,
29 :workflows,
29 :workflows,
30 :versions,
30 :versions,
31 :groups_users
31 :groups_users
32
32
33 include ApplicationHelper
33 include ApplicationHelper
34 include ProjectsHelper
34 include ProjectsHelper
35 include IssuesHelper
35 include IssuesHelper
36 include ERB::Util
36 include ERB::Util
37
37
38 def setup
38 def setup
39 setup_with_controller
39 setup_with_controller
40 User.current = User.find(1)
40 User.current = User.find(1)
41 end
41 end
42
42
43 def today
43 def today
44 @today ||= Date.today
44 @today ||= Date.today
45 end
45 end
46
46
47 # Creates a Gantt chart for a 4 week span
47 # Creates a Gantt chart for a 4 week span
48 def create_gantt(project=Project.generate!, options={})
48 def create_gantt(project=Project.generate!, options={})
49 @project = project
49 @project = project
50 @gantt = Redmine::Helpers::Gantt.new(options)
50 @gantt = Redmine::Helpers::Gantt.new(options)
51 @gantt.project = @project
51 @gantt.project = @project
52 @gantt.query = Query.create!(:project => @project, :name => 'Gantt')
52 @gantt.query = Query.create!(:project => @project, :name => 'Gantt')
53 @gantt.view = self
53 @gantt.view = self
54 @gantt.instance_variable_set('@date_from', options[:date_from] || (today - 14))
54 @gantt.instance_variable_set('@date_from', options[:date_from] || (today - 14))
55 @gantt.instance_variable_set('@date_to', options[:date_to] || (today + 14))
55 @gantt.instance_variable_set('@date_to', options[:date_to] || (today + 14))
56 end
56 end
57
57
58 context "#number_of_rows" do
58 context "#number_of_rows" do
59 context "with one project" do
59 context "with one project" do
60 should "return the number of rows just for that project"
60 should "return the number of rows just for that project"
61 end
61 end
62
62
63 context "with no project" do
63 context "with no project" do
64 should "return the total number of rows for all the projects, resursively"
64 should "return the total number of rows for all the projects, resursively"
65 end
65 end
66
66
67 should "not exceed max_rows option" do
67 should "not exceed max_rows option" do
68 p = Project.generate!
68 p = Project.generate!
69 5.times do
69 5.times do
70 Issue.generate_for_project!(p)
70 Issue.generate!(:project => p)
71 end
71 end
72 create_gantt(p)
72 create_gantt(p)
73 @gantt.render
73 @gantt.render
74 assert_equal 6, @gantt.number_of_rows
74 assert_equal 6, @gantt.number_of_rows
75 assert !@gantt.truncated
75 assert !@gantt.truncated
76 create_gantt(p, :max_rows => 3)
76 create_gantt(p, :max_rows => 3)
77 @gantt.render
77 @gantt.render
78 assert_equal 3, @gantt.number_of_rows
78 assert_equal 3, @gantt.number_of_rows
79 assert @gantt.truncated
79 assert @gantt.truncated
80 end
80 end
81 end
81 end
82
82
83 context "#number_of_rows_on_project" do
83 context "#number_of_rows_on_project" do
84 setup do
84 setup do
85 create_gantt
85 create_gantt
86 end
86 end
87
87
88 should "count 0 for an empty the project" do
88 should "count 0 for an empty the project" do
89 assert_equal 0, @gantt.number_of_rows_on_project(@project)
89 assert_equal 0, @gantt.number_of_rows_on_project(@project)
90 end
90 end
91
91
92 should "count the number of issues without a version" do
92 should "count the number of issues without a version" do
93 @project.issues << Issue.generate_for_project!(@project, :fixed_version => nil)
93 @project.issues << Issue.generate!(:project => @project, :fixed_version => nil)
94 assert_equal 2, @gantt.number_of_rows_on_project(@project)
94 assert_equal 2, @gantt.number_of_rows_on_project(@project)
95 end
95 end
96
96
97 should "count the number of issues on versions, including cross-project" do
97 should "count the number of issues on versions, including cross-project" do
98 version = Version.generate!
98 version = Version.generate!
99 @project.versions << version
99 @project.versions << version
100 @project.issues << Issue.generate_for_project!(@project, :fixed_version => version)
100 @project.issues << Issue.generate!(:project => @project, :fixed_version => version)
101 assert_equal 3, @gantt.number_of_rows_on_project(@project)
101 assert_equal 3, @gantt.number_of_rows_on_project(@project)
102 end
102 end
103 end
103 end
104
104
105 # TODO: more of an integration test
105 # TODO: more of an integration test
106 context "#subjects" do
106 context "#subjects" do
107 setup do
107 setup do
108 create_gantt
108 create_gantt
109 @project.enabled_module_names = [:issue_tracking]
109 @project.enabled_module_names = [:issue_tracking]
110 @tracker = Tracker.generate!
110 @tracker = Tracker.generate!
111 @project.trackers << @tracker
111 @project.trackers << @tracker
112 @version = Version.generate!(:effective_date => (today + 7), :sharing => 'none')
112 @version = Version.generate!(:effective_date => (today + 7), :sharing => 'none')
113 @project.versions << @version
113 @project.versions << @version
114 @issue = Issue.generate!(:fixed_version => @version,
114 @issue = Issue.generate!(:fixed_version => @version,
115 :subject => "gantt#line_for_project",
115 :subject => "gantt#line_for_project",
116 :tracker => @tracker,
116 :tracker => @tracker,
117 :project => @project,
117 :project => @project,
118 :done_ratio => 30,
118 :done_ratio => 30,
119 :start_date => (today - 1),
119 :start_date => (today - 1),
120 :due_date => (today + 7))
120 :due_date => (today + 7))
121 @project.issues << @issue
121 @project.issues << @issue
122 end
122 end
123
123
124 context "project" do
124 context "project" do
125 should "be rendered" do
125 should "be rendered" do
126 @output_buffer = @gantt.subjects
126 @output_buffer = @gantt.subjects
127 assert_select "div.project-name a", /#{@project.name}/
127 assert_select "div.project-name a", /#{@project.name}/
128 end
128 end
129
129
130 should "have an indent of 4" do
130 should "have an indent of 4" do
131 @output_buffer = @gantt.subjects
131 @output_buffer = @gantt.subjects
132 assert_select "div.project-name[style*=left:4px]"
132 assert_select "div.project-name[style*=left:4px]"
133 end
133 end
134 end
134 end
135
135
136 context "version" do
136 context "version" do
137 should "be rendered" do
137 should "be rendered" do
138 @output_buffer = @gantt.subjects
138 @output_buffer = @gantt.subjects
139 assert_select "div.version-name a", /#{@version.name}/
139 assert_select "div.version-name a", /#{@version.name}/
140 end
140 end
141
141
142 should "be indented 24 (one level)" do
142 should "be indented 24 (one level)" do
143 @output_buffer = @gantt.subjects
143 @output_buffer = @gantt.subjects
144 assert_select "div.version-name[style*=left:24px]"
144 assert_select "div.version-name[style*=left:24px]"
145 end
145 end
146
146
147 context "without assigned issues" do
147 context "without assigned issues" do
148 setup do
148 setup do
149 @version = Version.generate!(:effective_date => (today + 14),
149 @version = Version.generate!(:effective_date => (today + 14),
150 :sharing => 'none',
150 :sharing => 'none',
151 :name => 'empty_version')
151 :name => 'empty_version')
152 @project.versions << @version
152 @project.versions << @version
153 end
153 end
154
154
155 should "not be rendered" do
155 should "not be rendered" do
156 @output_buffer = @gantt.subjects
156 @output_buffer = @gantt.subjects
157 assert_select "div.version-name a", :text => /#{@version.name}/, :count => 0
157 assert_select "div.version-name a", :text => /#{@version.name}/, :count => 0
158 end
158 end
159 end
159 end
160 end
160 end
161
161
162 context "issue" do
162 context "issue" do
163 should "be rendered" do
163 should "be rendered" do
164 @output_buffer = @gantt.subjects
164 @output_buffer = @gantt.subjects
165 assert_select "div.issue-subject", /#{@issue.subject}/
165 assert_select "div.issue-subject", /#{@issue.subject}/
166 end
166 end
167
167
168 should "be indented 44 (two levels)" do
168 should "be indented 44 (two levels)" do
169 @output_buffer = @gantt.subjects
169 @output_buffer = @gantt.subjects
170 assert_select "div.issue-subject[style*=left:44px]"
170 assert_select "div.issue-subject[style*=left:44px]"
171 end
171 end
172
172
173 context "assigned to a shared version of another project" do
173 context "assigned to a shared version of another project" do
174 setup do
174 setup do
175 p = Project.generate!
175 p = Project.generate!
176 p.enabled_module_names = [:issue_tracking]
176 p.enabled_module_names = [:issue_tracking]
177 @shared_version = Version.generate!(:sharing => 'system')
177 @shared_version = Version.generate!(:sharing => 'system')
178 p.versions << @shared_version
178 p.versions << @shared_version
179 # Reassign the issue to a shared version of another project
179 # Reassign the issue to a shared version of another project
180 @issue = Issue.generate!(:fixed_version => @shared_version,
180 @issue = Issue.generate!(:fixed_version => @shared_version,
181 :subject => "gantt#assigned_to_shared_version",
181 :subject => "gantt#assigned_to_shared_version",
182 :tracker => @tracker,
182 :tracker => @tracker,
183 :project => @project,
183 :project => @project,
184 :done_ratio => 30,
184 :done_ratio => 30,
185 :start_date => (today - 1),
185 :start_date => (today - 1),
186 :due_date => (today + 7))
186 :due_date => (today + 7))
187 @project.issues << @issue
187 @project.issues << @issue
188 end
188 end
189
189
190 should "be rendered" do
190 should "be rendered" do
191 @output_buffer = @gantt.subjects
191 @output_buffer = @gantt.subjects
192 assert_select "div.issue-subject", /#{@issue.subject}/
192 assert_select "div.issue-subject", /#{@issue.subject}/
193 end
193 end
194 end
194 end
195
195
196 context "with subtasks" do
196 context "with subtasks" do
197 setup do
197 setup do
198 attrs = {:project => @project, :tracker => @tracker, :fixed_version => @version}
198 attrs = {:project => @project, :tracker => @tracker, :fixed_version => @version}
199 @child1 = Issue.generate!(
199 @child1 = Issue.generate!(
200 attrs.merge(:subject => 'child1',
200 attrs.merge(:subject => 'child1',
201 :parent_issue_id => @issue.id,
201 :parent_issue_id => @issue.id,
202 :start_date => (today - 1),
202 :start_date => (today - 1),
203 :due_date => (today + 2))
203 :due_date => (today + 2))
204 )
204 )
205 @child2 = Issue.generate!(
205 @child2 = Issue.generate!(
206 attrs.merge(:subject => 'child2',
206 attrs.merge(:subject => 'child2',
207 :parent_issue_id => @issue.id,
207 :parent_issue_id => @issue.id,
208 :start_date => today,
208 :start_date => today,
209 :due_date => (today + 7))
209 :due_date => (today + 7))
210 )
210 )
211 @grandchild = Issue.generate!(
211 @grandchild = Issue.generate!(
212 attrs.merge(:subject => 'grandchild',
212 attrs.merge(:subject => 'grandchild',
213 :parent_issue_id => @child1.id,
213 :parent_issue_id => @child1.id,
214 :start_date => (today - 1),
214 :start_date => (today - 1),
215 :due_date => (today + 2))
215 :due_date => (today + 2))
216 )
216 )
217 end
217 end
218
218
219 should "indent subtasks" do
219 should "indent subtasks" do
220 @output_buffer = @gantt.subjects
220 @output_buffer = @gantt.subjects
221 # parent task 44px
221 # parent task 44px
222 assert_select "div.issue-subject[style*=left:44px]", /#{@issue.subject}/
222 assert_select "div.issue-subject[style*=left:44px]", /#{@issue.subject}/
223 # children 64px
223 # children 64px
224 assert_select "div.issue-subject[style*=left:64px]", /child1/
224 assert_select "div.issue-subject[style*=left:64px]", /child1/
225 assert_select "div.issue-subject[style*=left:64px]", /child2/
225 assert_select "div.issue-subject[style*=left:64px]", /child2/
226 # grandchild 84px
226 # grandchild 84px
227 assert_select "div.issue-subject[style*=left:84px]", /grandchild/, @output_buffer
227 assert_select "div.issue-subject[style*=left:84px]", /grandchild/, @output_buffer
228 end
228 end
229 end
229 end
230 end
230 end
231 end
231 end
232
232
233 context "#lines" do
233 context "#lines" do
234 setup do
234 setup do
235 create_gantt
235 create_gantt
236 @project.enabled_module_names = [:issue_tracking]
236 @project.enabled_module_names = [:issue_tracking]
237 @tracker = Tracker.generate!
237 @tracker = Tracker.generate!
238 @project.trackers << @tracker
238 @project.trackers << @tracker
239 @version = Version.generate!(:effective_date => (today + 7))
239 @version = Version.generate!(:effective_date => (today + 7))
240 @project.versions << @version
240 @project.versions << @version
241 @issue = Issue.generate!(:fixed_version => @version,
241 @issue = Issue.generate!(:fixed_version => @version,
242 :subject => "gantt#line_for_project",
242 :subject => "gantt#line_for_project",
243 :tracker => @tracker,
243 :tracker => @tracker,
244 :project => @project,
244 :project => @project,
245 :done_ratio => 30,
245 :done_ratio => 30,
246 :start_date => (today - 1),
246 :start_date => (today - 1),
247 :due_date => (today + 7))
247 :due_date => (today + 7))
248 @project.issues << @issue
248 @project.issues << @issue
249 @output_buffer = @gantt.lines
249 @output_buffer = @gantt.lines
250 end
250 end
251
251
252 context "project" do
252 context "project" do
253 should "be rendered" do
253 should "be rendered" do
254 assert_select "div.project.task_todo"
254 assert_select "div.project.task_todo"
255 assert_select "div.project.starting"
255 assert_select "div.project.starting"
256 assert_select "div.project.ending"
256 assert_select "div.project.ending"
257 assert_select "div.label.project", /#{@project.name}/
257 assert_select "div.label.project", /#{@project.name}/
258 end
258 end
259 end
259 end
260
260
261 context "version" do
261 context "version" do
262 should "be rendered" do
262 should "be rendered" do
263 assert_select "div.version.task_todo"
263 assert_select "div.version.task_todo"
264 assert_select "div.version.starting"
264 assert_select "div.version.starting"
265 assert_select "div.version.ending"
265 assert_select "div.version.ending"
266 assert_select "div.label.version", /#{@version.name}/
266 assert_select "div.label.version", /#{@version.name}/
267 end
267 end
268 end
268 end
269
269
270 context "issue" do
270 context "issue" do
271 should "be rendered" do
271 should "be rendered" do
272 assert_select "div.task_todo"
272 assert_select "div.task_todo"
273 assert_select "div.task.label", /#{@issue.done_ratio}/
273 assert_select "div.task.label", /#{@issue.done_ratio}/
274 assert_select "div.tooltip", /#{@issue.subject}/
274 assert_select "div.tooltip", /#{@issue.subject}/
275 end
275 end
276 end
276 end
277 end
277 end
278
278
279 context "#render_project" do
279 context "#render_project" do
280 should "be tested"
280 should "be tested"
281 end
281 end
282
282
283 context "#render_issues" do
283 context "#render_issues" do
284 should "be tested"
284 should "be tested"
285 end
285 end
286
286
287 context "#render_version" do
287 context "#render_version" do
288 should "be tested"
288 should "be tested"
289 end
289 end
290
290
291 context "#subject_for_project" do
291 context "#subject_for_project" do
292 setup do
292 setup do
293 create_gantt
293 create_gantt
294 end
294 end
295
295
296 context ":html format" do
296 context ":html format" do
297 should "add an absolute positioned div" do
297 should "add an absolute positioned div" do
298 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
298 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
299 assert_select "div[style*=absolute]"
299 assert_select "div[style*=absolute]"
300 end
300 end
301
301
302 should "use the indent option to move the div to the right" do
302 should "use the indent option to move the div to the right" do
303 @output_buffer = @gantt.subject_for_project(@project, {:format => :html, :indent => 40})
303 @output_buffer = @gantt.subject_for_project(@project, {:format => :html, :indent => 40})
304 assert_select "div[style*=left:40]"
304 assert_select "div[style*=left:40]"
305 end
305 end
306
306
307 should "include the project name" do
307 should "include the project name" do
308 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
308 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
309 assert_select 'div', :text => /#{@project.name}/
309 assert_select 'div', :text => /#{@project.name}/
310 end
310 end
311
311
312 should "include a link to the project" do
312 should "include a link to the project" do
313 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
313 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
314 assert_select 'a[href=?]', "/projects/#{@project.identifier}", :text => /#{@project.name}/
314 assert_select 'a[href=?]', "/projects/#{@project.identifier}", :text => /#{@project.name}/
315 end
315 end
316
316
317 should "style overdue projects" do
317 should "style overdue projects" do
318 @project.enabled_module_names = [:issue_tracking]
318 @project.enabled_module_names = [:issue_tracking]
319 @project.versions << Version.generate!(:effective_date => (today - 1))
319 @project.versions << Version.generate!(:effective_date => (today - 1))
320 assert @project.reload.overdue?, "Need an overdue project for this test"
320 assert @project.reload.overdue?, "Need an overdue project for this test"
321 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
321 @output_buffer = @gantt.subject_for_project(@project, {:format => :html})
322 assert_select 'div span.project-overdue'
322 assert_select 'div span.project-overdue'
323 end
323 end
324 end
324 end
325 should "test the PNG format"
325 should "test the PNG format"
326 should "test the PDF format"
326 should "test the PDF format"
327 end
327 end
328
328
329 context "#line_for_project" do
329 context "#line_for_project" do
330 setup do
330 setup do
331 create_gantt
331 create_gantt
332 @project.enabled_module_names = [:issue_tracking]
332 @project.enabled_module_names = [:issue_tracking]
333 @tracker = Tracker.generate!
333 @tracker = Tracker.generate!
334 @project.trackers << @tracker
334 @project.trackers << @tracker
335 @version = Version.generate!(:effective_date => (today - 1))
335 @version = Version.generate!(:effective_date => (today - 1))
336 @project.versions << @version
336 @project.versions << @version
337 @project.issues << Issue.generate!(:fixed_version => @version,
337 @project.issues << Issue.generate!(:fixed_version => @version,
338 :subject => "gantt#line_for_project",
338 :subject => "gantt#line_for_project",
339 :tracker => @tracker,
339 :tracker => @tracker,
340 :project => @project,
340 :project => @project,
341 :done_ratio => 30,
341 :done_ratio => 30,
342 :start_date => (today - 7),
342 :start_date => (today - 7),
343 :due_date => (today + 7))
343 :due_date => (today + 7))
344 end
344 end
345
345
346 context ":html format" do
346 context ":html format" do
347 context "todo line" do
347 context "todo line" do
348 should "start from the starting point on the left" do
348 should "start from the starting point on the left" do
349 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
349 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
350 assert_select "div.project.task_todo[style*=left:28px]", true, @output_buffer
350 assert_select "div.project.task_todo[style*=left:28px]", true, @output_buffer
351 end
351 end
352
352
353 should "be the total width of the project" do
353 should "be the total width of the project" do
354 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
354 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
355 assert_select "div.project.task_todo[style*=width:58px]", true, @output_buffer
355 assert_select "div.project.task_todo[style*=width:58px]", true, @output_buffer
356 end
356 end
357 end
357 end
358
358
359 context "late line" do
359 context "late line" do
360 should_eventually "start from the starting point on the left" do
360 should_eventually "start from the starting point on the left" do
361 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
361 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
362 assert_select "div.project.task_late[style*=left:28px]", true, @output_buffer
362 assert_select "div.project.task_late[style*=left:28px]", true, @output_buffer
363 end
363 end
364
364
365 should_eventually "be the total delayed width of the project" do
365 should_eventually "be the total delayed width of the project" do
366 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
366 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
367 assert_select "div.project.task_late[style*=width:30px]", true, @output_buffer
367 assert_select "div.project.task_late[style*=width:30px]", true, @output_buffer
368 end
368 end
369 end
369 end
370
370
371 context "done line" do
371 context "done line" do
372 should_eventually "start from the starting point on the left" do
372 should_eventually "start from the starting point on the left" do
373 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
373 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
374 assert_select "div.project.task_done[style*=left:28px]", true, @output_buffer
374 assert_select "div.project.task_done[style*=left:28px]", true, @output_buffer
375 end
375 end
376
376
377 should_eventually "Be the total done width of the project" do
377 should_eventually "Be the total done width of the project" do
378 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
378 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
379 assert_select "div.project.task_done[style*=width:18px]", true, @output_buffer
379 assert_select "div.project.task_done[style*=width:18px]", true, @output_buffer
380 end
380 end
381 end
381 end
382
382
383 context "starting marker" do
383 context "starting marker" do
384 should "not appear if the starting point is off the gantt chart" do
384 should "not appear if the starting point is off the gantt chart" do
385 # Shift the date range of the chart
385 # Shift the date range of the chart
386 @gantt.instance_variable_set('@date_from', today)
386 @gantt.instance_variable_set('@date_from', today)
387 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
387 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
388 assert_select "div.project.starting", false, @output_buffer
388 assert_select "div.project.starting", false, @output_buffer
389 end
389 end
390
390
391 should "appear at the starting point" do
391 should "appear at the starting point" do
392 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
392 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
393 assert_select "div.project.starting[style*=left:28px]", true, @output_buffer
393 assert_select "div.project.starting[style*=left:28px]", true, @output_buffer
394 end
394 end
395 end
395 end
396
396
397 context "ending marker" do
397 context "ending marker" do
398 should "not appear if the starting point is off the gantt chart" do
398 should "not appear if the starting point is off the gantt chart" do
399 # Shift the date range of the chart
399 # Shift the date range of the chart
400 @gantt.instance_variable_set('@date_to', (today - 14))
400 @gantt.instance_variable_set('@date_to', (today - 14))
401 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
401 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
402 assert_select "div.project.ending", false, @output_buffer
402 assert_select "div.project.ending", false, @output_buffer
403 end
403 end
404
404
405 should "appear at the end of the date range" do
405 should "appear at the end of the date range" do
406 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
406 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
407 assert_select "div.project.ending[style*=left:88px]", true, @output_buffer
407 assert_select "div.project.ending[style*=left:88px]", true, @output_buffer
408 end
408 end
409 end
409 end
410
410
411 context "status content" do
411 context "status content" do
412 should "appear at the far left, even if it's far in the past" do
412 should "appear at the far left, even if it's far in the past" do
413 @gantt.instance_variable_set('@date_to', (today - 14))
413 @gantt.instance_variable_set('@date_to', (today - 14))
414 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
414 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
415 assert_select "div.project.label", /#{@project.name}/
415 assert_select "div.project.label", /#{@project.name}/
416 end
416 end
417
417
418 should "show the project name" do
418 should "show the project name" do
419 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
419 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
420 assert_select "div.project.label", /#{@project.name}/
420 assert_select "div.project.label", /#{@project.name}/
421 end
421 end
422
422
423 should_eventually "show the percent complete" do
423 should_eventually "show the percent complete" do
424 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
424 @output_buffer = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
425 assert_select "div.project.label", /0%/
425 assert_select "div.project.label", /0%/
426 end
426 end
427 end
427 end
428 end
428 end
429 should "test the PNG format"
429 should "test the PNG format"
430 should "test the PDF format"
430 should "test the PDF format"
431 end
431 end
432
432
433 context "#subject_for_version" do
433 context "#subject_for_version" do
434 setup do
434 setup do
435 create_gantt
435 create_gantt
436 @project.enabled_module_names = [:issue_tracking]
436 @project.enabled_module_names = [:issue_tracking]
437 @tracker = Tracker.generate!
437 @tracker = Tracker.generate!
438 @project.trackers << @tracker
438 @project.trackers << @tracker
439 @version = Version.generate!(:effective_date => (today - 1))
439 @version = Version.generate!(:effective_date => (today - 1))
440 @project.versions << @version
440 @project.versions << @version
441 @project.issues << Issue.generate!(:fixed_version => @version,
441 @project.issues << Issue.generate!(:fixed_version => @version,
442 :subject => "gantt#subject_for_version",
442 :subject => "gantt#subject_for_version",
443 :tracker => @tracker,
443 :tracker => @tracker,
444 :project => @project,
444 :project => @project,
445 :start_date => today)
445 :start_date => today)
446
446
447 end
447 end
448
448
449 context ":html format" do
449 context ":html format" do
450 should "add an absolute positioned div" do
450 should "add an absolute positioned div" do
451 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
451 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
452 assert_select "div[style*=absolute]"
452 assert_select "div[style*=absolute]"
453 end
453 end
454
454
455 should "use the indent option to move the div to the right" do
455 should "use the indent option to move the div to the right" do
456 @output_buffer = @gantt.subject_for_version(@version, {:format => :html, :indent => 40})
456 @output_buffer = @gantt.subject_for_version(@version, {:format => :html, :indent => 40})
457 assert_select "div[style*=left:40]"
457 assert_select "div[style*=left:40]"
458 end
458 end
459
459
460 should "include the version name" do
460 should "include the version name" do
461 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
461 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
462 assert_select 'div', :text => /#{@version.name}/
462 assert_select 'div', :text => /#{@version.name}/
463 end
463 end
464
464
465 should "include a link to the version" do
465 should "include a link to the version" do
466 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
466 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
467 assert_select 'a[href=?]', Regexp.escape("/versions/#{@version.to_param}"), :text => /#{@version.name}/
467 assert_select 'a[href=?]', Regexp.escape("/versions/#{@version.to_param}"), :text => /#{@version.name}/
468 end
468 end
469
469
470 should "style late versions" do
470 should "style late versions" do
471 assert @version.overdue?, "Need an overdue version for this test"
471 assert @version.overdue?, "Need an overdue version for this test"
472 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
472 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
473 assert_select 'div span.version-behind-schedule'
473 assert_select 'div span.version-behind-schedule'
474 end
474 end
475
475
476 should "style behind schedule versions" do
476 should "style behind schedule versions" do
477 assert @version.behind_schedule?, "Need a behind schedule version for this test"
477 assert @version.behind_schedule?, "Need a behind schedule version for this test"
478 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
478 @output_buffer = @gantt.subject_for_version(@version, {:format => :html})
479 assert_select 'div span.version-behind-schedule'
479 assert_select 'div span.version-behind-schedule'
480 end
480 end
481 end
481 end
482 should "test the PNG format"
482 should "test the PNG format"
483 should "test the PDF format"
483 should "test the PDF format"
484 end
484 end
485
485
486 context "#line_for_version" do
486 context "#line_for_version" do
487 setup do
487 setup do
488 create_gantt
488 create_gantt
489 @project.enabled_module_names = [:issue_tracking]
489 @project.enabled_module_names = [:issue_tracking]
490 @tracker = Tracker.generate!
490 @tracker = Tracker.generate!
491 @project.trackers << @tracker
491 @project.trackers << @tracker
492 @version = Version.generate!(:effective_date => (today + 7))
492 @version = Version.generate!(:effective_date => (today + 7))
493 @project.versions << @version
493 @project.versions << @version
494 @project.issues << Issue.generate!(:fixed_version => @version,
494 @project.issues << Issue.generate!(:fixed_version => @version,
495 :subject => "gantt#line_for_project",
495 :subject => "gantt#line_for_project",
496 :tracker => @tracker,
496 :tracker => @tracker,
497 :project => @project,
497 :project => @project,
498 :done_ratio => 30,
498 :done_ratio => 30,
499 :start_date => (today - 7),
499 :start_date => (today - 7),
500 :due_date => (today + 7))
500 :due_date => (today + 7))
501 end
501 end
502
502
503 context ":html format" do
503 context ":html format" do
504 context "todo line" do
504 context "todo line" do
505 should "start from the starting point on the left" do
505 should "start from the starting point on the left" do
506 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
506 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
507 assert_select "div.version.task_todo[style*=left:28px]", true, @output_buffer
507 assert_select "div.version.task_todo[style*=left:28px]", true, @output_buffer
508 end
508 end
509
509
510 should "be the total width of the version" do
510 should "be the total width of the version" do
511 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
511 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
512 assert_select "div.version.task_todo[style*=width:58px]", true, @output_buffer
512 assert_select "div.version.task_todo[style*=width:58px]", true, @output_buffer
513 end
513 end
514 end
514 end
515
515
516 context "late line" do
516 context "late line" do
517 should "start from the starting point on the left" do
517 should "start from the starting point on the left" do
518 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
518 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
519 assert_select "div.version.task_late[style*=left:28px]", true, @output_buffer
519 assert_select "div.version.task_late[style*=left:28px]", true, @output_buffer
520 end
520 end
521
521
522 should "be the total delayed width of the version" do
522 should "be the total delayed width of the version" do
523 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
523 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
524 assert_select "div.version.task_late[style*=width:30px]", true, @output_buffer
524 assert_select "div.version.task_late[style*=width:30px]", true, @output_buffer
525 end
525 end
526 end
526 end
527
527
528 context "done line" do
528 context "done line" do
529 should "start from the starting point on the left" do
529 should "start from the starting point on the left" do
530 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
530 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
531 assert_select "div.version.task_done[style*=left:28px]", true, @output_buffer
531 assert_select "div.version.task_done[style*=left:28px]", true, @output_buffer
532 end
532 end
533
533
534 should "be the total done width of the version" do
534 should "be the total done width of the version" do
535 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
535 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
536 assert_select "div.version.task_done[style*=width:16px]", true, @output_buffer
536 assert_select "div.version.task_done[style*=width:16px]", true, @output_buffer
537 end
537 end
538 end
538 end
539
539
540 context "starting marker" do
540 context "starting marker" do
541 should "not appear if the starting point is off the gantt chart" do
541 should "not appear if the starting point is off the gantt chart" do
542 # Shift the date range of the chart
542 # Shift the date range of the chart
543 @gantt.instance_variable_set('@date_from', today)
543 @gantt.instance_variable_set('@date_from', today)
544 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
544 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
545 assert_select "div.version.starting", false
545 assert_select "div.version.starting", false
546 end
546 end
547
547
548 should "appear at the starting point" do
548 should "appear at the starting point" do
549 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
549 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
550 assert_select "div.version.starting[style*=left:28px]", true, @output_buffer
550 assert_select "div.version.starting[style*=left:28px]", true, @output_buffer
551 end
551 end
552 end
552 end
553
553
554 context "ending marker" do
554 context "ending marker" do
555 should "not appear if the starting point is off the gantt chart" do
555 should "not appear if the starting point is off the gantt chart" do
556 # Shift the date range of the chart
556 # Shift the date range of the chart
557 @gantt.instance_variable_set('@date_to', (today - 14))
557 @gantt.instance_variable_set('@date_to', (today - 14))
558 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
558 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
559 assert_select "div.version.ending", false
559 assert_select "div.version.ending", false
560 end
560 end
561
561
562 should "appear at the end of the date range" do
562 should "appear at the end of the date range" do
563 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
563 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
564 assert_select "div.version.ending[style*=left:88px]", true, @output_buffer
564 assert_select "div.version.ending[style*=left:88px]", true, @output_buffer
565 end
565 end
566 end
566 end
567
567
568 context "status content" do
568 context "status content" do
569 should "appear at the far left, even if it's far in the past" do
569 should "appear at the far left, even if it's far in the past" do
570 @gantt.instance_variable_set('@date_to', (today - 14))
570 @gantt.instance_variable_set('@date_to', (today - 14))
571 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
571 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
572 assert_select "div.version.label", /#{@version.name}/
572 assert_select "div.version.label", /#{@version.name}/
573 end
573 end
574
574
575 should "show the version name" do
575 should "show the version name" do
576 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
576 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
577 assert_select "div.version.label", /#{@version.name}/
577 assert_select "div.version.label", /#{@version.name}/
578 end
578 end
579
579
580 should "show the percent complete" do
580 should "show the percent complete" do
581 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
581 @output_buffer = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
582 assert_select "div.version.label", /30%/
582 assert_select "div.version.label", /30%/
583 end
583 end
584 end
584 end
585 end
585 end
586 should "test the PNG format"
586 should "test the PNG format"
587 should "test the PDF format"
587 should "test the PDF format"
588 end
588 end
589
589
590 context "#subject_for_issue" do
590 context "#subject_for_issue" do
591 setup do
591 setup do
592 create_gantt
592 create_gantt
593 @project.enabled_module_names = [:issue_tracking]
593 @project.enabled_module_names = [:issue_tracking]
594 @tracker = Tracker.generate!
594 @tracker = Tracker.generate!
595 @project.trackers << @tracker
595 @project.trackers << @tracker
596 @issue = Issue.generate!(:subject => "gantt#subject_for_issue",
596 @issue = Issue.generate!(:subject => "gantt#subject_for_issue",
597 :tracker => @tracker,
597 :tracker => @tracker,
598 :project => @project,
598 :project => @project,
599 :start_date => (today - 3),
599 :start_date => (today - 3),
600 :due_date => (today - 1))
600 :due_date => (today - 1))
601 @project.issues << @issue
601 @project.issues << @issue
602 end
602 end
603
603
604 context ":html format" do
604 context ":html format" do
605 should "add an absolute positioned div" do
605 should "add an absolute positioned div" do
606 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
606 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
607 assert_select "div[style*=absolute]"
607 assert_select "div[style*=absolute]"
608 end
608 end
609
609
610 should "use the indent option to move the div to the right" do
610 should "use the indent option to move the div to the right" do
611 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html, :indent => 40})
611 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html, :indent => 40})
612 assert_select "div[style*=left:40]"
612 assert_select "div[style*=left:40]"
613 end
613 end
614
614
615 should "include the issue subject" do
615 should "include the issue subject" do
616 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
616 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
617 assert_select 'div', :text => /#{@issue.subject}/
617 assert_select 'div', :text => /#{@issue.subject}/
618 end
618 end
619
619
620 should "include a link to the issue" do
620 should "include a link to the issue" do
621 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
621 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
622 assert_select 'a[href=?]', Regexp.escape("/issues/#{@issue.to_param}"), :text => /#{@tracker.name} ##{@issue.id}/
622 assert_select 'a[href=?]', Regexp.escape("/issues/#{@issue.to_param}"), :text => /#{@tracker.name} ##{@issue.id}/
623 end
623 end
624
624
625 should "style overdue issues" do
625 should "style overdue issues" do
626 assert @issue.overdue?, "Need an overdue issue for this test"
626 assert @issue.overdue?, "Need an overdue issue for this test"
627 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
627 @output_buffer = @gantt.subject_for_issue(@issue, {:format => :html})
628 assert_select 'div span.issue-overdue'
628 assert_select 'div span.issue-overdue'
629 end
629 end
630 end
630 end
631 should "test the PNG format"
631 should "test the PNG format"
632 should "test the PDF format"
632 should "test the PDF format"
633 end
633 end
634
634
635 context "#line_for_issue" do
635 context "#line_for_issue" do
636 setup do
636 setup do
637 create_gantt
637 create_gantt
638 @project.enabled_module_names = [:issue_tracking]
638 @project.enabled_module_names = [:issue_tracking]
639 @tracker = Tracker.generate!
639 @tracker = Tracker.generate!
640 @project.trackers << @tracker
640 @project.trackers << @tracker
641 @version = Version.generate!(:effective_date => (today + 7))
641 @version = Version.generate!(:effective_date => (today + 7))
642 @project.versions << @version
642 @project.versions << @version
643 @issue = Issue.generate!(:fixed_version => @version,
643 @issue = Issue.generate!(:fixed_version => @version,
644 :subject => "gantt#line_for_project",
644 :subject => "gantt#line_for_project",
645 :tracker => @tracker,
645 :tracker => @tracker,
646 :project => @project,
646 :project => @project,
647 :done_ratio => 30,
647 :done_ratio => 30,
648 :start_date => (today - 7),
648 :start_date => (today - 7),
649 :due_date => (today + 7))
649 :due_date => (today + 7))
650 @project.issues << @issue
650 @project.issues << @issue
651 end
651 end
652
652
653 context ":html format" do
653 context ":html format" do
654 context "todo line" do
654 context "todo line" do
655 should "start from the starting point on the left" do
655 should "start from the starting point on the left" do
656 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
656 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
657 assert_select "div.task_todo[style*=left:28px]", true, @output_buffer
657 assert_select "div.task_todo[style*=left:28px]", true, @output_buffer
658 end
658 end
659
659
660 should "be the total width of the issue" do
660 should "be the total width of the issue" do
661 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
661 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
662 assert_select "div.task_todo[style*=width:58px]", true, @output_buffer
662 assert_select "div.task_todo[style*=width:58px]", true, @output_buffer
663 end
663 end
664 end
664 end
665
665
666 context "late line" do
666 context "late line" do
667 should "start from the starting point on the left" do
667 should "start from the starting point on the left" do
668 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
668 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
669 assert_select "div.task_late[style*=left:28px]", true, @output_buffer
669 assert_select "div.task_late[style*=left:28px]", true, @output_buffer
670 end
670 end
671
671
672 should "be the total delayed width of the issue" do
672 should "be the total delayed width of the issue" do
673 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
673 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
674 assert_select "div.task_late[style*=width:30px]", true, @output_buffer
674 assert_select "div.task_late[style*=width:30px]", true, @output_buffer
675 end
675 end
676 end
676 end
677
677
678 context "done line" do
678 context "done line" do
679 should "start from the starting point on the left" do
679 should "start from the starting point on the left" do
680 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
680 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
681 assert_select "div.task_done[style*=left:28px]", true, @output_buffer
681 assert_select "div.task_done[style*=left:28px]", true, @output_buffer
682 end
682 end
683
683
684 should "be the total done width of the issue" do
684 should "be the total done width of the issue" do
685 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
685 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
686 # 15 days * 4 px * 30% - 2 px for borders = 16 px
686 # 15 days * 4 px * 30% - 2 px for borders = 16 px
687 assert_select "div.task_done[style*=width:16px]", true, @output_buffer
687 assert_select "div.task_done[style*=width:16px]", true, @output_buffer
688 end
688 end
689
689
690 should "not be the total done width if the chart starts after issue start date" do
690 should "not be the total done width if the chart starts after issue start date" do
691 create_gantt(@project, :date_from => (today - 5))
691 create_gantt(@project, :date_from => (today - 5))
692 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
692 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
693 assert_select "div.task_done[style*=left:0px]", true, @output_buffer
693 assert_select "div.task_done[style*=left:0px]", true, @output_buffer
694 assert_select "div.task_done[style*=width:8px]", true, @output_buffer
694 assert_select "div.task_done[style*=width:8px]", true, @output_buffer
695 end
695 end
696
696
697 context "for completed issue" do
697 context "for completed issue" do
698 setup do
698 setup do
699 @issue.done_ratio = 100
699 @issue.done_ratio = 100
700 end
700 end
701
701
702 should "be the total width of the issue" do
702 should "be the total width of the issue" do
703 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
703 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
704 assert_select "div.task_done[style*=width:58px]", true, @output_buffer
704 assert_select "div.task_done[style*=width:58px]", true, @output_buffer
705 end
705 end
706
706
707 should "be the total width of the issue with due_date=start_date" do
707 should "be the total width of the issue with due_date=start_date" do
708 @issue.due_date = @issue.start_date
708 @issue.due_date = @issue.start_date
709 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
709 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
710 assert_select "div.task_done[style*=width:2px]", true, @output_buffer
710 assert_select "div.task_done[style*=width:2px]", true, @output_buffer
711 end
711 end
712 end
712 end
713 end
713 end
714
714
715 context "status content" do
715 context "status content" do
716 should "appear at the far left, even if it's far in the past" do
716 should "appear at the far left, even if it's far in the past" do
717 @gantt.instance_variable_set('@date_to', (today - 14))
717 @gantt.instance_variable_set('@date_to', (today - 14))
718 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
718 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
719 assert_select "div.task.label", true, @output_buffer
719 assert_select "div.task.label", true, @output_buffer
720 end
720 end
721
721
722 should "show the issue status" do
722 should "show the issue status" do
723 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
723 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
724 assert_select "div.task.label", /#{@issue.status.name}/
724 assert_select "div.task.label", /#{@issue.status.name}/
725 end
725 end
726
726
727 should "show the percent complete" do
727 should "show the percent complete" do
728 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
728 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
729 assert_select "div.task.label", /30%/
729 assert_select "div.task.label", /30%/
730 end
730 end
731 end
731 end
732 end
732 end
733
733
734 should "have an issue tooltip" do
734 should "have an issue tooltip" do
735 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
735 @output_buffer = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
736 assert_select "div.tooltip", /#{@issue.subject}/
736 assert_select "div.tooltip", /#{@issue.subject}/
737 end
737 end
738 should "test the PNG format"
738 should "test the PNG format"
739 should "test the PDF format"
739 should "test the PDF format"
740 end
740 end
741
741
742 context "#to_image" do
742 context "#to_image" do
743 should "be tested"
743 should "be tested"
744 end
744 end
745
745
746 context "#to_pdf" do
746 context "#to_pdf" do
747 should "be tested"
747 should "be tested"
748 end
748 end
749 end
749 end
@@ -1,1234 +1,1228
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ProjectTest < ActiveSupport::TestCase
20 class ProjectTest < ActiveSupport::TestCase
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :journals, :journal_details,
22 :journals, :journal_details,
23 :enumerations, :users, :issue_categories,
23 :enumerations, :users, :issue_categories,
24 :projects_trackers,
24 :projects_trackers,
25 :custom_fields,
25 :custom_fields,
26 :custom_fields_projects,
26 :custom_fields_projects,
27 :custom_fields_trackers,
27 :custom_fields_trackers,
28 :custom_values,
28 :custom_values,
29 :roles,
29 :roles,
30 :member_roles,
30 :member_roles,
31 :members,
31 :members,
32 :enabled_modules,
32 :enabled_modules,
33 :workflows,
33 :workflows,
34 :versions,
34 :versions,
35 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
35 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
36 :groups_users,
36 :groups_users,
37 :boards, :messages,
37 :boards, :messages,
38 :repositories,
38 :repositories,
39 :documents
39 :documents
40
40
41 def setup
41 def setup
42 @ecookbook = Project.find(1)
42 @ecookbook = Project.find(1)
43 @ecookbook_sub1 = Project.find(3)
43 @ecookbook_sub1 = Project.find(3)
44 set_tmp_attachments_directory
44 set_tmp_attachments_directory
45 User.current = nil
45 User.current = nil
46 end
46 end
47
47
48 def test_truth
48 def test_truth
49 assert_kind_of Project, @ecookbook
49 assert_kind_of Project, @ecookbook
50 assert_equal "eCookbook", @ecookbook.name
50 assert_equal "eCookbook", @ecookbook.name
51 end
51 end
52
52
53 def test_default_attributes
53 def test_default_attributes
54 with_settings :default_projects_public => '1' do
54 with_settings :default_projects_public => '1' do
55 assert_equal true, Project.new.is_public
55 assert_equal true, Project.new.is_public
56 assert_equal false, Project.new(:is_public => false).is_public
56 assert_equal false, Project.new(:is_public => false).is_public
57 end
57 end
58
58
59 with_settings :default_projects_public => '0' do
59 with_settings :default_projects_public => '0' do
60 assert_equal false, Project.new.is_public
60 assert_equal false, Project.new.is_public
61 assert_equal true, Project.new(:is_public => true).is_public
61 assert_equal true, Project.new(:is_public => true).is_public
62 end
62 end
63
63
64 with_settings :sequential_project_identifiers => '1' do
64 with_settings :sequential_project_identifiers => '1' do
65 assert !Project.new.identifier.blank?
65 assert !Project.new.identifier.blank?
66 assert Project.new(:identifier => '').identifier.blank?
66 assert Project.new(:identifier => '').identifier.blank?
67 end
67 end
68
68
69 with_settings :sequential_project_identifiers => '0' do
69 with_settings :sequential_project_identifiers => '0' do
70 assert Project.new.identifier.blank?
70 assert Project.new.identifier.blank?
71 assert !Project.new(:identifier => 'test').blank?
71 assert !Project.new(:identifier => 'test').blank?
72 end
72 end
73
73
74 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
74 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
75 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
75 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
76 end
76 end
77
77
78 assert_equal Tracker.all.sort, Project.new.trackers.sort
78 assert_equal Tracker.all.sort, Project.new.trackers.sort
79 assert_equal Tracker.find(1, 3).sort, Project.new(:tracker_ids => [1, 3]).trackers.sort
79 assert_equal Tracker.find(1, 3).sort, Project.new(:tracker_ids => [1, 3]).trackers.sort
80 end
80 end
81
81
82 def test_update
82 def test_update
83 assert_equal "eCookbook", @ecookbook.name
83 assert_equal "eCookbook", @ecookbook.name
84 @ecookbook.name = "eCook"
84 @ecookbook.name = "eCook"
85 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
85 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
86 @ecookbook.reload
86 @ecookbook.reload
87 assert_equal "eCook", @ecookbook.name
87 assert_equal "eCook", @ecookbook.name
88 end
88 end
89
89
90 def test_validate_identifier
90 def test_validate_identifier
91 to_test = {"abc" => true,
91 to_test = {"abc" => true,
92 "ab12" => true,
92 "ab12" => true,
93 "ab-12" => true,
93 "ab-12" => true,
94 "ab_12" => true,
94 "ab_12" => true,
95 "12" => false,
95 "12" => false,
96 "new" => false}
96 "new" => false}
97
97
98 to_test.each do |identifier, valid|
98 to_test.each do |identifier, valid|
99 p = Project.new
99 p = Project.new
100 p.identifier = identifier
100 p.identifier = identifier
101 p.valid?
101 p.valid?
102 if valid
102 if valid
103 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
103 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
104 else
104 else
105 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
105 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
106 end
106 end
107 end
107 end
108 end
108 end
109
109
110 def test_identifier_should_not_be_frozen_for_a_new_project
110 def test_identifier_should_not_be_frozen_for_a_new_project
111 assert_equal false, Project.new.identifier_frozen?
111 assert_equal false, Project.new.identifier_frozen?
112 end
112 end
113
113
114 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
114 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
115 Project.update_all(["identifier = ''"], "id = 1")
115 Project.update_all(["identifier = ''"], "id = 1")
116
116
117 assert_equal false, Project.find(1).identifier_frozen?
117 assert_equal false, Project.find(1).identifier_frozen?
118 end
118 end
119
119
120 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
120 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
121 assert_equal true, Project.find(1).identifier_frozen?
121 assert_equal true, Project.find(1).identifier_frozen?
122 end
122 end
123
123
124 def test_members_should_be_active_users
124 def test_members_should_be_active_users
125 Project.all.each do |project|
125 Project.all.each do |project|
126 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
126 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
127 end
127 end
128 end
128 end
129
129
130 def test_users_should_be_active_users
130 def test_users_should_be_active_users
131 Project.all.each do |project|
131 Project.all.each do |project|
132 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
132 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
133 end
133 end
134 end
134 end
135
135
136 def test_open_scope_on_issues_association
136 def test_open_scope_on_issues_association
137 assert_kind_of Issue, Project.find(1).issues.open.first
137 assert_kind_of Issue, Project.find(1).issues.open.first
138 end
138 end
139
139
140 def test_archive
140 def test_archive
141 user = @ecookbook.members.first.user
141 user = @ecookbook.members.first.user
142 @ecookbook.archive
142 @ecookbook.archive
143 @ecookbook.reload
143 @ecookbook.reload
144
144
145 assert !@ecookbook.active?
145 assert !@ecookbook.active?
146 assert @ecookbook.archived?
146 assert @ecookbook.archived?
147 assert !user.projects.include?(@ecookbook)
147 assert !user.projects.include?(@ecookbook)
148 # Subproject are also archived
148 # Subproject are also archived
149 assert !@ecookbook.children.empty?
149 assert !@ecookbook.children.empty?
150 assert @ecookbook.descendants.active.empty?
150 assert @ecookbook.descendants.active.empty?
151 end
151 end
152
152
153 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
153 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
154 # Assign an issue of a project to a version of a child project
154 # Assign an issue of a project to a version of a child project
155 Issue.find(4).update_attribute :fixed_version_id, 4
155 Issue.find(4).update_attribute :fixed_version_id, 4
156
156
157 assert_no_difference "Project.count(:all, :conditions => 'status = #{Project::STATUS_ARCHIVED}')" do
157 assert_no_difference "Project.count(:all, :conditions => 'status = #{Project::STATUS_ARCHIVED}')" do
158 assert_equal false, @ecookbook.archive
158 assert_equal false, @ecookbook.archive
159 end
159 end
160 @ecookbook.reload
160 @ecookbook.reload
161 assert @ecookbook.active?
161 assert @ecookbook.active?
162 end
162 end
163
163
164 def test_unarchive
164 def test_unarchive
165 user = @ecookbook.members.first.user
165 user = @ecookbook.members.first.user
166 @ecookbook.archive
166 @ecookbook.archive
167 # A subproject of an archived project can not be unarchived
167 # A subproject of an archived project can not be unarchived
168 assert !@ecookbook_sub1.unarchive
168 assert !@ecookbook_sub1.unarchive
169
169
170 # Unarchive project
170 # Unarchive project
171 assert @ecookbook.unarchive
171 assert @ecookbook.unarchive
172 @ecookbook.reload
172 @ecookbook.reload
173 assert @ecookbook.active?
173 assert @ecookbook.active?
174 assert !@ecookbook.archived?
174 assert !@ecookbook.archived?
175 assert user.projects.include?(@ecookbook)
175 assert user.projects.include?(@ecookbook)
176 # Subproject can now be unarchived
176 # Subproject can now be unarchived
177 @ecookbook_sub1.reload
177 @ecookbook_sub1.reload
178 assert @ecookbook_sub1.unarchive
178 assert @ecookbook_sub1.unarchive
179 end
179 end
180
180
181 def test_destroy
181 def test_destroy
182 # 2 active members
182 # 2 active members
183 assert_equal 2, @ecookbook.members.size
183 assert_equal 2, @ecookbook.members.size
184 # and 1 is locked
184 # and 1 is locked
185 assert_equal 3, Member.find(:all, :conditions => ['project_id = ?', @ecookbook.id]).size
185 assert_equal 3, Member.find(:all, :conditions => ['project_id = ?', @ecookbook.id]).size
186 # some boards
186 # some boards
187 assert @ecookbook.boards.any?
187 assert @ecookbook.boards.any?
188
188
189 @ecookbook.destroy
189 @ecookbook.destroy
190 # make sure that the project non longer exists
190 # make sure that the project non longer exists
191 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
191 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
192 # make sure related data was removed
192 # make sure related data was removed
193 assert_nil Member.first(:conditions => {:project_id => @ecookbook.id})
193 assert_nil Member.first(:conditions => {:project_id => @ecookbook.id})
194 assert_nil Board.first(:conditions => {:project_id => @ecookbook.id})
194 assert_nil Board.first(:conditions => {:project_id => @ecookbook.id})
195 assert_nil Issue.first(:conditions => {:project_id => @ecookbook.id})
195 assert_nil Issue.first(:conditions => {:project_id => @ecookbook.id})
196 end
196 end
197
197
198 def test_destroy_should_destroy_subtasks
198 def test_destroy_should_destroy_subtasks
199 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
199 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
200 issues[0].update_attribute :parent_issue_id, issues[1].id
200 issues[0].update_attribute :parent_issue_id, issues[1].id
201 issues[2].update_attribute :parent_issue_id, issues[1].id
201 issues[2].update_attribute :parent_issue_id, issues[1].id
202 assert_equal 2, issues[1].children.count
202 assert_equal 2, issues[1].children.count
203
203
204 assert_nothing_raised do
204 assert_nothing_raised do
205 Project.find(1).destroy
205 Project.find(1).destroy
206 end
206 end
207 assert Issue.find_all_by_id(issues.map(&:id)).empty?
207 assert Issue.find_all_by_id(issues.map(&:id)).empty?
208 end
208 end
209
209
210 def test_destroying_root_projects_should_clear_data
210 def test_destroying_root_projects_should_clear_data
211 Project.roots.each do |root|
211 Project.roots.each do |root|
212 root.destroy
212 root.destroy
213 end
213 end
214
214
215 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
215 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
216 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
216 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
217 assert_equal 0, MemberRole.count
217 assert_equal 0, MemberRole.count
218 assert_equal 0, Issue.count
218 assert_equal 0, Issue.count
219 assert_equal 0, Journal.count
219 assert_equal 0, Journal.count
220 assert_equal 0, JournalDetail.count
220 assert_equal 0, JournalDetail.count
221 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
221 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
222 assert_equal 0, EnabledModule.count
222 assert_equal 0, EnabledModule.count
223 assert_equal 0, IssueCategory.count
223 assert_equal 0, IssueCategory.count
224 assert_equal 0, IssueRelation.count
224 assert_equal 0, IssueRelation.count
225 assert_equal 0, Board.count
225 assert_equal 0, Board.count
226 assert_equal 0, Message.count
226 assert_equal 0, Message.count
227 assert_equal 0, News.count
227 assert_equal 0, News.count
228 assert_equal 0, Query.count(:conditions => "project_id IS NOT NULL")
228 assert_equal 0, Query.count(:conditions => "project_id IS NOT NULL")
229 assert_equal 0, Repository.count
229 assert_equal 0, Repository.count
230 assert_equal 0, Changeset.count
230 assert_equal 0, Changeset.count
231 assert_equal 0, Change.count
231 assert_equal 0, Change.count
232 assert_equal 0, Comment.count
232 assert_equal 0, Comment.count
233 assert_equal 0, TimeEntry.count
233 assert_equal 0, TimeEntry.count
234 assert_equal 0, Version.count
234 assert_equal 0, Version.count
235 assert_equal 0, Watcher.count
235 assert_equal 0, Watcher.count
236 assert_equal 0, Wiki.count
236 assert_equal 0, Wiki.count
237 assert_equal 0, WikiPage.count
237 assert_equal 0, WikiPage.count
238 assert_equal 0, WikiContent.count
238 assert_equal 0, WikiContent.count
239 assert_equal 0, WikiContent::Version.count
239 assert_equal 0, WikiContent::Version.count
240 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").size
240 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").size
241 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").size
241 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").size
242 assert_equal 0, CustomValue.count(:conditions => {:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']})
242 assert_equal 0, CustomValue.count(:conditions => {:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']})
243 end
243 end
244
244
245 def test_move_an_orphan_project_to_a_root_project
245 def test_move_an_orphan_project_to_a_root_project
246 sub = Project.find(2)
246 sub = Project.find(2)
247 sub.set_parent! @ecookbook
247 sub.set_parent! @ecookbook
248 assert_equal @ecookbook.id, sub.parent.id
248 assert_equal @ecookbook.id, sub.parent.id
249 @ecookbook.reload
249 @ecookbook.reload
250 assert_equal 4, @ecookbook.children.size
250 assert_equal 4, @ecookbook.children.size
251 end
251 end
252
252
253 def test_move_an_orphan_project_to_a_subproject
253 def test_move_an_orphan_project_to_a_subproject
254 sub = Project.find(2)
254 sub = Project.find(2)
255 assert sub.set_parent!(@ecookbook_sub1)
255 assert sub.set_parent!(@ecookbook_sub1)
256 end
256 end
257
257
258 def test_move_a_root_project_to_a_project
258 def test_move_a_root_project_to_a_project
259 sub = @ecookbook
259 sub = @ecookbook
260 assert sub.set_parent!(Project.find(2))
260 assert sub.set_parent!(Project.find(2))
261 end
261 end
262
262
263 def test_should_not_move_a_project_to_its_children
263 def test_should_not_move_a_project_to_its_children
264 sub = @ecookbook
264 sub = @ecookbook
265 assert !(sub.set_parent!(Project.find(3)))
265 assert !(sub.set_parent!(Project.find(3)))
266 end
266 end
267
267
268 def test_set_parent_should_add_roots_in_alphabetical_order
268 def test_set_parent_should_add_roots_in_alphabetical_order
269 ProjectCustomField.delete_all
269 ProjectCustomField.delete_all
270 Project.delete_all
270 Project.delete_all
271 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
271 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
272 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
272 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
273 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
273 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
274 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
274 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
275
275
276 assert_equal 4, Project.count
276 assert_equal 4, Project.count
277 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
277 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
278 end
278 end
279
279
280 def test_set_parent_should_add_children_in_alphabetical_order
280 def test_set_parent_should_add_children_in_alphabetical_order
281 ProjectCustomField.delete_all
281 ProjectCustomField.delete_all
282 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
282 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
283 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
283 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
284 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
284 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
285 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
285 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
286 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
286 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
287
287
288 parent.reload
288 parent.reload
289 assert_equal 4, parent.children.size
289 assert_equal 4, parent.children.size
290 assert_equal parent.children.all.sort_by(&:name), parent.children.all
290 assert_equal parent.children.all.sort_by(&:name), parent.children.all
291 end
291 end
292
292
293 def test_rebuild_should_sort_children_alphabetically
293 def test_rebuild_should_sort_children_alphabetically
294 ProjectCustomField.delete_all
294 ProjectCustomField.delete_all
295 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
295 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
296 Project.create!(:name => 'Project C', :identifier => 'project-c').move_to_child_of(parent)
296 Project.create!(:name => 'Project C', :identifier => 'project-c').move_to_child_of(parent)
297 Project.create!(:name => 'Project B', :identifier => 'project-b').move_to_child_of(parent)
297 Project.create!(:name => 'Project B', :identifier => 'project-b').move_to_child_of(parent)
298 Project.create!(:name => 'Project D', :identifier => 'project-d').move_to_child_of(parent)
298 Project.create!(:name => 'Project D', :identifier => 'project-d').move_to_child_of(parent)
299 Project.create!(:name => 'Project A', :identifier => 'project-a').move_to_child_of(parent)
299 Project.create!(:name => 'Project A', :identifier => 'project-a').move_to_child_of(parent)
300
300
301 Project.update_all("lft = NULL, rgt = NULL")
301 Project.update_all("lft = NULL, rgt = NULL")
302 Project.rebuild!
302 Project.rebuild!
303
303
304 parent.reload
304 parent.reload
305 assert_equal 4, parent.children.size
305 assert_equal 4, parent.children.size
306 assert_equal parent.children.all.sort_by(&:name), parent.children.all
306 assert_equal parent.children.all.sort_by(&:name), parent.children.all
307 end
307 end
308
308
309
309
310 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
310 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
311 # Parent issue with a hierarchy project's fixed version
311 # Parent issue with a hierarchy project's fixed version
312 parent_issue = Issue.find(1)
312 parent_issue = Issue.find(1)
313 parent_issue.update_attribute(:fixed_version_id, 4)
313 parent_issue.update_attribute(:fixed_version_id, 4)
314 parent_issue.reload
314 parent_issue.reload
315 assert_equal 4, parent_issue.fixed_version_id
315 assert_equal 4, parent_issue.fixed_version_id
316
316
317 # Should keep fixed versions for the issues
317 # Should keep fixed versions for the issues
318 issue_with_local_fixed_version = Issue.find(5)
318 issue_with_local_fixed_version = Issue.find(5)
319 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
319 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
320 issue_with_local_fixed_version.reload
320 issue_with_local_fixed_version.reload
321 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
321 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
322
322
323 # Local issue with hierarchy fixed_version
323 # Local issue with hierarchy fixed_version
324 issue_with_hierarchy_fixed_version = Issue.find(13)
324 issue_with_hierarchy_fixed_version = Issue.find(13)
325 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
325 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
326 issue_with_hierarchy_fixed_version.reload
326 issue_with_hierarchy_fixed_version.reload
327 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
327 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
328
328
329 # Move project out of the issue's hierarchy
329 # Move project out of the issue's hierarchy
330 moved_project = Project.find(3)
330 moved_project = Project.find(3)
331 moved_project.set_parent!(Project.find(2))
331 moved_project.set_parent!(Project.find(2))
332 parent_issue.reload
332 parent_issue.reload
333 issue_with_local_fixed_version.reload
333 issue_with_local_fixed_version.reload
334 issue_with_hierarchy_fixed_version.reload
334 issue_with_hierarchy_fixed_version.reload
335
335
336 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
336 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
337 assert_equal nil, issue_with_hierarchy_fixed_version.fixed_version_id, "Fixed version is still set after moving the Project out of the hierarchy where the version is defined in"
337 assert_equal nil, issue_with_hierarchy_fixed_version.fixed_version_id, "Fixed version is still set after moving the Project out of the hierarchy where the version is defined in"
338 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
338 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
339 end
339 end
340
340
341 def test_parent
341 def test_parent
342 p = Project.find(6).parent
342 p = Project.find(6).parent
343 assert p.is_a?(Project)
343 assert p.is_a?(Project)
344 assert_equal 5, p.id
344 assert_equal 5, p.id
345 end
345 end
346
346
347 def test_ancestors
347 def test_ancestors
348 a = Project.find(6).ancestors
348 a = Project.find(6).ancestors
349 assert a.first.is_a?(Project)
349 assert a.first.is_a?(Project)
350 assert_equal [1, 5], a.collect(&:id)
350 assert_equal [1, 5], a.collect(&:id)
351 end
351 end
352
352
353 def test_root
353 def test_root
354 r = Project.find(6).root
354 r = Project.find(6).root
355 assert r.is_a?(Project)
355 assert r.is_a?(Project)
356 assert_equal 1, r.id
356 assert_equal 1, r.id
357 end
357 end
358
358
359 def test_children
359 def test_children
360 c = Project.find(1).children
360 c = Project.find(1).children
361 assert c.first.is_a?(Project)
361 assert c.first.is_a?(Project)
362 assert_equal [5, 3, 4], c.collect(&:id)
362 assert_equal [5, 3, 4], c.collect(&:id)
363 end
363 end
364
364
365 def test_descendants
365 def test_descendants
366 d = Project.find(1).descendants
366 d = Project.find(1).descendants
367 assert d.first.is_a?(Project)
367 assert d.first.is_a?(Project)
368 assert_equal [5, 6, 3, 4], d.collect(&:id)
368 assert_equal [5, 6, 3, 4], d.collect(&:id)
369 end
369 end
370
370
371 def test_allowed_parents_should_be_empty_for_non_member_user
371 def test_allowed_parents_should_be_empty_for_non_member_user
372 Role.non_member.add_permission!(:add_project)
372 Role.non_member.add_permission!(:add_project)
373 user = User.find(9)
373 user = User.find(9)
374 assert user.memberships.empty?
374 assert user.memberships.empty?
375 User.current = user
375 User.current = user
376 assert Project.new.allowed_parents.compact.empty?
376 assert Project.new.allowed_parents.compact.empty?
377 end
377 end
378
378
379 def test_allowed_parents_with_add_subprojects_permission
379 def test_allowed_parents_with_add_subprojects_permission
380 Role.find(1).remove_permission!(:add_project)
380 Role.find(1).remove_permission!(:add_project)
381 Role.find(1).add_permission!(:add_subprojects)
381 Role.find(1).add_permission!(:add_subprojects)
382 User.current = User.find(2)
382 User.current = User.find(2)
383 # new project
383 # new project
384 assert !Project.new.allowed_parents.include?(nil)
384 assert !Project.new.allowed_parents.include?(nil)
385 assert Project.new.allowed_parents.include?(Project.find(1))
385 assert Project.new.allowed_parents.include?(Project.find(1))
386 # existing root project
386 # existing root project
387 assert Project.find(1).allowed_parents.include?(nil)
387 assert Project.find(1).allowed_parents.include?(nil)
388 # existing child
388 # existing child
389 assert Project.find(3).allowed_parents.include?(Project.find(1))
389 assert Project.find(3).allowed_parents.include?(Project.find(1))
390 assert !Project.find(3).allowed_parents.include?(nil)
390 assert !Project.find(3).allowed_parents.include?(nil)
391 end
391 end
392
392
393 def test_allowed_parents_with_add_project_permission
393 def test_allowed_parents_with_add_project_permission
394 Role.find(1).add_permission!(:add_project)
394 Role.find(1).add_permission!(:add_project)
395 Role.find(1).remove_permission!(:add_subprojects)
395 Role.find(1).remove_permission!(:add_subprojects)
396 User.current = User.find(2)
396 User.current = User.find(2)
397 # new project
397 # new project
398 assert Project.new.allowed_parents.include?(nil)
398 assert Project.new.allowed_parents.include?(nil)
399 assert !Project.new.allowed_parents.include?(Project.find(1))
399 assert !Project.new.allowed_parents.include?(Project.find(1))
400 # existing root project
400 # existing root project
401 assert Project.find(1).allowed_parents.include?(nil)
401 assert Project.find(1).allowed_parents.include?(nil)
402 # existing child
402 # existing child
403 assert Project.find(3).allowed_parents.include?(Project.find(1))
403 assert Project.find(3).allowed_parents.include?(Project.find(1))
404 assert Project.find(3).allowed_parents.include?(nil)
404 assert Project.find(3).allowed_parents.include?(nil)
405 end
405 end
406
406
407 def test_allowed_parents_with_add_project_and_subprojects_permission
407 def test_allowed_parents_with_add_project_and_subprojects_permission
408 Role.find(1).add_permission!(:add_project)
408 Role.find(1).add_permission!(:add_project)
409 Role.find(1).add_permission!(:add_subprojects)
409 Role.find(1).add_permission!(:add_subprojects)
410 User.current = User.find(2)
410 User.current = User.find(2)
411 # new project
411 # new project
412 assert Project.new.allowed_parents.include?(nil)
412 assert Project.new.allowed_parents.include?(nil)
413 assert Project.new.allowed_parents.include?(Project.find(1))
413 assert Project.new.allowed_parents.include?(Project.find(1))
414 # existing root project
414 # existing root project
415 assert Project.find(1).allowed_parents.include?(nil)
415 assert Project.find(1).allowed_parents.include?(nil)
416 # existing child
416 # existing child
417 assert Project.find(3).allowed_parents.include?(Project.find(1))
417 assert Project.find(3).allowed_parents.include?(Project.find(1))
418 assert Project.find(3).allowed_parents.include?(nil)
418 assert Project.find(3).allowed_parents.include?(nil)
419 end
419 end
420
420
421 def test_users_by_role
421 def test_users_by_role
422 users_by_role = Project.find(1).users_by_role
422 users_by_role = Project.find(1).users_by_role
423 assert_kind_of Hash, users_by_role
423 assert_kind_of Hash, users_by_role
424 role = Role.find(1)
424 role = Role.find(1)
425 assert_kind_of Array, users_by_role[role]
425 assert_kind_of Array, users_by_role[role]
426 assert users_by_role[role].include?(User.find(2))
426 assert users_by_role[role].include?(User.find(2))
427 end
427 end
428
428
429 def test_rolled_up_trackers
429 def test_rolled_up_trackers
430 parent = Project.find(1)
430 parent = Project.find(1)
431 parent.trackers = Tracker.find([1,2])
431 parent.trackers = Tracker.find([1,2])
432 child = parent.children.find(3)
432 child = parent.children.find(3)
433
433
434 assert_equal [1, 2], parent.tracker_ids
434 assert_equal [1, 2], parent.tracker_ids
435 assert_equal [2, 3], child.trackers.collect(&:id)
435 assert_equal [2, 3], child.trackers.collect(&:id)
436
436
437 assert_kind_of Tracker, parent.rolled_up_trackers.first
437 assert_kind_of Tracker, parent.rolled_up_trackers.first
438 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
438 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
439
439
440 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
440 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
441 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
441 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
442 end
442 end
443
443
444 def test_rolled_up_trackers_should_ignore_archived_subprojects
444 def test_rolled_up_trackers_should_ignore_archived_subprojects
445 parent = Project.find(1)
445 parent = Project.find(1)
446 parent.trackers = Tracker.find([1,2])
446 parent.trackers = Tracker.find([1,2])
447 child = parent.children.find(3)
447 child = parent.children.find(3)
448 child.trackers = Tracker.find([1,3])
448 child.trackers = Tracker.find([1,3])
449 parent.children.each(&:archive)
449 parent.children.each(&:archive)
450
450
451 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
451 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
452 end
452 end
453
453
454 context "#rolled_up_versions" do
454 context "#rolled_up_versions" do
455 setup do
455 setup do
456 @project = Project.generate!
456 @project = Project.generate!
457 @parent_version_1 = Version.generate!(:project => @project)
457 @parent_version_1 = Version.generate!(:project => @project)
458 @parent_version_2 = Version.generate!(:project => @project)
458 @parent_version_2 = Version.generate!(:project => @project)
459 end
459 end
460
460
461 should "include the versions for the current project" do
461 should "include the versions for the current project" do
462 assert_same_elements [@parent_version_1, @parent_version_2], @project.rolled_up_versions
462 assert_same_elements [@parent_version_1, @parent_version_2], @project.rolled_up_versions
463 end
463 end
464
464
465 should "include versions for a subproject" do
465 should "include versions for a subproject" do
466 @subproject = Project.generate!
466 @subproject = Project.generate!
467 @subproject.set_parent!(@project)
467 @subproject.set_parent!(@project)
468 @subproject_version = Version.generate!(:project => @subproject)
468 @subproject_version = Version.generate!(:project => @subproject)
469
469
470 assert_same_elements [
470 assert_same_elements [
471 @parent_version_1,
471 @parent_version_1,
472 @parent_version_2,
472 @parent_version_2,
473 @subproject_version
473 @subproject_version
474 ], @project.rolled_up_versions
474 ], @project.rolled_up_versions
475 end
475 end
476
476
477 should "include versions for a sub-subproject" do
477 should "include versions for a sub-subproject" do
478 @subproject = Project.generate!
478 @subproject = Project.generate!
479 @subproject.set_parent!(@project)
479 @subproject.set_parent!(@project)
480 @sub_subproject = Project.generate!
480 @sub_subproject = Project.generate!
481 @sub_subproject.set_parent!(@subproject)
481 @sub_subproject.set_parent!(@subproject)
482 @sub_subproject_version = Version.generate!(:project => @sub_subproject)
482 @sub_subproject_version = Version.generate!(:project => @sub_subproject)
483
483
484 @project.reload
484 @project.reload
485
485
486 assert_same_elements [
486 assert_same_elements [
487 @parent_version_1,
487 @parent_version_1,
488 @parent_version_2,
488 @parent_version_2,
489 @sub_subproject_version
489 @sub_subproject_version
490 ], @project.rolled_up_versions
490 ], @project.rolled_up_versions
491 end
491 end
492
492
493 should "only check active projects" do
493 should "only check active projects" do
494 @subproject = Project.generate!
494 @subproject = Project.generate!
495 @subproject.set_parent!(@project)
495 @subproject.set_parent!(@project)
496 @subproject_version = Version.generate!(:project => @subproject)
496 @subproject_version = Version.generate!(:project => @subproject)
497 assert @subproject.archive
497 assert @subproject.archive
498
498
499 @project.reload
499 @project.reload
500
500
501 assert !@subproject.active?
501 assert !@subproject.active?
502 assert_same_elements [@parent_version_1, @parent_version_2], @project.rolled_up_versions
502 assert_same_elements [@parent_version_1, @parent_version_2], @project.rolled_up_versions
503 end
503 end
504 end
504 end
505
505
506 def test_shared_versions_none_sharing
506 def test_shared_versions_none_sharing
507 p = Project.find(5)
507 p = Project.find(5)
508 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
508 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
509 assert p.shared_versions.include?(v)
509 assert p.shared_versions.include?(v)
510 assert !p.children.first.shared_versions.include?(v)
510 assert !p.children.first.shared_versions.include?(v)
511 assert !p.root.shared_versions.include?(v)
511 assert !p.root.shared_versions.include?(v)
512 assert !p.siblings.first.shared_versions.include?(v)
512 assert !p.siblings.first.shared_versions.include?(v)
513 assert !p.root.siblings.first.shared_versions.include?(v)
513 assert !p.root.siblings.first.shared_versions.include?(v)
514 end
514 end
515
515
516 def test_shared_versions_descendants_sharing
516 def test_shared_versions_descendants_sharing
517 p = Project.find(5)
517 p = Project.find(5)
518 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
518 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
519 assert p.shared_versions.include?(v)
519 assert p.shared_versions.include?(v)
520 assert p.children.first.shared_versions.include?(v)
520 assert p.children.first.shared_versions.include?(v)
521 assert !p.root.shared_versions.include?(v)
521 assert !p.root.shared_versions.include?(v)
522 assert !p.siblings.first.shared_versions.include?(v)
522 assert !p.siblings.first.shared_versions.include?(v)
523 assert !p.root.siblings.first.shared_versions.include?(v)
523 assert !p.root.siblings.first.shared_versions.include?(v)
524 end
524 end
525
525
526 def test_shared_versions_hierarchy_sharing
526 def test_shared_versions_hierarchy_sharing
527 p = Project.find(5)
527 p = Project.find(5)
528 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
528 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
529 assert p.shared_versions.include?(v)
529 assert p.shared_versions.include?(v)
530 assert p.children.first.shared_versions.include?(v)
530 assert p.children.first.shared_versions.include?(v)
531 assert p.root.shared_versions.include?(v)
531 assert p.root.shared_versions.include?(v)
532 assert !p.siblings.first.shared_versions.include?(v)
532 assert !p.siblings.first.shared_versions.include?(v)
533 assert !p.root.siblings.first.shared_versions.include?(v)
533 assert !p.root.siblings.first.shared_versions.include?(v)
534 end
534 end
535
535
536 def test_shared_versions_tree_sharing
536 def test_shared_versions_tree_sharing
537 p = Project.find(5)
537 p = Project.find(5)
538 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
538 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
539 assert p.shared_versions.include?(v)
539 assert p.shared_versions.include?(v)
540 assert p.children.first.shared_versions.include?(v)
540 assert p.children.first.shared_versions.include?(v)
541 assert p.root.shared_versions.include?(v)
541 assert p.root.shared_versions.include?(v)
542 assert p.siblings.first.shared_versions.include?(v)
542 assert p.siblings.first.shared_versions.include?(v)
543 assert !p.root.siblings.first.shared_versions.include?(v)
543 assert !p.root.siblings.first.shared_versions.include?(v)
544 end
544 end
545
545
546 def test_shared_versions_system_sharing
546 def test_shared_versions_system_sharing
547 p = Project.find(5)
547 p = Project.find(5)
548 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
548 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
549 assert p.shared_versions.include?(v)
549 assert p.shared_versions.include?(v)
550 assert p.children.first.shared_versions.include?(v)
550 assert p.children.first.shared_versions.include?(v)
551 assert p.root.shared_versions.include?(v)
551 assert p.root.shared_versions.include?(v)
552 assert p.siblings.first.shared_versions.include?(v)
552 assert p.siblings.first.shared_versions.include?(v)
553 assert p.root.siblings.first.shared_versions.include?(v)
553 assert p.root.siblings.first.shared_versions.include?(v)
554 end
554 end
555
555
556 def test_shared_versions
556 def test_shared_versions
557 parent = Project.find(1)
557 parent = Project.find(1)
558 child = parent.children.find(3)
558 child = parent.children.find(3)
559 private_child = parent.children.find(5)
559 private_child = parent.children.find(5)
560
560
561 assert_equal [1,2,3], parent.version_ids.sort
561 assert_equal [1,2,3], parent.version_ids.sort
562 assert_equal [4], child.version_ids
562 assert_equal [4], child.version_ids
563 assert_equal [6], private_child.version_ids
563 assert_equal [6], private_child.version_ids
564 assert_equal [7], Version.find_all_by_sharing('system').collect(&:id)
564 assert_equal [7], Version.find_all_by_sharing('system').collect(&:id)
565
565
566 assert_equal 6, parent.shared_versions.size
566 assert_equal 6, parent.shared_versions.size
567 parent.shared_versions.each do |version|
567 parent.shared_versions.each do |version|
568 assert_kind_of Version, version
568 assert_kind_of Version, version
569 end
569 end
570
570
571 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
571 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
572 end
572 end
573
573
574 def test_shared_versions_should_ignore_archived_subprojects
574 def test_shared_versions_should_ignore_archived_subprojects
575 parent = Project.find(1)
575 parent = Project.find(1)
576 child = parent.children.find(3)
576 child = parent.children.find(3)
577 child.archive
577 child.archive
578 parent.reload
578 parent.reload
579
579
580 assert_equal [1,2,3], parent.version_ids.sort
580 assert_equal [1,2,3], parent.version_ids.sort
581 assert_equal [4], child.version_ids
581 assert_equal [4], child.version_ids
582 assert !parent.shared_versions.collect(&:id).include?(4)
582 assert !parent.shared_versions.collect(&:id).include?(4)
583 end
583 end
584
584
585 def test_shared_versions_visible_to_user
585 def test_shared_versions_visible_to_user
586 user = User.find(3)
586 user = User.find(3)
587 parent = Project.find(1)
587 parent = Project.find(1)
588 child = parent.children.find(5)
588 child = parent.children.find(5)
589
589
590 assert_equal [1,2,3], parent.version_ids.sort
590 assert_equal [1,2,3], parent.version_ids.sort
591 assert_equal [6], child.version_ids
591 assert_equal [6], child.version_ids
592
592
593 versions = parent.shared_versions.visible(user)
593 versions = parent.shared_versions.visible(user)
594
594
595 assert_equal 4, versions.size
595 assert_equal 4, versions.size
596 versions.each do |version|
596 versions.each do |version|
597 assert_kind_of Version, version
597 assert_kind_of Version, version
598 end
598 end
599
599
600 assert !versions.collect(&:id).include?(6)
600 assert !versions.collect(&:id).include?(6)
601 end
601 end
602
602
603 def test_shared_versions_for_new_project_should_include_system_shared_versions
603 def test_shared_versions_for_new_project_should_include_system_shared_versions
604 p = Project.find(5)
604 p = Project.find(5)
605 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
605 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
606
606
607 assert_include v, Project.new.shared_versions
607 assert_include v, Project.new.shared_versions
608 end
608 end
609
609
610 def test_next_identifier
610 def test_next_identifier
611 ProjectCustomField.delete_all
611 ProjectCustomField.delete_all
612 Project.create!(:name => 'last', :identifier => 'p2008040')
612 Project.create!(:name => 'last', :identifier => 'p2008040')
613 assert_equal 'p2008041', Project.next_identifier
613 assert_equal 'p2008041', Project.next_identifier
614 end
614 end
615
615
616 def test_next_identifier_first_project
616 def test_next_identifier_first_project
617 Project.delete_all
617 Project.delete_all
618 assert_nil Project.next_identifier
618 assert_nil Project.next_identifier
619 end
619 end
620
620
621 def test_enabled_module_names
621 def test_enabled_module_names
622 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
622 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
623 project = Project.new
623 project = Project.new
624
624
625 project.enabled_module_names = %w(issue_tracking news)
625 project.enabled_module_names = %w(issue_tracking news)
626 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
626 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
627 end
627 end
628 end
628 end
629
629
630 context "enabled_modules" do
630 context "enabled_modules" do
631 setup do
631 setup do
632 @project = Project.find(1)
632 @project = Project.find(1)
633 end
633 end
634
634
635 should "define module by names and preserve ids" do
635 should "define module by names and preserve ids" do
636 # Remove one module
636 # Remove one module
637 modules = @project.enabled_modules.slice(0..-2)
637 modules = @project.enabled_modules.slice(0..-2)
638 assert modules.any?
638 assert modules.any?
639 assert_difference 'EnabledModule.count', -1 do
639 assert_difference 'EnabledModule.count', -1 do
640 @project.enabled_module_names = modules.collect(&:name)
640 @project.enabled_module_names = modules.collect(&:name)
641 end
641 end
642 @project.reload
642 @project.reload
643 # Ids should be preserved
643 # Ids should be preserved
644 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
644 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
645 end
645 end
646
646
647 should "enable a module" do
647 should "enable a module" do
648 @project.enabled_module_names = []
648 @project.enabled_module_names = []
649 @project.reload
649 @project.reload
650 assert_equal [], @project.enabled_module_names
650 assert_equal [], @project.enabled_module_names
651 #with string
651 #with string
652 @project.enable_module!("issue_tracking")
652 @project.enable_module!("issue_tracking")
653 assert_equal ["issue_tracking"], @project.enabled_module_names
653 assert_equal ["issue_tracking"], @project.enabled_module_names
654 #with symbol
654 #with symbol
655 @project.enable_module!(:gantt)
655 @project.enable_module!(:gantt)
656 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
656 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
657 #don't add a module twice
657 #don't add a module twice
658 @project.enable_module!("issue_tracking")
658 @project.enable_module!("issue_tracking")
659 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
659 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
660 end
660 end
661
661
662 should "disable a module" do
662 should "disable a module" do
663 #with string
663 #with string
664 assert @project.enabled_module_names.include?("issue_tracking")
664 assert @project.enabled_module_names.include?("issue_tracking")
665 @project.disable_module!("issue_tracking")
665 @project.disable_module!("issue_tracking")
666 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
666 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
667 #with symbol
667 #with symbol
668 assert @project.enabled_module_names.include?("gantt")
668 assert @project.enabled_module_names.include?("gantt")
669 @project.disable_module!(:gantt)
669 @project.disable_module!(:gantt)
670 assert ! @project.reload.enabled_module_names.include?("gantt")
670 assert ! @project.reload.enabled_module_names.include?("gantt")
671 #with EnabledModule object
671 #with EnabledModule object
672 first_module = @project.enabled_modules.first
672 first_module = @project.enabled_modules.first
673 @project.disable_module!(first_module)
673 @project.disable_module!(first_module)
674 assert ! @project.reload.enabled_module_names.include?(first_module.name)
674 assert ! @project.reload.enabled_module_names.include?(first_module.name)
675 end
675 end
676 end
676 end
677
677
678 def test_enabled_module_names_should_not_recreate_enabled_modules
678 def test_enabled_module_names_should_not_recreate_enabled_modules
679 project = Project.find(1)
679 project = Project.find(1)
680 # Remove one module
680 # Remove one module
681 modules = project.enabled_modules.slice(0..-2)
681 modules = project.enabled_modules.slice(0..-2)
682 assert modules.any?
682 assert modules.any?
683 assert_difference 'EnabledModule.count', -1 do
683 assert_difference 'EnabledModule.count', -1 do
684 project.enabled_module_names = modules.collect(&:name)
684 project.enabled_module_names = modules.collect(&:name)
685 end
685 end
686 project.reload
686 project.reload
687 # Ids should be preserved
687 # Ids should be preserved
688 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
688 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
689 end
689 end
690
690
691 def test_copy_from_existing_project
691 def test_copy_from_existing_project
692 source_project = Project.find(1)
692 source_project = Project.find(1)
693 copied_project = Project.copy_from(1)
693 copied_project = Project.copy_from(1)
694
694
695 assert copied_project
695 assert copied_project
696 # Cleared attributes
696 # Cleared attributes
697 assert copied_project.id.blank?
697 assert copied_project.id.blank?
698 assert copied_project.name.blank?
698 assert copied_project.name.blank?
699 assert copied_project.identifier.blank?
699 assert copied_project.identifier.blank?
700
700
701 # Duplicated attributes
701 # Duplicated attributes
702 assert_equal source_project.description, copied_project.description
702 assert_equal source_project.description, copied_project.description
703 assert_equal source_project.enabled_modules, copied_project.enabled_modules
703 assert_equal source_project.enabled_modules, copied_project.enabled_modules
704 assert_equal source_project.trackers, copied_project.trackers
704 assert_equal source_project.trackers, copied_project.trackers
705
705
706 # Default attributes
706 # Default attributes
707 assert_equal 1, copied_project.status
707 assert_equal 1, copied_project.status
708 end
708 end
709
709
710 def test_activities_should_use_the_system_activities
710 def test_activities_should_use_the_system_activities
711 project = Project.find(1)
711 project = Project.find(1)
712 assert_equal project.activities, TimeEntryActivity.find(:all, :conditions => {:active => true} )
712 assert_equal project.activities, TimeEntryActivity.find(:all, :conditions => {:active => true} )
713 end
713 end
714
714
715
715
716 def test_activities_should_use_the_project_specific_activities
716 def test_activities_should_use_the_project_specific_activities
717 project = Project.find(1)
717 project = Project.find(1)
718 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
718 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
719 assert overridden_activity.save!
719 assert overridden_activity.save!
720
720
721 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
721 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
722 end
722 end
723
723
724 def test_activities_should_not_include_the_inactive_project_specific_activities
724 def test_activities_should_not_include_the_inactive_project_specific_activities
725 project = Project.find(1)
725 project = Project.find(1)
726 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.find(:first), :active => false})
726 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.find(:first), :active => false})
727 assert overridden_activity.save!
727 assert overridden_activity.save!
728
728
729 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
729 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
730 end
730 end
731
731
732 def test_activities_should_not_include_project_specific_activities_from_other_projects
732 def test_activities_should_not_include_project_specific_activities_from_other_projects
733 project = Project.find(1)
733 project = Project.find(1)
734 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
734 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
735 assert overridden_activity.save!
735 assert overridden_activity.save!
736
736
737 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
737 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
738 end
738 end
739
739
740 def test_activities_should_handle_nils
740 def test_activities_should_handle_nils
741 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.find(:first)})
741 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.find(:first)})
742 TimeEntryActivity.delete_all
742 TimeEntryActivity.delete_all
743
743
744 # No activities
744 # No activities
745 project = Project.find(1)
745 project = Project.find(1)
746 assert project.activities.empty?
746 assert project.activities.empty?
747
747
748 # No system, one overridden
748 # No system, one overridden
749 assert overridden_activity.save!
749 assert overridden_activity.save!
750 project.reload
750 project.reload
751 assert_equal [overridden_activity], project.activities
751 assert_equal [overridden_activity], project.activities
752 end
752 end
753
753
754 def test_activities_should_override_system_activities_with_project_activities
754 def test_activities_should_override_system_activities_with_project_activities
755 project = Project.find(1)
755 project = Project.find(1)
756 parent_activity = TimeEntryActivity.find(:first)
756 parent_activity = TimeEntryActivity.find(:first)
757 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
757 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
758 assert overridden_activity.save!
758 assert overridden_activity.save!
759
759
760 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
760 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
761 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
761 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
762 end
762 end
763
763
764 def test_activities_should_include_inactive_activities_if_specified
764 def test_activities_should_include_inactive_activities_if_specified
765 project = Project.find(1)
765 project = Project.find(1)
766 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.find(:first), :active => false})
766 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.find(:first), :active => false})
767 assert overridden_activity.save!
767 assert overridden_activity.save!
768
768
769 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
769 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
770 end
770 end
771
771
772 test 'activities should not include active System activities if the project has an override that is inactive' do
772 test 'activities should not include active System activities if the project has an override that is inactive' do
773 project = Project.find(1)
773 project = Project.find(1)
774 system_activity = TimeEntryActivity.find_by_name('Design')
774 system_activity = TimeEntryActivity.find_by_name('Design')
775 assert system_activity.active?
775 assert system_activity.active?
776 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
776 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
777 assert overridden_activity.save!
777 assert overridden_activity.save!
778
778
779 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
779 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
780 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
780 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
781 end
781 end
782
782
783 def test_close_completed_versions
783 def test_close_completed_versions
784 Version.update_all("status = 'open'")
784 Version.update_all("status = 'open'")
785 project = Project.find(1)
785 project = Project.find(1)
786 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
786 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
787 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
787 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
788 project.close_completed_versions
788 project.close_completed_versions
789 project.reload
789 project.reload
790 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
790 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
791 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
791 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
792 end
792 end
793
793
794 context "Project#copy" do
794 context "Project#copy" do
795 setup do
795 setup do
796 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
796 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
797 Project.destroy_all :identifier => "copy-test"
797 Project.destroy_all :identifier => "copy-test"
798 @source_project = Project.find(2)
798 @source_project = Project.find(2)
799 @project = Project.new(:name => 'Copy Test', :identifier => 'copy-test')
799 @project = Project.new(:name => 'Copy Test', :identifier => 'copy-test')
800 @project.trackers = @source_project.trackers
800 @project.trackers = @source_project.trackers
801 @project.enabled_module_names = @source_project.enabled_modules.collect(&:name)
801 @project.enabled_module_names = @source_project.enabled_modules.collect(&:name)
802 end
802 end
803
803
804 should "copy issues" do
804 should "copy issues" do
805 @source_project.issues << Issue.generate!(:status => IssueStatus.find_by_name('Closed'),
805 @source_project.issues << Issue.generate!(:status => IssueStatus.find_by_name('Closed'),
806 :subject => "copy issue status",
806 :subject => "copy issue status",
807 :tracker_id => 1,
807 :tracker_id => 1,
808 :assigned_to_id => 2,
808 :assigned_to_id => 2,
809 :project_id => @source_project.id)
809 :project_id => @source_project.id)
810 assert @project.valid?
810 assert @project.valid?
811 assert @project.issues.empty?
811 assert @project.issues.empty?
812 assert @project.copy(@source_project)
812 assert @project.copy(@source_project)
813
813
814 assert_equal @source_project.issues.size, @project.issues.size
814 assert_equal @source_project.issues.size, @project.issues.size
815 @project.issues.each do |issue|
815 @project.issues.each do |issue|
816 assert issue.valid?
816 assert issue.valid?
817 assert ! issue.assigned_to.blank?
817 assert ! issue.assigned_to.blank?
818 assert_equal @project, issue.project
818 assert_equal @project, issue.project
819 end
819 end
820
820
821 copied_issue = @project.issues.first(:conditions => {:subject => "copy issue status"})
821 copied_issue = @project.issues.first(:conditions => {:subject => "copy issue status"})
822 assert copied_issue
822 assert copied_issue
823 assert copied_issue.status
823 assert copied_issue.status
824 assert_equal "Closed", copied_issue.status.name
824 assert_equal "Closed", copied_issue.status.name
825 end
825 end
826
826
827 should "copy issues assigned to a locked version" do
827 should "copy issues assigned to a locked version" do
828 User.current = User.find(1)
828 User.current = User.find(1)
829 assigned_version = Version.generate!(:name => "Assigned Issues")
829 assigned_version = Version.generate!(:name => "Assigned Issues")
830 @source_project.versions << assigned_version
830 @source_project.versions << assigned_version
831 Issue.generate_for_project!(@source_project,
831 Issue.generate!(:project => @source_project,
832 :fixed_version_id => assigned_version.id,
832 :fixed_version_id => assigned_version.id,
833 :subject => "copy issues assigned to a locked version",
833 :subject => "copy issues assigned to a locked version")
834 :tracker_id => 1,
835 :project_id => @source_project.id)
836 assigned_version.update_attribute :status, 'locked'
834 assigned_version.update_attribute :status, 'locked'
837
835
838 assert @project.copy(@source_project)
836 assert @project.copy(@source_project)
839 @project.reload
837 @project.reload
840 copied_issue = @project.issues.first(:conditions => {:subject => "copy issues assigned to a locked version"})
838 copied_issue = @project.issues.first(:conditions => {:subject => "copy issues assigned to a locked version"})
841
839
842 assert copied_issue
840 assert copied_issue
843 assert copied_issue.fixed_version
841 assert copied_issue.fixed_version
844 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
842 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
845 assert_equal 'locked', copied_issue.fixed_version.status
843 assert_equal 'locked', copied_issue.fixed_version.status
846 end
844 end
847
845
848 should "change the new issues to use the copied version" do
846 should "change the new issues to use the copied version" do
849 User.current = User.find(1)
847 User.current = User.find(1)
850 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open')
848 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open')
851 @source_project.versions << assigned_version
849 @source_project.versions << assigned_version
852 assert_equal 3, @source_project.versions.size
850 assert_equal 3, @source_project.versions.size
853 Issue.generate_for_project!(@source_project,
851 Issue.generate!(:project => @source_project,
854 :fixed_version_id => assigned_version.id,
852 :fixed_version_id => assigned_version.id,
855 :subject => "change the new issues to use the copied version",
853 :subject => "change the new issues to use the copied version")
856 :tracker_id => 1,
857 :project_id => @source_project.id)
858
854
859 assert @project.copy(@source_project)
855 assert @project.copy(@source_project)
860 @project.reload
856 @project.reload
861 copied_issue = @project.issues.first(:conditions => {:subject => "change the new issues to use the copied version"})
857 copied_issue = @project.issues.first(:conditions => {:subject => "change the new issues to use the copied version"})
862
858
863 assert copied_issue
859 assert copied_issue
864 assert copied_issue.fixed_version
860 assert copied_issue.fixed_version
865 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
861 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
866 assert_not_equal assigned_version.id, copied_issue.fixed_version.id # Different record
862 assert_not_equal assigned_version.id, copied_issue.fixed_version.id # Different record
867 end
863 end
868
864
869 should "keep target shared versions from other project" do
865 should "keep target shared versions from other project" do
870 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open', :project_id => 1, :sharing => 'system')
866 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open', :project_id => 1, :sharing => 'system')
871 issue = Issue.generate_for_project!(@source_project,
867 issue = Issue.generate!(:project => @source_project,
872 :fixed_version => assigned_version,
868 :fixed_version => assigned_version,
873 :subject => "keep target shared versions",
869 :subject => "keep target shared versions")
874 :tracker_id => 1,
875 :project_id => @source_project.id)
876
870
877 assert @project.copy(@source_project)
871 assert @project.copy(@source_project)
878 @project.reload
872 @project.reload
879 copied_issue = @project.issues.first(:conditions => {:subject => "keep target shared versions"})
873 copied_issue = @project.issues.first(:conditions => {:subject => "keep target shared versions"})
880
874
881 assert copied_issue
875 assert copied_issue
882 assert_equal assigned_version, copied_issue.fixed_version
876 assert_equal assigned_version, copied_issue.fixed_version
883 end
877 end
884
878
885 should "copy issue relations" do
879 should "copy issue relations" do
886 Setting.cross_project_issue_relations = '1'
880 Setting.cross_project_issue_relations = '1'
887
881
888 second_issue = Issue.generate!(:status_id => 5,
882 second_issue = Issue.generate!(:status_id => 5,
889 :subject => "copy issue relation",
883 :subject => "copy issue relation",
890 :tracker_id => 1,
884 :tracker_id => 1,
891 :assigned_to_id => 2,
885 :assigned_to_id => 2,
892 :project_id => @source_project.id)
886 :project_id => @source_project.id)
893 source_relation = IssueRelation.create!(:issue_from => Issue.find(4),
887 source_relation = IssueRelation.create!(:issue_from => Issue.find(4),
894 :issue_to => second_issue,
888 :issue_to => second_issue,
895 :relation_type => "relates")
889 :relation_type => "relates")
896 source_relation_cross_project = IssueRelation.create!(:issue_from => Issue.find(1),
890 source_relation_cross_project = IssueRelation.create!(:issue_from => Issue.find(1),
897 :issue_to => second_issue,
891 :issue_to => second_issue,
898 :relation_type => "duplicates")
892 :relation_type => "duplicates")
899
893
900 assert @project.copy(@source_project)
894 assert @project.copy(@source_project)
901 assert_equal @source_project.issues.count, @project.issues.count
895 assert_equal @source_project.issues.count, @project.issues.count
902 copied_issue = @project.issues.find_by_subject("Issue on project 2") # Was #4
896 copied_issue = @project.issues.find_by_subject("Issue on project 2") # Was #4
903 copied_second_issue = @project.issues.find_by_subject("copy issue relation")
897 copied_second_issue = @project.issues.find_by_subject("copy issue relation")
904
898
905 # First issue with a relation on project
899 # First issue with a relation on project
906 assert_equal 1, copied_issue.relations.size, "Relation not copied"
900 assert_equal 1, copied_issue.relations.size, "Relation not copied"
907 copied_relation = copied_issue.relations.first
901 copied_relation = copied_issue.relations.first
908 assert_equal "relates", copied_relation.relation_type
902 assert_equal "relates", copied_relation.relation_type
909 assert_equal copied_second_issue.id, copied_relation.issue_to_id
903 assert_equal copied_second_issue.id, copied_relation.issue_to_id
910 assert_not_equal source_relation.id, copied_relation.id
904 assert_not_equal source_relation.id, copied_relation.id
911
905
912 # Second issue with a cross project relation
906 # Second issue with a cross project relation
913 assert_equal 2, copied_second_issue.relations.size, "Relation not copied"
907 assert_equal 2, copied_second_issue.relations.size, "Relation not copied"
914 copied_relation = copied_second_issue.relations.select {|r| r.relation_type == 'duplicates'}.first
908 copied_relation = copied_second_issue.relations.select {|r| r.relation_type == 'duplicates'}.first
915 assert_equal "duplicates", copied_relation.relation_type
909 assert_equal "duplicates", copied_relation.relation_type
916 assert_equal 1, copied_relation.issue_from_id, "Cross project relation not kept"
910 assert_equal 1, copied_relation.issue_from_id, "Cross project relation not kept"
917 assert_not_equal source_relation_cross_project.id, copied_relation.id
911 assert_not_equal source_relation_cross_project.id, copied_relation.id
918 end
912 end
919
913
920 should "copy issue attachments" do
914 should "copy issue attachments" do
921 issue = Issue.generate!(:subject => "copy with attachment", :tracker_id => 1, :project_id => @source_project.id)
915 issue = Issue.generate!(:subject => "copy with attachment", :tracker_id => 1, :project_id => @source_project.id)
922 Attachment.create!(:container => issue, :file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 1)
916 Attachment.create!(:container => issue, :file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 1)
923 @source_project.issues << issue
917 @source_project.issues << issue
924 assert @project.copy(@source_project)
918 assert @project.copy(@source_project)
925
919
926 copied_issue = @project.issues.first(:conditions => {:subject => "copy with attachment"})
920 copied_issue = @project.issues.first(:conditions => {:subject => "copy with attachment"})
927 assert_not_nil copied_issue
921 assert_not_nil copied_issue
928 assert_equal 1, copied_issue.attachments.count, "Attachment not copied"
922 assert_equal 1, copied_issue.attachments.count, "Attachment not copied"
929 assert_equal "testfile.txt", copied_issue.attachments.first.filename
923 assert_equal "testfile.txt", copied_issue.attachments.first.filename
930 end
924 end
931
925
932 should "copy memberships" do
926 should "copy memberships" do
933 assert @project.valid?
927 assert @project.valid?
934 assert @project.members.empty?
928 assert @project.members.empty?
935 assert @project.copy(@source_project)
929 assert @project.copy(@source_project)
936
930
937 assert_equal @source_project.memberships.size, @project.memberships.size
931 assert_equal @source_project.memberships.size, @project.memberships.size
938 @project.memberships.each do |membership|
932 @project.memberships.each do |membership|
939 assert membership
933 assert membership
940 assert_equal @project, membership.project
934 assert_equal @project, membership.project
941 end
935 end
942 end
936 end
943
937
944 should "copy memberships with groups and additional roles" do
938 should "copy memberships with groups and additional roles" do
945 group = Group.create!(:lastname => "Copy group")
939 group = Group.create!(:lastname => "Copy group")
946 user = User.find(7)
940 user = User.find(7)
947 group.users << user
941 group.users << user
948 # group role
942 # group role
949 Member.create!(:project_id => @source_project.id, :principal => group, :role_ids => [2])
943 Member.create!(:project_id => @source_project.id, :principal => group, :role_ids => [2])
950 member = Member.find_by_user_id_and_project_id(user.id, @source_project.id)
944 member = Member.find_by_user_id_and_project_id(user.id, @source_project.id)
951 # additional role
945 # additional role
952 member.role_ids = [1]
946 member.role_ids = [1]
953
947
954 assert @project.copy(@source_project)
948 assert @project.copy(@source_project)
955 member = Member.find_by_user_id_and_project_id(user.id, @project.id)
949 member = Member.find_by_user_id_and_project_id(user.id, @project.id)
956 assert_not_nil member
950 assert_not_nil member
957 assert_equal [1, 2], member.role_ids.sort
951 assert_equal [1, 2], member.role_ids.sort
958 end
952 end
959
953
960 should "copy project specific queries" do
954 should "copy project specific queries" do
961 assert @project.valid?
955 assert @project.valid?
962 assert @project.queries.empty?
956 assert @project.queries.empty?
963 assert @project.copy(@source_project)
957 assert @project.copy(@source_project)
964
958
965 assert_equal @source_project.queries.size, @project.queries.size
959 assert_equal @source_project.queries.size, @project.queries.size
966 @project.queries.each do |query|
960 @project.queries.each do |query|
967 assert query
961 assert query
968 assert_equal @project, query.project
962 assert_equal @project, query.project
969 end
963 end
970 assert_equal @source_project.queries.map(&:user_id).sort, @project.queries.map(&:user_id).sort
964 assert_equal @source_project.queries.map(&:user_id).sort, @project.queries.map(&:user_id).sort
971 end
965 end
972
966
973 should "copy versions" do
967 should "copy versions" do
974 @source_project.versions << Version.generate!
968 @source_project.versions << Version.generate!
975 @source_project.versions << Version.generate!
969 @source_project.versions << Version.generate!
976
970
977 assert @project.versions.empty?
971 assert @project.versions.empty?
978 assert @project.copy(@source_project)
972 assert @project.copy(@source_project)
979
973
980 assert_equal @source_project.versions.size, @project.versions.size
974 assert_equal @source_project.versions.size, @project.versions.size
981 @project.versions.each do |version|
975 @project.versions.each do |version|
982 assert version
976 assert version
983 assert_equal @project, version.project
977 assert_equal @project, version.project
984 end
978 end
985 end
979 end
986
980
987 should "copy wiki" do
981 should "copy wiki" do
988 assert_difference 'Wiki.count' do
982 assert_difference 'Wiki.count' do
989 assert @project.copy(@source_project)
983 assert @project.copy(@source_project)
990 end
984 end
991
985
992 assert @project.wiki
986 assert @project.wiki
993 assert_not_equal @source_project.wiki, @project.wiki
987 assert_not_equal @source_project.wiki, @project.wiki
994 assert_equal "Start page", @project.wiki.start_page
988 assert_equal "Start page", @project.wiki.start_page
995 end
989 end
996
990
997 should "copy wiki pages and content with hierarchy" do
991 should "copy wiki pages and content with hierarchy" do
998 assert_difference 'WikiPage.count', @source_project.wiki.pages.size do
992 assert_difference 'WikiPage.count', @source_project.wiki.pages.size do
999 assert @project.copy(@source_project)
993 assert @project.copy(@source_project)
1000 end
994 end
1001
995
1002 assert @project.wiki
996 assert @project.wiki
1003 assert_equal @source_project.wiki.pages.size, @project.wiki.pages.size
997 assert_equal @source_project.wiki.pages.size, @project.wiki.pages.size
1004
998
1005 @project.wiki.pages.each do |wiki_page|
999 @project.wiki.pages.each do |wiki_page|
1006 assert wiki_page.content
1000 assert wiki_page.content
1007 assert !@source_project.wiki.pages.include?(wiki_page)
1001 assert !@source_project.wiki.pages.include?(wiki_page)
1008 end
1002 end
1009
1003
1010 parent = @project.wiki.find_page('Parent_page')
1004 parent = @project.wiki.find_page('Parent_page')
1011 child1 = @project.wiki.find_page('Child_page_1')
1005 child1 = @project.wiki.find_page('Child_page_1')
1012 child2 = @project.wiki.find_page('Child_page_2')
1006 child2 = @project.wiki.find_page('Child_page_2')
1013 assert_equal parent, child1.parent
1007 assert_equal parent, child1.parent
1014 assert_equal parent, child2.parent
1008 assert_equal parent, child2.parent
1015 end
1009 end
1016
1010
1017 should "copy issue categories" do
1011 should "copy issue categories" do
1018 assert @project.copy(@source_project)
1012 assert @project.copy(@source_project)
1019
1013
1020 assert_equal 2, @project.issue_categories.size
1014 assert_equal 2, @project.issue_categories.size
1021 @project.issue_categories.each do |issue_category|
1015 @project.issue_categories.each do |issue_category|
1022 assert !@source_project.issue_categories.include?(issue_category)
1016 assert !@source_project.issue_categories.include?(issue_category)
1023 end
1017 end
1024 end
1018 end
1025
1019
1026 should "copy boards" do
1020 should "copy boards" do
1027 assert @project.copy(@source_project)
1021 assert @project.copy(@source_project)
1028
1022
1029 assert_equal 1, @project.boards.size
1023 assert_equal 1, @project.boards.size
1030 @project.boards.each do |board|
1024 @project.boards.each do |board|
1031 assert !@source_project.boards.include?(board)
1025 assert !@source_project.boards.include?(board)
1032 end
1026 end
1033 end
1027 end
1034
1028
1035 should "change the new issues to use the copied issue categories" do
1029 should "change the new issues to use the copied issue categories" do
1036 issue = Issue.find(4)
1030 issue = Issue.find(4)
1037 issue.update_attribute(:category_id, 3)
1031 issue.update_attribute(:category_id, 3)
1038
1032
1039 assert @project.copy(@source_project)
1033 assert @project.copy(@source_project)
1040
1034
1041 @project.issues.each do |issue|
1035 @project.issues.each do |issue|
1042 assert issue.category
1036 assert issue.category
1043 assert_equal "Stock management", issue.category.name # Same name
1037 assert_equal "Stock management", issue.category.name # Same name
1044 assert_not_equal IssueCategory.find(3), issue.category # Different record
1038 assert_not_equal IssueCategory.find(3), issue.category # Different record
1045 end
1039 end
1046 end
1040 end
1047
1041
1048 should "limit copy with :only option" do
1042 should "limit copy with :only option" do
1049 assert @project.members.empty?
1043 assert @project.members.empty?
1050 assert @project.issue_categories.empty?
1044 assert @project.issue_categories.empty?
1051 assert @source_project.issues.any?
1045 assert @source_project.issues.any?
1052
1046
1053 assert @project.copy(@source_project, :only => ['members', 'issue_categories'])
1047 assert @project.copy(@source_project, :only => ['members', 'issue_categories'])
1054
1048
1055 assert @project.members.any?
1049 assert @project.members.any?
1056 assert @project.issue_categories.any?
1050 assert @project.issue_categories.any?
1057 assert @project.issues.empty?
1051 assert @project.issues.empty?
1058 end
1052 end
1059 end
1053 end
1060
1054
1061 def test_copy_should_copy_subtasks
1055 def test_copy_should_copy_subtasks
1062 source = Project.generate!(:tracker_ids => [1])
1056 source = Project.generate!(:tracker_ids => [1])
1063 issue = Issue.generate_with_descendants!(source, :subject => 'Parent')
1057 issue = Issue.generate_with_descendants!(source, :subject => 'Parent')
1064 project = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1])
1058 project = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1])
1065
1059
1066 assert_difference 'Project.count' do
1060 assert_difference 'Project.count' do
1067 assert_difference 'Issue.count', 1+issue.descendants.count do
1061 assert_difference 'Issue.count', 1+issue.descendants.count do
1068 assert project.copy(source.reload)
1062 assert project.copy(source.reload)
1069 end
1063 end
1070 end
1064 end
1071 copy = Issue.where(:parent_id => nil).order("id DESC").first
1065 copy = Issue.where(:parent_id => nil).order("id DESC").first
1072 assert_equal project, copy.project
1066 assert_equal project, copy.project
1073 assert_equal issue.descendants.count, copy.descendants.count
1067 assert_equal issue.descendants.count, copy.descendants.count
1074 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1068 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1075 assert child_copy.descendants.any?
1069 assert child_copy.descendants.any?
1076 end
1070 end
1077
1071
1078 context "#start_date" do
1072 context "#start_date" do
1079 setup do
1073 setup do
1080 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1074 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1081 @project = Project.generate!(:identifier => 'test0')
1075 @project = Project.generate!(:identifier => 'test0')
1082 @project.trackers << Tracker.generate!
1076 @project.trackers << Tracker.generate!
1083 end
1077 end
1084
1078
1085 should "be nil if there are no issues on the project" do
1079 should "be nil if there are no issues on the project" do
1086 assert_nil @project.start_date
1080 assert_nil @project.start_date
1087 end
1081 end
1088
1082
1089 should "be tested when issues have no start date"
1083 should "be tested when issues have no start date"
1090
1084
1091 should "be the earliest start date of it's issues" do
1085 should "be the earliest start date of it's issues" do
1092 early = 7.days.ago.to_date
1086 early = 7.days.ago.to_date
1093 Issue.generate_for_project!(@project, :start_date => Date.today)
1087 Issue.generate!(:project => @project, :start_date => Date.today)
1094 Issue.generate_for_project!(@project, :start_date => early)
1088 Issue.generate!(:project => @project, :start_date => early)
1095
1089
1096 assert_equal early, @project.start_date
1090 assert_equal early, @project.start_date
1097 end
1091 end
1098
1092
1099 end
1093 end
1100
1094
1101 context "#due_date" do
1095 context "#due_date" do
1102 setup do
1096 setup do
1103 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1097 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1104 @project = Project.generate!(:identifier => 'test0')
1098 @project = Project.generate!(:identifier => 'test0')
1105 @project.trackers << Tracker.generate!
1099 @project.trackers << Tracker.generate!
1106 end
1100 end
1107
1101
1108 should "be nil if there are no issues on the project" do
1102 should "be nil if there are no issues on the project" do
1109 assert_nil @project.due_date
1103 assert_nil @project.due_date
1110 end
1104 end
1111
1105
1112 should "be tested when issues have no due date"
1106 should "be tested when issues have no due date"
1113
1107
1114 should "be the latest due date of it's issues" do
1108 should "be the latest due date of it's issues" do
1115 future = 7.days.from_now.to_date
1109 future = 7.days.from_now.to_date
1116 Issue.generate_for_project!(@project, :due_date => future)
1110 Issue.generate!(:project => @project, :due_date => future)
1117 Issue.generate_for_project!(@project, :due_date => Date.today)
1111 Issue.generate!(:project => @project, :due_date => Date.today)
1118
1112
1119 assert_equal future, @project.due_date
1113 assert_equal future, @project.due_date
1120 end
1114 end
1121
1115
1122 should "be the latest due date of it's versions" do
1116 should "be the latest due date of it's versions" do
1123 future = 7.days.from_now.to_date
1117 future = 7.days.from_now.to_date
1124 @project.versions << Version.generate!(:effective_date => future)
1118 @project.versions << Version.generate!(:effective_date => future)
1125 @project.versions << Version.generate!(:effective_date => Date.today)
1119 @project.versions << Version.generate!(:effective_date => Date.today)
1126
1120
1127
1121
1128 assert_equal future, @project.due_date
1122 assert_equal future, @project.due_date
1129
1123
1130 end
1124 end
1131
1125
1132 should "pick the latest date from it's issues and versions" do
1126 should "pick the latest date from it's issues and versions" do
1133 future = 7.days.from_now.to_date
1127 future = 7.days.from_now.to_date
1134 far_future = 14.days.from_now.to_date
1128 far_future = 14.days.from_now.to_date
1135 Issue.generate_for_project!(@project, :due_date => far_future)
1129 Issue.generate!(:project => @project, :due_date => far_future)
1136 @project.versions << Version.generate!(:effective_date => future)
1130 @project.versions << Version.generate!(:effective_date => future)
1137
1131
1138 assert_equal far_future, @project.due_date
1132 assert_equal far_future, @project.due_date
1139 end
1133 end
1140
1134
1141 end
1135 end
1142
1136
1143 context "Project#completed_percent" do
1137 context "Project#completed_percent" do
1144 setup do
1138 setup do
1145 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1139 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1146 @project = Project.generate!(:identifier => 'test0')
1140 @project = Project.generate!(:identifier => 'test0')
1147 @project.trackers << Tracker.generate!
1141 @project.trackers << Tracker.generate!
1148 end
1142 end
1149
1143
1150 context "no versions" do
1144 context "no versions" do
1151 should "be 100" do
1145 should "be 100" do
1152 assert_equal 100, @project.completed_percent
1146 assert_equal 100, @project.completed_percent
1153 end
1147 end
1154 end
1148 end
1155
1149
1156 context "with versions" do
1150 context "with versions" do
1157 should "return 0 if the versions have no issues" do
1151 should "return 0 if the versions have no issues" do
1158 Version.generate!(:project => @project)
1152 Version.generate!(:project => @project)
1159 Version.generate!(:project => @project)
1153 Version.generate!(:project => @project)
1160
1154
1161 assert_equal 0, @project.completed_percent
1155 assert_equal 0, @project.completed_percent
1162 end
1156 end
1163
1157
1164 should "return 100 if the version has only closed issues" do
1158 should "return 100 if the version has only closed issues" do
1165 v1 = Version.generate!(:project => @project)
1159 v1 = Version.generate!(:project => @project)
1166 Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
1160 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
1167 v2 = Version.generate!(:project => @project)
1161 v2 = Version.generate!(:project => @project)
1168 Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
1162 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
1169
1163
1170 assert_equal 100, @project.completed_percent
1164 assert_equal 100, @project.completed_percent
1171 end
1165 end
1172
1166
1173 should "return the averaged completed percent of the versions (not weighted)" do
1167 should "return the averaged completed percent of the versions (not weighted)" do
1174 v1 = Version.generate!(:project => @project)
1168 v1 = Version.generate!(:project => @project)
1175 Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
1169 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
1176 v2 = Version.generate!(:project => @project)
1170 v2 = Version.generate!(:project => @project)
1177 Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
1171 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
1178
1172
1179 assert_equal 50, @project.completed_percent
1173 assert_equal 50, @project.completed_percent
1180 end
1174 end
1181
1175
1182 end
1176 end
1183 end
1177 end
1184
1178
1185 context "#notified_users" do
1179 context "#notified_users" do
1186 setup do
1180 setup do
1187 @project = Project.generate!
1181 @project = Project.generate!
1188 @role = Role.generate!
1182 @role = Role.generate!
1189
1183
1190 @user_with_membership_notification = User.generate!(:mail_notification => 'selected')
1184 @user_with_membership_notification = User.generate!(:mail_notification => 'selected')
1191 Member.create!(:project => @project, :roles => [@role], :principal => @user_with_membership_notification, :mail_notification => true)
1185 Member.create!(:project => @project, :roles => [@role], :principal => @user_with_membership_notification, :mail_notification => true)
1192
1186
1193 @all_events_user = User.generate!(:mail_notification => 'all')
1187 @all_events_user = User.generate!(:mail_notification => 'all')
1194 Member.create!(:project => @project, :roles => [@role], :principal => @all_events_user)
1188 Member.create!(:project => @project, :roles => [@role], :principal => @all_events_user)
1195
1189
1196 @no_events_user = User.generate!(:mail_notification => 'none')
1190 @no_events_user = User.generate!(:mail_notification => 'none')
1197 Member.create!(:project => @project, :roles => [@role], :principal => @no_events_user)
1191 Member.create!(:project => @project, :roles => [@role], :principal => @no_events_user)
1198
1192
1199 @only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
1193 @only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
1200 Member.create!(:project => @project, :roles => [@role], :principal => @only_my_events_user)
1194 Member.create!(:project => @project, :roles => [@role], :principal => @only_my_events_user)
1201
1195
1202 @only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
1196 @only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
1203 Member.create!(:project => @project, :roles => [@role], :principal => @only_assigned_user)
1197 Member.create!(:project => @project, :roles => [@role], :principal => @only_assigned_user)
1204
1198
1205 @only_owned_user = User.generate!(:mail_notification => 'only_owner')
1199 @only_owned_user = User.generate!(:mail_notification => 'only_owner')
1206 Member.create!(:project => @project, :roles => [@role], :principal => @only_owned_user)
1200 Member.create!(:project => @project, :roles => [@role], :principal => @only_owned_user)
1207 end
1201 end
1208
1202
1209 should "include members with a mail notification" do
1203 should "include members with a mail notification" do
1210 assert @project.notified_users.include?(@user_with_membership_notification)
1204 assert @project.notified_users.include?(@user_with_membership_notification)
1211 end
1205 end
1212
1206
1213 should "include users with the 'all' notification option" do
1207 should "include users with the 'all' notification option" do
1214 assert @project.notified_users.include?(@all_events_user)
1208 assert @project.notified_users.include?(@all_events_user)
1215 end
1209 end
1216
1210
1217 should "not include users with the 'none' notification option" do
1211 should "not include users with the 'none' notification option" do
1218 assert !@project.notified_users.include?(@no_events_user)
1212 assert !@project.notified_users.include?(@no_events_user)
1219 end
1213 end
1220
1214
1221 should "not include users with the 'only_my_events' notification option" do
1215 should "not include users with the 'only_my_events' notification option" do
1222 assert !@project.notified_users.include?(@only_my_events_user)
1216 assert !@project.notified_users.include?(@only_my_events_user)
1223 end
1217 end
1224
1218
1225 should "not include users with the 'only_assigned' notification option" do
1219 should "not include users with the 'only_assigned' notification option" do
1226 assert !@project.notified_users.include?(@only_assigned_user)
1220 assert !@project.notified_users.include?(@only_assigned_user)
1227 end
1221 end
1228
1222
1229 should "not include users with the 'only_owner' notification option" do
1223 should "not include users with the 'only_owner' notification option" do
1230 assert !@project.notified_users.include?(@only_owned_user)
1224 assert !@project.notified_users.include?(@only_owned_user)
1231 end
1225 end
1232 end
1226 end
1233
1227
1234 end
1228 end
@@ -1,1215 +1,1215
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class QueryTest < ActiveSupport::TestCase
20 class QueryTest < ActiveSupport::TestCase
21 include Redmine::I18n
21 include Redmine::I18n
22
22
23 fixtures :projects, :enabled_modules, :users, :members,
23 fixtures :projects, :enabled_modules, :users, :members,
24 :member_roles, :roles, :trackers, :issue_statuses,
24 :member_roles, :roles, :trackers, :issue_statuses,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :watchers, :custom_fields, :custom_values, :versions,
26 :watchers, :custom_fields, :custom_values, :versions,
27 :queries,
27 :queries,
28 :projects_trackers,
28 :projects_trackers,
29 :custom_fields_trackers
29 :custom_fields_trackers
30
30
31 def test_custom_fields_for_all_projects_should_be_available_in_global_queries
31 def test_custom_fields_for_all_projects_should_be_available_in_global_queries
32 query = Query.new(:project => nil, :name => '_')
32 query = Query.new(:project => nil, :name => '_')
33 assert query.available_filters.has_key?('cf_1')
33 assert query.available_filters.has_key?('cf_1')
34 assert !query.available_filters.has_key?('cf_3')
34 assert !query.available_filters.has_key?('cf_3')
35 end
35 end
36
36
37 def test_system_shared_versions_should_be_available_in_global_queries
37 def test_system_shared_versions_should_be_available_in_global_queries
38 Version.find(2).update_attribute :sharing, 'system'
38 Version.find(2).update_attribute :sharing, 'system'
39 query = Query.new(:project => nil, :name => '_')
39 query = Query.new(:project => nil, :name => '_')
40 assert query.available_filters.has_key?('fixed_version_id')
40 assert query.available_filters.has_key?('fixed_version_id')
41 assert query.available_filters['fixed_version_id'][:values].detect {|v| v.last == '2'}
41 assert query.available_filters['fixed_version_id'][:values].detect {|v| v.last == '2'}
42 end
42 end
43
43
44 def test_project_filter_in_global_queries
44 def test_project_filter_in_global_queries
45 query = Query.new(:project => nil, :name => '_')
45 query = Query.new(:project => nil, :name => '_')
46 project_filter = query.available_filters["project_id"]
46 project_filter = query.available_filters["project_id"]
47 assert_not_nil project_filter
47 assert_not_nil project_filter
48 project_ids = project_filter[:values].map{|p| p[1]}
48 project_ids = project_filter[:values].map{|p| p[1]}
49 assert project_ids.include?("1") #public project
49 assert project_ids.include?("1") #public project
50 assert !project_ids.include?("2") #private project user cannot see
50 assert !project_ids.include?("2") #private project user cannot see
51 end
51 end
52
52
53 def find_issues_with_query(query)
53 def find_issues_with_query(query)
54 Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
54 Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
55 query.statement
55 query.statement
56 ).all
56 ).all
57 end
57 end
58
58
59 def assert_find_issues_with_query_is_successful(query)
59 def assert_find_issues_with_query_is_successful(query)
60 assert_nothing_raised do
60 assert_nothing_raised do
61 find_issues_with_query(query)
61 find_issues_with_query(query)
62 end
62 end
63 end
63 end
64
64
65 def assert_query_statement_includes(query, condition)
65 def assert_query_statement_includes(query, condition)
66 assert query.statement.include?(condition), "Query statement condition not found in: #{query.statement}"
66 assert query.statement.include?(condition), "Query statement condition not found in: #{query.statement}"
67 end
67 end
68
68
69 def assert_query_result(expected, query)
69 def assert_query_result(expected, query)
70 assert_nothing_raised do
70 assert_nothing_raised do
71 assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
71 assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
72 assert_equal expected.size, query.issue_count
72 assert_equal expected.size, query.issue_count
73 end
73 end
74 end
74 end
75
75
76 def test_query_should_allow_shared_versions_for_a_project_query
76 def test_query_should_allow_shared_versions_for_a_project_query
77 subproject_version = Version.find(4)
77 subproject_version = Version.find(4)
78 query = Query.new(:project => Project.find(1), :name => '_')
78 query = Query.new(:project => Project.find(1), :name => '_')
79 query.add_filter('fixed_version_id', '=', [subproject_version.id.to_s])
79 query.add_filter('fixed_version_id', '=', [subproject_version.id.to_s])
80
80
81 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IN ('4')")
81 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IN ('4')")
82 end
82 end
83
83
84 def test_query_with_multiple_custom_fields
84 def test_query_with_multiple_custom_fields
85 query = Query.find(1)
85 query = Query.find(1)
86 assert query.valid?
86 assert query.valid?
87 assert query.statement.include?("#{CustomValue.table_name}.value IN ('MySQL')")
87 assert query.statement.include?("#{CustomValue.table_name}.value IN ('MySQL')")
88 issues = find_issues_with_query(query)
88 issues = find_issues_with_query(query)
89 assert_equal 1, issues.length
89 assert_equal 1, issues.length
90 assert_equal Issue.find(3), issues.first
90 assert_equal Issue.find(3), issues.first
91 end
91 end
92
92
93 def test_operator_none
93 def test_operator_none
94 query = Query.new(:project => Project.find(1), :name => '_')
94 query = Query.new(:project => Project.find(1), :name => '_')
95 query.add_filter('fixed_version_id', '!*', [''])
95 query.add_filter('fixed_version_id', '!*', [''])
96 query.add_filter('cf_1', '!*', [''])
96 query.add_filter('cf_1', '!*', [''])
97 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
97 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
98 assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
98 assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
99 find_issues_with_query(query)
99 find_issues_with_query(query)
100 end
100 end
101
101
102 def test_operator_none_for_integer
102 def test_operator_none_for_integer
103 query = Query.new(:project => Project.find(1), :name => '_')
103 query = Query.new(:project => Project.find(1), :name => '_')
104 query.add_filter('estimated_hours', '!*', [''])
104 query.add_filter('estimated_hours', '!*', [''])
105 issues = find_issues_with_query(query)
105 issues = find_issues_with_query(query)
106 assert !issues.empty?
106 assert !issues.empty?
107 assert issues.all? {|i| !i.estimated_hours}
107 assert issues.all? {|i| !i.estimated_hours}
108 end
108 end
109
109
110 def test_operator_none_for_date
110 def test_operator_none_for_date
111 query = Query.new(:project => Project.find(1), :name => '_')
111 query = Query.new(:project => Project.find(1), :name => '_')
112 query.add_filter('start_date', '!*', [''])
112 query.add_filter('start_date', '!*', [''])
113 issues = find_issues_with_query(query)
113 issues = find_issues_with_query(query)
114 assert !issues.empty?
114 assert !issues.empty?
115 assert issues.all? {|i| i.start_date.nil?}
115 assert issues.all? {|i| i.start_date.nil?}
116 end
116 end
117
117
118 def test_operator_none_for_string_custom_field
118 def test_operator_none_for_string_custom_field
119 query = Query.new(:project => Project.find(1), :name => '_')
119 query = Query.new(:project => Project.find(1), :name => '_')
120 query.add_filter('cf_2', '!*', [''])
120 query.add_filter('cf_2', '!*', [''])
121 assert query.has_filter?('cf_2')
121 assert query.has_filter?('cf_2')
122 issues = find_issues_with_query(query)
122 issues = find_issues_with_query(query)
123 assert !issues.empty?
123 assert !issues.empty?
124 assert issues.all? {|i| i.custom_field_value(2).blank?}
124 assert issues.all? {|i| i.custom_field_value(2).blank?}
125 end
125 end
126
126
127 def test_operator_all
127 def test_operator_all
128 query = Query.new(:project => Project.find(1), :name => '_')
128 query = Query.new(:project => Project.find(1), :name => '_')
129 query.add_filter('fixed_version_id', '*', [''])
129 query.add_filter('fixed_version_id', '*', [''])
130 query.add_filter('cf_1', '*', [''])
130 query.add_filter('cf_1', '*', [''])
131 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
131 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
132 assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
132 assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
133 find_issues_with_query(query)
133 find_issues_with_query(query)
134 end
134 end
135
135
136 def test_operator_all_for_date
136 def test_operator_all_for_date
137 query = Query.new(:project => Project.find(1), :name => '_')
137 query = Query.new(:project => Project.find(1), :name => '_')
138 query.add_filter('start_date', '*', [''])
138 query.add_filter('start_date', '*', [''])
139 issues = find_issues_with_query(query)
139 issues = find_issues_with_query(query)
140 assert !issues.empty?
140 assert !issues.empty?
141 assert issues.all? {|i| i.start_date.present?}
141 assert issues.all? {|i| i.start_date.present?}
142 end
142 end
143
143
144 def test_operator_all_for_string_custom_field
144 def test_operator_all_for_string_custom_field
145 query = Query.new(:project => Project.find(1), :name => '_')
145 query = Query.new(:project => Project.find(1), :name => '_')
146 query.add_filter('cf_2', '*', [''])
146 query.add_filter('cf_2', '*', [''])
147 assert query.has_filter?('cf_2')
147 assert query.has_filter?('cf_2')
148 issues = find_issues_with_query(query)
148 issues = find_issues_with_query(query)
149 assert !issues.empty?
149 assert !issues.empty?
150 assert issues.all? {|i| i.custom_field_value(2).present?}
150 assert issues.all? {|i| i.custom_field_value(2).present?}
151 end
151 end
152
152
153 def test_numeric_filter_should_not_accept_non_numeric_values
153 def test_numeric_filter_should_not_accept_non_numeric_values
154 query = Query.new(:name => '_')
154 query = Query.new(:name => '_')
155 query.add_filter('estimated_hours', '=', ['a'])
155 query.add_filter('estimated_hours', '=', ['a'])
156
156
157 assert query.has_filter?('estimated_hours')
157 assert query.has_filter?('estimated_hours')
158 assert !query.valid?
158 assert !query.valid?
159 end
159 end
160
160
161 def test_operator_is_on_float
161 def test_operator_is_on_float
162 Issue.update_all("estimated_hours = 171.2", "id=2")
162 Issue.update_all("estimated_hours = 171.2", "id=2")
163
163
164 query = Query.new(:name => '_')
164 query = Query.new(:name => '_')
165 query.add_filter('estimated_hours', '=', ['171.20'])
165 query.add_filter('estimated_hours', '=', ['171.20'])
166 issues = find_issues_with_query(query)
166 issues = find_issues_with_query(query)
167 assert_equal 1, issues.size
167 assert_equal 1, issues.size
168 assert_equal 2, issues.first.id
168 assert_equal 2, issues.first.id
169 end
169 end
170
170
171 def test_operator_is_on_integer_custom_field
171 def test_operator_is_on_integer_custom_field
172 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
172 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
173 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
173 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
174 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
174 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
175 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
175 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
176
176
177 query = Query.new(:name => '_')
177 query = Query.new(:name => '_')
178 query.add_filter("cf_#{f.id}", '=', ['12'])
178 query.add_filter("cf_#{f.id}", '=', ['12'])
179 issues = find_issues_with_query(query)
179 issues = find_issues_with_query(query)
180 assert_equal 1, issues.size
180 assert_equal 1, issues.size
181 assert_equal 2, issues.first.id
181 assert_equal 2, issues.first.id
182 end
182 end
183
183
184 def test_operator_is_on_integer_custom_field_should_accept_negative_value
184 def test_operator_is_on_integer_custom_field_should_accept_negative_value
185 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
185 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
186 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
186 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
187 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
187 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
188 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
188 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
189
189
190 query = Query.new(:name => '_')
190 query = Query.new(:name => '_')
191 query.add_filter("cf_#{f.id}", '=', ['-12'])
191 query.add_filter("cf_#{f.id}", '=', ['-12'])
192 assert query.valid?
192 assert query.valid?
193 issues = find_issues_with_query(query)
193 issues = find_issues_with_query(query)
194 assert_equal 1, issues.size
194 assert_equal 1, issues.size
195 assert_equal 2, issues.first.id
195 assert_equal 2, issues.first.id
196 end
196 end
197
197
198 def test_operator_is_on_float_custom_field
198 def test_operator_is_on_float_custom_field
199 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
199 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
200 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
200 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
201 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
201 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
202 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
202 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
203
203
204 query = Query.new(:name => '_')
204 query = Query.new(:name => '_')
205 query.add_filter("cf_#{f.id}", '=', ['12.7'])
205 query.add_filter("cf_#{f.id}", '=', ['12.7'])
206 issues = find_issues_with_query(query)
206 issues = find_issues_with_query(query)
207 assert_equal 1, issues.size
207 assert_equal 1, issues.size
208 assert_equal 2, issues.first.id
208 assert_equal 2, issues.first.id
209 end
209 end
210
210
211 def test_operator_is_on_float_custom_field_should_accept_negative_value
211 def test_operator_is_on_float_custom_field_should_accept_negative_value
212 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
212 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
213 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
213 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
214 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
214 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
215 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
215 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
216
216
217 query = Query.new(:name => '_')
217 query = Query.new(:name => '_')
218 query.add_filter("cf_#{f.id}", '=', ['-12.7'])
218 query.add_filter("cf_#{f.id}", '=', ['-12.7'])
219 assert query.valid?
219 assert query.valid?
220 issues = find_issues_with_query(query)
220 issues = find_issues_with_query(query)
221 assert_equal 1, issues.size
221 assert_equal 1, issues.size
222 assert_equal 2, issues.first.id
222 assert_equal 2, issues.first.id
223 end
223 end
224
224
225 def test_operator_is_on_multi_list_custom_field
225 def test_operator_is_on_multi_list_custom_field
226 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
226 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
227 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
227 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
228 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
228 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
229 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
229 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
230 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
230 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
231
231
232 query = Query.new(:name => '_')
232 query = Query.new(:name => '_')
233 query.add_filter("cf_#{f.id}", '=', ['value1'])
233 query.add_filter("cf_#{f.id}", '=', ['value1'])
234 issues = find_issues_with_query(query)
234 issues = find_issues_with_query(query)
235 assert_equal [1, 3], issues.map(&:id).sort
235 assert_equal [1, 3], issues.map(&:id).sort
236
236
237 query = Query.new(:name => '_')
237 query = Query.new(:name => '_')
238 query.add_filter("cf_#{f.id}", '=', ['value2'])
238 query.add_filter("cf_#{f.id}", '=', ['value2'])
239 issues = find_issues_with_query(query)
239 issues = find_issues_with_query(query)
240 assert_equal [1], issues.map(&:id).sort
240 assert_equal [1], issues.map(&:id).sort
241 end
241 end
242
242
243 def test_operator_is_not_on_multi_list_custom_field
243 def test_operator_is_not_on_multi_list_custom_field
244 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
244 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
245 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
245 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
246 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
246 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
247 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
247 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
248 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
248 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
249
249
250 query = Query.new(:name => '_')
250 query = Query.new(:name => '_')
251 query.add_filter("cf_#{f.id}", '!', ['value1'])
251 query.add_filter("cf_#{f.id}", '!', ['value1'])
252 issues = find_issues_with_query(query)
252 issues = find_issues_with_query(query)
253 assert !issues.map(&:id).include?(1)
253 assert !issues.map(&:id).include?(1)
254 assert !issues.map(&:id).include?(3)
254 assert !issues.map(&:id).include?(3)
255
255
256 query = Query.new(:name => '_')
256 query = Query.new(:name => '_')
257 query.add_filter("cf_#{f.id}", '!', ['value2'])
257 query.add_filter("cf_#{f.id}", '!', ['value2'])
258 issues = find_issues_with_query(query)
258 issues = find_issues_with_query(query)
259 assert !issues.map(&:id).include?(1)
259 assert !issues.map(&:id).include?(1)
260 assert issues.map(&:id).include?(3)
260 assert issues.map(&:id).include?(3)
261 end
261 end
262
262
263 def test_operator_is_on_is_private_field
263 def test_operator_is_on_is_private_field
264 # is_private filter only available for those who can set issues private
264 # is_private filter only available for those who can set issues private
265 User.current = User.find(2)
265 User.current = User.find(2)
266
266
267 query = Query.new(:name => '_')
267 query = Query.new(:name => '_')
268 assert query.available_filters.key?('is_private')
268 assert query.available_filters.key?('is_private')
269
269
270 query.add_filter("is_private", '=', ['1'])
270 query.add_filter("is_private", '=', ['1'])
271 issues = find_issues_with_query(query)
271 issues = find_issues_with_query(query)
272 assert issues.any?
272 assert issues.any?
273 assert_nil issues.detect {|issue| !issue.is_private?}
273 assert_nil issues.detect {|issue| !issue.is_private?}
274 ensure
274 ensure
275 User.current = nil
275 User.current = nil
276 end
276 end
277
277
278 def test_operator_is_not_on_is_private_field
278 def test_operator_is_not_on_is_private_field
279 # is_private filter only available for those who can set issues private
279 # is_private filter only available for those who can set issues private
280 User.current = User.find(2)
280 User.current = User.find(2)
281
281
282 query = Query.new(:name => '_')
282 query = Query.new(:name => '_')
283 assert query.available_filters.key?('is_private')
283 assert query.available_filters.key?('is_private')
284
284
285 query.add_filter("is_private", '!', ['1'])
285 query.add_filter("is_private", '!', ['1'])
286 issues = find_issues_with_query(query)
286 issues = find_issues_with_query(query)
287 assert issues.any?
287 assert issues.any?
288 assert_nil issues.detect {|issue| issue.is_private?}
288 assert_nil issues.detect {|issue| issue.is_private?}
289 ensure
289 ensure
290 User.current = nil
290 User.current = nil
291 end
291 end
292
292
293 def test_operator_greater_than
293 def test_operator_greater_than
294 query = Query.new(:project => Project.find(1), :name => '_')
294 query = Query.new(:project => Project.find(1), :name => '_')
295 query.add_filter('done_ratio', '>=', ['40'])
295 query.add_filter('done_ratio', '>=', ['40'])
296 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
296 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
297 find_issues_with_query(query)
297 find_issues_with_query(query)
298 end
298 end
299
299
300 def test_operator_greater_than_a_float
300 def test_operator_greater_than_a_float
301 query = Query.new(:project => Project.find(1), :name => '_')
301 query = Query.new(:project => Project.find(1), :name => '_')
302 query.add_filter('estimated_hours', '>=', ['40.5'])
302 query.add_filter('estimated_hours', '>=', ['40.5'])
303 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
303 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
304 find_issues_with_query(query)
304 find_issues_with_query(query)
305 end
305 end
306
306
307 def test_operator_greater_than_on_int_custom_field
307 def test_operator_greater_than_on_int_custom_field
308 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
308 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
309 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
309 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
310 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
310 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
311 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
311 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
312
312
313 query = Query.new(:project => Project.find(1), :name => '_')
313 query = Query.new(:project => Project.find(1), :name => '_')
314 query.add_filter("cf_#{f.id}", '>=', ['8'])
314 query.add_filter("cf_#{f.id}", '>=', ['8'])
315 issues = find_issues_with_query(query)
315 issues = find_issues_with_query(query)
316 assert_equal 1, issues.size
316 assert_equal 1, issues.size
317 assert_equal 2, issues.first.id
317 assert_equal 2, issues.first.id
318 end
318 end
319
319
320 def test_operator_lesser_than
320 def test_operator_lesser_than
321 query = Query.new(:project => Project.find(1), :name => '_')
321 query = Query.new(:project => Project.find(1), :name => '_')
322 query.add_filter('done_ratio', '<=', ['30'])
322 query.add_filter('done_ratio', '<=', ['30'])
323 assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
323 assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
324 find_issues_with_query(query)
324 find_issues_with_query(query)
325 end
325 end
326
326
327 def test_operator_lesser_than_on_custom_field
327 def test_operator_lesser_than_on_custom_field
328 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
328 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
329 query = Query.new(:project => Project.find(1), :name => '_')
329 query = Query.new(:project => Project.find(1), :name => '_')
330 query.add_filter("cf_#{f.id}", '<=', ['30'])
330 query.add_filter("cf_#{f.id}", '<=', ['30'])
331 assert query.statement.include?("CAST(custom_values.value AS decimal(60,3)) <= 30.0")
331 assert query.statement.include?("CAST(custom_values.value AS decimal(60,3)) <= 30.0")
332 find_issues_with_query(query)
332 find_issues_with_query(query)
333 end
333 end
334
334
335 def test_operator_between
335 def test_operator_between
336 query = Query.new(:project => Project.find(1), :name => '_')
336 query = Query.new(:project => Project.find(1), :name => '_')
337 query.add_filter('done_ratio', '><', ['30', '40'])
337 query.add_filter('done_ratio', '><', ['30', '40'])
338 assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
338 assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
339 find_issues_with_query(query)
339 find_issues_with_query(query)
340 end
340 end
341
341
342 def test_operator_between_on_custom_field
342 def test_operator_between_on_custom_field
343 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
343 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
344 query = Query.new(:project => Project.find(1), :name => '_')
344 query = Query.new(:project => Project.find(1), :name => '_')
345 query.add_filter("cf_#{f.id}", '><', ['30', '40'])
345 query.add_filter("cf_#{f.id}", '><', ['30', '40'])
346 assert_include "CAST(custom_values.value AS decimal(60,3)) BETWEEN 30.0 AND 40.0", query.statement
346 assert_include "CAST(custom_values.value AS decimal(60,3)) BETWEEN 30.0 AND 40.0", query.statement
347 find_issues_with_query(query)
347 find_issues_with_query(query)
348 end
348 end
349
349
350 def test_date_filter_should_not_accept_non_date_values
350 def test_date_filter_should_not_accept_non_date_values
351 query = Query.new(:name => '_')
351 query = Query.new(:name => '_')
352 query.add_filter('created_on', '=', ['a'])
352 query.add_filter('created_on', '=', ['a'])
353
353
354 assert query.has_filter?('created_on')
354 assert query.has_filter?('created_on')
355 assert !query.valid?
355 assert !query.valid?
356 end
356 end
357
357
358 def test_date_filter_should_not_accept_invalid_date_values
358 def test_date_filter_should_not_accept_invalid_date_values
359 query = Query.new(:name => '_')
359 query = Query.new(:name => '_')
360 query.add_filter('created_on', '=', ['2011-01-34'])
360 query.add_filter('created_on', '=', ['2011-01-34'])
361
361
362 assert query.has_filter?('created_on')
362 assert query.has_filter?('created_on')
363 assert !query.valid?
363 assert !query.valid?
364 end
364 end
365
365
366 def test_relative_date_filter_should_not_accept_non_integer_values
366 def test_relative_date_filter_should_not_accept_non_integer_values
367 query = Query.new(:name => '_')
367 query = Query.new(:name => '_')
368 query.add_filter('created_on', '>t-', ['a'])
368 query.add_filter('created_on', '>t-', ['a'])
369
369
370 assert query.has_filter?('created_on')
370 assert query.has_filter?('created_on')
371 assert !query.valid?
371 assert !query.valid?
372 end
372 end
373
373
374 def test_operator_date_equals
374 def test_operator_date_equals
375 query = Query.new(:name => '_')
375 query = Query.new(:name => '_')
376 query.add_filter('due_date', '=', ['2011-07-10'])
376 query.add_filter('due_date', '=', ['2011-07-10'])
377 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
377 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
378 find_issues_with_query(query)
378 find_issues_with_query(query)
379 end
379 end
380
380
381 def test_operator_date_lesser_than
381 def test_operator_date_lesser_than
382 query = Query.new(:name => '_')
382 query = Query.new(:name => '_')
383 query.add_filter('due_date', '<=', ['2011-07-10'])
383 query.add_filter('due_date', '<=', ['2011-07-10'])
384 assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
384 assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
385 find_issues_with_query(query)
385 find_issues_with_query(query)
386 end
386 end
387
387
388 def test_operator_date_greater_than
388 def test_operator_date_greater_than
389 query = Query.new(:name => '_')
389 query = Query.new(:name => '_')
390 query.add_filter('due_date', '>=', ['2011-07-10'])
390 query.add_filter('due_date', '>=', ['2011-07-10'])
391 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement
391 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement
392 find_issues_with_query(query)
392 find_issues_with_query(query)
393 end
393 end
394
394
395 def test_operator_date_between
395 def test_operator_date_between
396 query = Query.new(:name => '_')
396 query = Query.new(:name => '_')
397 query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
397 query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
398 assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
398 assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
399 find_issues_with_query(query)
399 find_issues_with_query(query)
400 end
400 end
401
401
402 def test_operator_in_more_than
402 def test_operator_in_more_than
403 Issue.find(7).update_attribute(:due_date, (Date.today + 15))
403 Issue.find(7).update_attribute(:due_date, (Date.today + 15))
404 query = Query.new(:project => Project.find(1), :name => '_')
404 query = Query.new(:project => Project.find(1), :name => '_')
405 query.add_filter('due_date', '>t+', ['15'])
405 query.add_filter('due_date', '>t+', ['15'])
406 issues = find_issues_with_query(query)
406 issues = find_issues_with_query(query)
407 assert !issues.empty?
407 assert !issues.empty?
408 issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
408 issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
409 end
409 end
410
410
411 def test_operator_in_less_than
411 def test_operator_in_less_than
412 query = Query.new(:project => Project.find(1), :name => '_')
412 query = Query.new(:project => Project.find(1), :name => '_')
413 query.add_filter('due_date', '<t+', ['15'])
413 query.add_filter('due_date', '<t+', ['15'])
414 issues = find_issues_with_query(query)
414 issues = find_issues_with_query(query)
415 assert !issues.empty?
415 assert !issues.empty?
416 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
416 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
417 end
417 end
418
418
419 def test_operator_less_than_ago
419 def test_operator_less_than_ago
420 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
420 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
421 query = Query.new(:project => Project.find(1), :name => '_')
421 query = Query.new(:project => Project.find(1), :name => '_')
422 query.add_filter('due_date', '>t-', ['3'])
422 query.add_filter('due_date', '>t-', ['3'])
423 issues = find_issues_with_query(query)
423 issues = find_issues_with_query(query)
424 assert !issues.empty?
424 assert !issues.empty?
425 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
425 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
426 end
426 end
427
427
428 def test_operator_more_than_ago
428 def test_operator_more_than_ago
429 Issue.find(7).update_attribute(:due_date, (Date.today - 10))
429 Issue.find(7).update_attribute(:due_date, (Date.today - 10))
430 query = Query.new(:project => Project.find(1), :name => '_')
430 query = Query.new(:project => Project.find(1), :name => '_')
431 query.add_filter('due_date', '<t-', ['10'])
431 query.add_filter('due_date', '<t-', ['10'])
432 assert query.statement.include?("#{Issue.table_name}.due_date <=")
432 assert query.statement.include?("#{Issue.table_name}.due_date <=")
433 issues = find_issues_with_query(query)
433 issues = find_issues_with_query(query)
434 assert !issues.empty?
434 assert !issues.empty?
435 issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
435 issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
436 end
436 end
437
437
438 def test_operator_in
438 def test_operator_in
439 Issue.find(7).update_attribute(:due_date, (Date.today + 2))
439 Issue.find(7).update_attribute(:due_date, (Date.today + 2))
440 query = Query.new(:project => Project.find(1), :name => '_')
440 query = Query.new(:project => Project.find(1), :name => '_')
441 query.add_filter('due_date', 't+', ['2'])
441 query.add_filter('due_date', 't+', ['2'])
442 issues = find_issues_with_query(query)
442 issues = find_issues_with_query(query)
443 assert !issues.empty?
443 assert !issues.empty?
444 issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
444 issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
445 end
445 end
446
446
447 def test_operator_ago
447 def test_operator_ago
448 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
448 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
449 query = Query.new(:project => Project.find(1), :name => '_')
449 query = Query.new(:project => Project.find(1), :name => '_')
450 query.add_filter('due_date', 't-', ['3'])
450 query.add_filter('due_date', 't-', ['3'])
451 issues = find_issues_with_query(query)
451 issues = find_issues_with_query(query)
452 assert !issues.empty?
452 assert !issues.empty?
453 issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
453 issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
454 end
454 end
455
455
456 def test_operator_today
456 def test_operator_today
457 query = Query.new(:project => Project.find(1), :name => '_')
457 query = Query.new(:project => Project.find(1), :name => '_')
458 query.add_filter('due_date', 't', [''])
458 query.add_filter('due_date', 't', [''])
459 issues = find_issues_with_query(query)
459 issues = find_issues_with_query(query)
460 assert !issues.empty?
460 assert !issues.empty?
461 issues.each {|issue| assert_equal Date.today, issue.due_date}
461 issues.each {|issue| assert_equal Date.today, issue.due_date}
462 end
462 end
463
463
464 def test_operator_this_week_on_date
464 def test_operator_this_week_on_date
465 query = Query.new(:project => Project.find(1), :name => '_')
465 query = Query.new(:project => Project.find(1), :name => '_')
466 query.add_filter('due_date', 'w', [''])
466 query.add_filter('due_date', 'w', [''])
467 find_issues_with_query(query)
467 find_issues_with_query(query)
468 end
468 end
469
469
470 def test_operator_this_week_on_datetime
470 def test_operator_this_week_on_datetime
471 query = Query.new(:project => Project.find(1), :name => '_')
471 query = Query.new(:project => Project.find(1), :name => '_')
472 query.add_filter('created_on', 'w', [''])
472 query.add_filter('created_on', 'w', [''])
473 find_issues_with_query(query)
473 find_issues_with_query(query)
474 end
474 end
475
475
476 def test_operator_contains
476 def test_operator_contains
477 query = Query.new(:project => Project.find(1), :name => '_')
477 query = Query.new(:project => Project.find(1), :name => '_')
478 query.add_filter('subject', '~', ['uNable'])
478 query.add_filter('subject', '~', ['uNable'])
479 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
479 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
480 result = find_issues_with_query(query)
480 result = find_issues_with_query(query)
481 assert result.empty?
481 assert result.empty?
482 result.each {|issue| assert issue.subject.downcase.include?('unable') }
482 result.each {|issue| assert issue.subject.downcase.include?('unable') }
483 end
483 end
484
484
485 def test_range_for_this_week_with_week_starting_on_monday
485 def test_range_for_this_week_with_week_starting_on_monday
486 I18n.locale = :fr
486 I18n.locale = :fr
487 assert_equal '1', I18n.t(:general_first_day_of_week)
487 assert_equal '1', I18n.t(:general_first_day_of_week)
488
488
489 Date.stubs(:today).returns(Date.parse('2011-04-29'))
489 Date.stubs(:today).returns(Date.parse('2011-04-29'))
490
490
491 query = Query.new(:project => Project.find(1), :name => '_')
491 query = Query.new(:project => Project.find(1), :name => '_')
492 query.add_filter('due_date', 'w', [''])
492 query.add_filter('due_date', 'w', [''])
493 assert query.statement.match(/issues\.due_date > '2011-04-24 23:59:59(\.9+)?' AND issues\.due_date <= '2011-05-01 23:59:59(\.9+)?/), "range not found in #{query.statement}"
493 assert query.statement.match(/issues\.due_date > '2011-04-24 23:59:59(\.9+)?' AND issues\.due_date <= '2011-05-01 23:59:59(\.9+)?/), "range not found in #{query.statement}"
494 I18n.locale = :en
494 I18n.locale = :en
495 end
495 end
496
496
497 def test_range_for_this_week_with_week_starting_on_sunday
497 def test_range_for_this_week_with_week_starting_on_sunday
498 I18n.locale = :en
498 I18n.locale = :en
499 assert_equal '7', I18n.t(:general_first_day_of_week)
499 assert_equal '7', I18n.t(:general_first_day_of_week)
500
500
501 Date.stubs(:today).returns(Date.parse('2011-04-29'))
501 Date.stubs(:today).returns(Date.parse('2011-04-29'))
502
502
503 query = Query.new(:project => Project.find(1), :name => '_')
503 query = Query.new(:project => Project.find(1), :name => '_')
504 query.add_filter('due_date', 'w', [''])
504 query.add_filter('due_date', 'w', [''])
505 assert query.statement.match(/issues\.due_date > '2011-04-23 23:59:59(\.9+)?' AND issues\.due_date <= '2011-04-30 23:59:59(\.9+)?/), "range not found in #{query.statement}"
505 assert query.statement.match(/issues\.due_date > '2011-04-23 23:59:59(\.9+)?' AND issues\.due_date <= '2011-04-30 23:59:59(\.9+)?/), "range not found in #{query.statement}"
506 end
506 end
507
507
508 def test_operator_does_not_contains
508 def test_operator_does_not_contains
509 query = Query.new(:project => Project.find(1), :name => '_')
509 query = Query.new(:project => Project.find(1), :name => '_')
510 query.add_filter('subject', '!~', ['uNable'])
510 query.add_filter('subject', '!~', ['uNable'])
511 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
511 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
512 find_issues_with_query(query)
512 find_issues_with_query(query)
513 end
513 end
514
514
515 def test_filter_assigned_to_me
515 def test_filter_assigned_to_me
516 user = User.find(2)
516 user = User.find(2)
517 group = Group.find(10)
517 group = Group.find(10)
518 User.current = user
518 User.current = user
519 i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
519 i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
520 i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
520 i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
521 i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => Group.find(11))
521 i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => Group.find(11))
522 group.users << user
522 group.users << user
523
523
524 query = Query.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
524 query = Query.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
525 result = query.issues
525 result = query.issues
526 assert_equal Issue.visible.all(:conditions => {:assigned_to_id => ([2] + user.reload.group_ids)}).sort_by(&:id), result.sort_by(&:id)
526 assert_equal Issue.visible.all(:conditions => {:assigned_to_id => ([2] + user.reload.group_ids)}).sort_by(&:id), result.sort_by(&:id)
527
527
528 assert result.include?(i1)
528 assert result.include?(i1)
529 assert result.include?(i2)
529 assert result.include?(i2)
530 assert !result.include?(i3)
530 assert !result.include?(i3)
531 end
531 end
532
532
533 def test_user_custom_field_filtered_on_me
533 def test_user_custom_field_filtered_on_me
534 User.current = User.find(2)
534 User.current = User.find(2)
535 cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
535 cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
536 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
536 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
537 issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
537 issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
538
538
539 query = Query.new(:name => '_', :project => Project.find(1))
539 query = Query.new(:name => '_', :project => Project.find(1))
540 filter = query.available_filters["cf_#{cf.id}"]
540 filter = query.available_filters["cf_#{cf.id}"]
541 assert_not_nil filter
541 assert_not_nil filter
542 assert_include 'me', filter[:values].map{|v| v[1]}
542 assert_include 'me', filter[:values].map{|v| v[1]}
543
543
544 query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
544 query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
545 result = query.issues
545 result = query.issues
546 assert_equal 1, result.size
546 assert_equal 1, result.size
547 assert_equal issue1, result.first
547 assert_equal issue1, result.first
548 end
548 end
549
549
550 def test_filter_my_projects
550 def test_filter_my_projects
551 User.current = User.find(2)
551 User.current = User.find(2)
552 query = Query.new(:name => '_')
552 query = Query.new(:name => '_')
553 filter = query.available_filters['project_id']
553 filter = query.available_filters['project_id']
554 assert_not_nil filter
554 assert_not_nil filter
555 assert_include 'mine', filter[:values].map{|v| v[1]}
555 assert_include 'mine', filter[:values].map{|v| v[1]}
556
556
557 query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
557 query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
558 result = query.issues
558 result = query.issues
559 assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
559 assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
560 end
560 end
561
561
562 def test_filter_watched_issues
562 def test_filter_watched_issues
563 User.current = User.find(1)
563 User.current = User.find(1)
564 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
564 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
565 result = find_issues_with_query(query)
565 result = find_issues_with_query(query)
566 assert_not_nil result
566 assert_not_nil result
567 assert !result.empty?
567 assert !result.empty?
568 assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
568 assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
569 User.current = nil
569 User.current = nil
570 end
570 end
571
571
572 def test_filter_unwatched_issues
572 def test_filter_unwatched_issues
573 User.current = User.find(1)
573 User.current = User.find(1)
574 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
574 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
575 result = find_issues_with_query(query)
575 result = find_issues_with_query(query)
576 assert_not_nil result
576 assert_not_nil result
577 assert !result.empty?
577 assert !result.empty?
578 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
578 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
579 User.current = nil
579 User.current = nil
580 end
580 end
581
581
582 def test_filter_on_project_custom_field
582 def test_filter_on_project_custom_field
583 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
583 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
584 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
584 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
585 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
585 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
586
586
587 query = Query.new(:name => '_')
587 query = Query.new(:name => '_')
588 filter_name = "project.cf_#{field.id}"
588 filter_name = "project.cf_#{field.id}"
589 assert_include filter_name, query.available_filters.keys
589 assert_include filter_name, query.available_filters.keys
590 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
590 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
591 assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
591 assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
592 end
592 end
593
593
594 def test_filter_on_author_custom_field
594 def test_filter_on_author_custom_field
595 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
595 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
596 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
596 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
597
597
598 query = Query.new(:name => '_')
598 query = Query.new(:name => '_')
599 filter_name = "author.cf_#{field.id}"
599 filter_name = "author.cf_#{field.id}"
600 assert_include filter_name, query.available_filters.keys
600 assert_include filter_name, query.available_filters.keys
601 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
601 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
602 assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
602 assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
603 end
603 end
604
604
605 def test_filter_on_assigned_to_custom_field
605 def test_filter_on_assigned_to_custom_field
606 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
606 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
607 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
607 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
608
608
609 query = Query.new(:name => '_')
609 query = Query.new(:name => '_')
610 filter_name = "assigned_to.cf_#{field.id}"
610 filter_name = "assigned_to.cf_#{field.id}"
611 assert_include filter_name, query.available_filters.keys
611 assert_include filter_name, query.available_filters.keys
612 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
612 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
613 assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
613 assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
614 end
614 end
615
615
616 def test_filter_on_fixed_version_custom_field
616 def test_filter_on_fixed_version_custom_field
617 field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
617 field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
618 CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
618 CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
619
619
620 query = Query.new(:name => '_')
620 query = Query.new(:name => '_')
621 filter_name = "fixed_version.cf_#{field.id}"
621 filter_name = "fixed_version.cf_#{field.id}"
622 assert_include filter_name, query.available_filters.keys
622 assert_include filter_name, query.available_filters.keys
623 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
623 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
624 assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
624 assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
625 end
625 end
626
626
627 def test_filter_on_relations_with_a_specific_issue
627 def test_filter_on_relations_with_a_specific_issue
628 IssueRelation.delete_all
628 IssueRelation.delete_all
629 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
629 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
630 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
630 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
631
631
632 query = Query.new(:name => '_')
632 query = Query.new(:name => '_')
633 query.filters = {"relates" => {:operator => '=', :values => ['1']}}
633 query.filters = {"relates" => {:operator => '=', :values => ['1']}}
634 assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
634 assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
635
635
636 query = Query.new(:name => '_')
636 query = Query.new(:name => '_')
637 query.filters = {"relates" => {:operator => '=', :values => ['2']}}
637 query.filters = {"relates" => {:operator => '=', :values => ['2']}}
638 assert_equal [1], find_issues_with_query(query).map(&:id).sort
638 assert_equal [1], find_issues_with_query(query).map(&:id).sort
639 end
639 end
640
640
641 def test_filter_on_relations_with_any_issues_in_a_project
641 def test_filter_on_relations_with_any_issues_in_a_project
642 IssueRelation.delete_all
642 IssueRelation.delete_all
643 with_settings :cross_project_issue_relations => '1' do
643 with_settings :cross_project_issue_relations => '1' do
644 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
644 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
645 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
645 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
646 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
646 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
647 end
647 end
648
648
649 query = Query.new(:name => '_')
649 query = Query.new(:name => '_')
650 query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
650 query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
651 assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
651 assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
652
652
653 query = Query.new(:name => '_')
653 query = Query.new(:name => '_')
654 query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
654 query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
655 assert_equal [1], find_issues_with_query(query).map(&:id).sort
655 assert_equal [1], find_issues_with_query(query).map(&:id).sort
656
656
657 query = Query.new(:name => '_')
657 query = Query.new(:name => '_')
658 query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
658 query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
659 assert_equal [], find_issues_with_query(query).map(&:id).sort
659 assert_equal [], find_issues_with_query(query).map(&:id).sort
660 end
660 end
661
661
662 def test_filter_on_relations_with_any_issues_not_in_a_project
662 def test_filter_on_relations_with_any_issues_not_in_a_project
663 IssueRelation.delete_all
663 IssueRelation.delete_all
664 with_settings :cross_project_issue_relations => '1' do
664 with_settings :cross_project_issue_relations => '1' do
665 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
665 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
666 #IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
666 #IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
667 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
667 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
668 end
668 end
669
669
670 query = Query.new(:name => '_')
670 query = Query.new(:name => '_')
671 query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
671 query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
672 assert_equal [1], find_issues_with_query(query).map(&:id).sort
672 assert_equal [1], find_issues_with_query(query).map(&:id).sort
673 end
673 end
674
674
675 def test_filter_on_relations_with_no_issues_in_a_project
675 def test_filter_on_relations_with_no_issues_in_a_project
676 IssueRelation.delete_all
676 IssueRelation.delete_all
677 with_settings :cross_project_issue_relations => '1' do
677 with_settings :cross_project_issue_relations => '1' do
678 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
678 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
679 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
679 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
680 IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
680 IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
681 end
681 end
682
682
683 query = Query.new(:name => '_')
683 query = Query.new(:name => '_')
684 query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
684 query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
685 ids = find_issues_with_query(query).map(&:id).sort
685 ids = find_issues_with_query(query).map(&:id).sort
686 assert_include 2, ids
686 assert_include 2, ids
687 assert_not_include 1, ids
687 assert_not_include 1, ids
688 assert_not_include 3, ids
688 assert_not_include 3, ids
689 end
689 end
690
690
691 def test_filter_on_relations_with_no_issues
691 def test_filter_on_relations_with_no_issues
692 IssueRelation.delete_all
692 IssueRelation.delete_all
693 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
693 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
694 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
694 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
695
695
696 query = Query.new(:name => '_')
696 query = Query.new(:name => '_')
697 query.filters = {"relates" => {:operator => '!*', :values => ['']}}
697 query.filters = {"relates" => {:operator => '!*', :values => ['']}}
698 ids = find_issues_with_query(query).map(&:id)
698 ids = find_issues_with_query(query).map(&:id)
699 assert_equal [], ids & [1, 2, 3]
699 assert_equal [], ids & [1, 2, 3]
700 assert_include 4, ids
700 assert_include 4, ids
701 end
701 end
702
702
703 def test_filter_on_relations_with_any_issues
703 def test_filter_on_relations_with_any_issues
704 IssueRelation.delete_all
704 IssueRelation.delete_all
705 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
705 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
706 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
706 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
707
707
708 query = Query.new(:name => '_')
708 query = Query.new(:name => '_')
709 query.filters = {"relates" => {:operator => '*', :values => ['']}}
709 query.filters = {"relates" => {:operator => '*', :values => ['']}}
710 assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
710 assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
711 end
711 end
712
712
713 def test_statement_should_be_nil_with_no_filters
713 def test_statement_should_be_nil_with_no_filters
714 q = Query.new(:name => '_')
714 q = Query.new(:name => '_')
715 q.filters = {}
715 q.filters = {}
716
716
717 assert q.valid?
717 assert q.valid?
718 assert_nil q.statement
718 assert_nil q.statement
719 end
719 end
720
720
721 def test_default_columns
721 def test_default_columns
722 q = Query.new
722 q = Query.new
723 assert !q.columns.empty?
723 assert !q.columns.empty?
724 end
724 end
725
725
726 def test_set_column_names
726 def test_set_column_names
727 q = Query.new
727 q = Query.new
728 q.column_names = ['tracker', :subject, '', 'unknonw_column']
728 q.column_names = ['tracker', :subject, '', 'unknonw_column']
729 assert_equal [:tracker, :subject], q.columns.collect {|c| c.name}
729 assert_equal [:tracker, :subject], q.columns.collect {|c| c.name}
730 c = q.columns.first
730 c = q.columns.first
731 assert q.has_column?(c)
731 assert q.has_column?(c)
732 end
732 end
733
733
734 def test_query_should_preload_spent_hours
734 def test_query_should_preload_spent_hours
735 q = Query.new(:name => '_', :column_names => [:subject, :spent_hours])
735 q = Query.new(:name => '_', :column_names => [:subject, :spent_hours])
736 assert q.has_column?(:spent_hours)
736 assert q.has_column?(:spent_hours)
737 issues = q.issues
737 issues = q.issues
738 assert_not_nil issues.first.instance_variable_get("@spent_hours")
738 assert_not_nil issues.first.instance_variable_get("@spent_hours")
739 end
739 end
740
740
741 def test_groupable_columns_should_include_custom_fields
741 def test_groupable_columns_should_include_custom_fields
742 q = Query.new
742 q = Query.new
743 column = q.groupable_columns.detect {|c| c.name == :cf_1}
743 column = q.groupable_columns.detect {|c| c.name == :cf_1}
744 assert_not_nil column
744 assert_not_nil column
745 assert_kind_of QueryCustomFieldColumn, column
745 assert_kind_of QueryCustomFieldColumn, column
746 end
746 end
747
747
748 def test_groupable_columns_should_not_include_multi_custom_fields
748 def test_groupable_columns_should_not_include_multi_custom_fields
749 field = CustomField.find(1)
749 field = CustomField.find(1)
750 field.update_attribute :multiple, true
750 field.update_attribute :multiple, true
751
751
752 q = Query.new
752 q = Query.new
753 column = q.groupable_columns.detect {|c| c.name == :cf_1}
753 column = q.groupable_columns.detect {|c| c.name == :cf_1}
754 assert_nil column
754 assert_nil column
755 end
755 end
756
756
757 def test_groupable_columns_should_include_user_custom_fields
757 def test_groupable_columns_should_include_user_custom_fields
758 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
758 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
759
759
760 q = Query.new
760 q = Query.new
761 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
761 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
762 end
762 end
763
763
764 def test_groupable_columns_should_include_version_custom_fields
764 def test_groupable_columns_should_include_version_custom_fields
765 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
765 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
766
766
767 q = Query.new
767 q = Query.new
768 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
768 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
769 end
769 end
770
770
771 def test_grouped_with_valid_column
771 def test_grouped_with_valid_column
772 q = Query.new(:group_by => 'status')
772 q = Query.new(:group_by => 'status')
773 assert q.grouped?
773 assert q.grouped?
774 assert_not_nil q.group_by_column
774 assert_not_nil q.group_by_column
775 assert_equal :status, q.group_by_column.name
775 assert_equal :status, q.group_by_column.name
776 assert_not_nil q.group_by_statement
776 assert_not_nil q.group_by_statement
777 assert_equal 'status', q.group_by_statement
777 assert_equal 'status', q.group_by_statement
778 end
778 end
779
779
780 def test_grouped_with_invalid_column
780 def test_grouped_with_invalid_column
781 q = Query.new(:group_by => 'foo')
781 q = Query.new(:group_by => 'foo')
782 assert !q.grouped?
782 assert !q.grouped?
783 assert_nil q.group_by_column
783 assert_nil q.group_by_column
784 assert_nil q.group_by_statement
784 assert_nil q.group_by_statement
785 end
785 end
786
786
787 def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
787 def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
788 with_settings :user_format => 'lastname_coma_firstname' do
788 with_settings :user_format => 'lastname_coma_firstname' do
789 q = Query.new
789 q = Query.new
790 assert q.sortable_columns.has_key?('assigned_to')
790 assert q.sortable_columns.has_key?('assigned_to')
791 assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
791 assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
792 end
792 end
793 end
793 end
794
794
795 def test_sortable_columns_should_sort_authors_according_to_user_format_setting
795 def test_sortable_columns_should_sort_authors_according_to_user_format_setting
796 with_settings :user_format => 'lastname_coma_firstname' do
796 with_settings :user_format => 'lastname_coma_firstname' do
797 q = Query.new
797 q = Query.new
798 assert q.sortable_columns.has_key?('author')
798 assert q.sortable_columns.has_key?('author')
799 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
799 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
800 end
800 end
801 end
801 end
802
802
803 def test_sortable_columns_should_include_custom_field
803 def test_sortable_columns_should_include_custom_field
804 q = Query.new
804 q = Query.new
805 assert q.sortable_columns['cf_1']
805 assert q.sortable_columns['cf_1']
806 end
806 end
807
807
808 def test_sortable_columns_should_not_include_multi_custom_field
808 def test_sortable_columns_should_not_include_multi_custom_field
809 field = CustomField.find(1)
809 field = CustomField.find(1)
810 field.update_attribute :multiple, true
810 field.update_attribute :multiple, true
811
811
812 q = Query.new
812 q = Query.new
813 assert !q.sortable_columns['cf_1']
813 assert !q.sortable_columns['cf_1']
814 end
814 end
815
815
816 def test_default_sort
816 def test_default_sort
817 q = Query.new
817 q = Query.new
818 assert_equal [], q.sort_criteria
818 assert_equal [], q.sort_criteria
819 end
819 end
820
820
821 def test_set_sort_criteria_with_hash
821 def test_set_sort_criteria_with_hash
822 q = Query.new
822 q = Query.new
823 q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
823 q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
824 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
824 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
825 end
825 end
826
826
827 def test_set_sort_criteria_with_array
827 def test_set_sort_criteria_with_array
828 q = Query.new
828 q = Query.new
829 q.sort_criteria = [['priority', 'desc'], 'tracker']
829 q.sort_criteria = [['priority', 'desc'], 'tracker']
830 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
830 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
831 end
831 end
832
832
833 def test_create_query_with_sort
833 def test_create_query_with_sort
834 q = Query.new(:name => 'Sorted')
834 q = Query.new(:name => 'Sorted')
835 q.sort_criteria = [['priority', 'desc'], 'tracker']
835 q.sort_criteria = [['priority', 'desc'], 'tracker']
836 assert q.save
836 assert q.save
837 q.reload
837 q.reload
838 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
838 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
839 end
839 end
840
840
841 def test_sort_by_string_custom_field_asc
841 def test_sort_by_string_custom_field_asc
842 q = Query.new
842 q = Query.new
843 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
843 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
844 assert c
844 assert c
845 assert c.sortable
845 assert c.sortable
846 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
846 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
847 q.statement
847 q.statement
848 ).order("#{c.sortable} ASC").all
848 ).order("#{c.sortable} ASC").all
849 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
849 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
850 assert !values.empty?
850 assert !values.empty?
851 assert_equal values.sort, values
851 assert_equal values.sort, values
852 end
852 end
853
853
854 def test_sort_by_string_custom_field_desc
854 def test_sort_by_string_custom_field_desc
855 q = Query.new
855 q = Query.new
856 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
856 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
857 assert c
857 assert c
858 assert c.sortable
858 assert c.sortable
859 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
859 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
860 q.statement
860 q.statement
861 ).order("#{c.sortable} DESC").all
861 ).order("#{c.sortable} DESC").all
862 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
862 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
863 assert !values.empty?
863 assert !values.empty?
864 assert_equal values.sort.reverse, values
864 assert_equal values.sort.reverse, values
865 end
865 end
866
866
867 def test_sort_by_float_custom_field_asc
867 def test_sort_by_float_custom_field_asc
868 q = Query.new
868 q = Query.new
869 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
869 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
870 assert c
870 assert c
871 assert c.sortable
871 assert c.sortable
872 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
872 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
873 q.statement
873 q.statement
874 ).order("#{c.sortable} ASC").all
874 ).order("#{c.sortable} ASC").all
875 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
875 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
876 assert !values.empty?
876 assert !values.empty?
877 assert_equal values.sort, values
877 assert_equal values.sort, values
878 end
878 end
879
879
880 def test_invalid_query_should_raise_query_statement_invalid_error
880 def test_invalid_query_should_raise_query_statement_invalid_error
881 q = Query.new
881 q = Query.new
882 assert_raise Query::StatementInvalid do
882 assert_raise Query::StatementInvalid do
883 q.issues(:conditions => "foo = 1")
883 q.issues(:conditions => "foo = 1")
884 end
884 end
885 end
885 end
886
886
887 def test_issue_count
887 def test_issue_count
888 q = Query.new(:name => '_')
888 q = Query.new(:name => '_')
889 issue_count = q.issue_count
889 issue_count = q.issue_count
890 assert_equal q.issues.size, issue_count
890 assert_equal q.issues.size, issue_count
891 end
891 end
892
892
893 def test_issue_count_with_archived_issues
893 def test_issue_count_with_archived_issues
894 p = Project.generate! do |project|
894 p = Project.generate! do |project|
895 project.status = Project::STATUS_ARCHIVED
895 project.status = Project::STATUS_ARCHIVED
896 end
896 end
897 i = Issue.generate!( :project => p, :tracker => p.trackers.first )
897 i = Issue.generate!( :project => p, :tracker => p.trackers.first )
898 assert !i.visible?
898 assert !i.visible?
899
899
900 test_issue_count
900 test_issue_count
901 end
901 end
902
902
903 def test_issue_count_by_association_group
903 def test_issue_count_by_association_group
904 q = Query.new(:name => '_', :group_by => 'assigned_to')
904 q = Query.new(:name => '_', :group_by => 'assigned_to')
905 count_by_group = q.issue_count_by_group
905 count_by_group = q.issue_count_by_group
906 assert_kind_of Hash, count_by_group
906 assert_kind_of Hash, count_by_group
907 assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
907 assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
908 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
908 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
909 assert count_by_group.has_key?(User.find(3))
909 assert count_by_group.has_key?(User.find(3))
910 end
910 end
911
911
912 def test_issue_count_by_list_custom_field_group
912 def test_issue_count_by_list_custom_field_group
913 q = Query.new(:name => '_', :group_by => 'cf_1')
913 q = Query.new(:name => '_', :group_by => 'cf_1')
914 count_by_group = q.issue_count_by_group
914 count_by_group = q.issue_count_by_group
915 assert_kind_of Hash, count_by_group
915 assert_kind_of Hash, count_by_group
916 assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
916 assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
917 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
917 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
918 assert count_by_group.has_key?('MySQL')
918 assert count_by_group.has_key?('MySQL')
919 end
919 end
920
920
921 def test_issue_count_by_date_custom_field_group
921 def test_issue_count_by_date_custom_field_group
922 q = Query.new(:name => '_', :group_by => 'cf_8')
922 q = Query.new(:name => '_', :group_by => 'cf_8')
923 count_by_group = q.issue_count_by_group
923 count_by_group = q.issue_count_by_group
924 assert_kind_of Hash, count_by_group
924 assert_kind_of Hash, count_by_group
925 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
925 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
926 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
926 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
927 end
927 end
928
928
929 def test_issue_count_with_nil_group_only
929 def test_issue_count_with_nil_group_only
930 Issue.update_all("assigned_to_id = NULL")
930 Issue.update_all("assigned_to_id = NULL")
931
931
932 q = Query.new(:name => '_', :group_by => 'assigned_to')
932 q = Query.new(:name => '_', :group_by => 'assigned_to')
933 count_by_group = q.issue_count_by_group
933 count_by_group = q.issue_count_by_group
934 assert_kind_of Hash, count_by_group
934 assert_kind_of Hash, count_by_group
935 assert_equal 1, count_by_group.keys.size
935 assert_equal 1, count_by_group.keys.size
936 assert_nil count_by_group.keys.first
936 assert_nil count_by_group.keys.first
937 end
937 end
938
938
939 def test_issue_ids
939 def test_issue_ids
940 q = Query.new(:name => '_')
940 q = Query.new(:name => '_')
941 order = "issues.subject, issues.id"
941 order = "issues.subject, issues.id"
942 issues = q.issues(:order => order)
942 issues = q.issues(:order => order)
943 assert_equal issues.map(&:id), q.issue_ids(:order => order)
943 assert_equal issues.map(&:id), q.issue_ids(:order => order)
944 end
944 end
945
945
946 def test_label_for
946 def test_label_for
947 set_language_if_valid 'en'
947 set_language_if_valid 'en'
948 q = Query.new
948 q = Query.new
949 assert_equal 'Assignee', q.label_for('assigned_to_id')
949 assert_equal 'Assignee', q.label_for('assigned_to_id')
950 end
950 end
951
951
952 def test_label_for_fr
952 def test_label_for_fr
953 set_language_if_valid 'fr'
953 set_language_if_valid 'fr'
954 q = Query.new
954 q = Query.new
955 s = "Assign\xc3\xa9 \xc3\xa0"
955 s = "Assign\xc3\xa9 \xc3\xa0"
956 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
956 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
957 assert_equal s, q.label_for('assigned_to_id')
957 assert_equal s, q.label_for('assigned_to_id')
958 end
958 end
959
959
960 def test_editable_by
960 def test_editable_by
961 admin = User.find(1)
961 admin = User.find(1)
962 manager = User.find(2)
962 manager = User.find(2)
963 developer = User.find(3)
963 developer = User.find(3)
964
964
965 # Public query on project 1
965 # Public query on project 1
966 q = Query.find(1)
966 q = Query.find(1)
967 assert q.editable_by?(admin)
967 assert q.editable_by?(admin)
968 assert q.editable_by?(manager)
968 assert q.editable_by?(manager)
969 assert !q.editable_by?(developer)
969 assert !q.editable_by?(developer)
970
970
971 # Private query on project 1
971 # Private query on project 1
972 q = Query.find(2)
972 q = Query.find(2)
973 assert q.editable_by?(admin)
973 assert q.editable_by?(admin)
974 assert !q.editable_by?(manager)
974 assert !q.editable_by?(manager)
975 assert q.editable_by?(developer)
975 assert q.editable_by?(developer)
976
976
977 # Private query for all projects
977 # Private query for all projects
978 q = Query.find(3)
978 q = Query.find(3)
979 assert q.editable_by?(admin)
979 assert q.editable_by?(admin)
980 assert !q.editable_by?(manager)
980 assert !q.editable_by?(manager)
981 assert q.editable_by?(developer)
981 assert q.editable_by?(developer)
982
982
983 # Public query for all projects
983 # Public query for all projects
984 q = Query.find(4)
984 q = Query.find(4)
985 assert q.editable_by?(admin)
985 assert q.editable_by?(admin)
986 assert !q.editable_by?(manager)
986 assert !q.editable_by?(manager)
987 assert !q.editable_by?(developer)
987 assert !q.editable_by?(developer)
988 end
988 end
989
989
990 def test_visible_scope
990 def test_visible_scope
991 query_ids = Query.visible(User.anonymous).map(&:id)
991 query_ids = Query.visible(User.anonymous).map(&:id)
992
992
993 assert query_ids.include?(1), 'public query on public project was not visible'
993 assert query_ids.include?(1), 'public query on public project was not visible'
994 assert query_ids.include?(4), 'public query for all projects was not visible'
994 assert query_ids.include?(4), 'public query for all projects was not visible'
995 assert !query_ids.include?(2), 'private query on public project was visible'
995 assert !query_ids.include?(2), 'private query on public project was visible'
996 assert !query_ids.include?(3), 'private query for all projects was visible'
996 assert !query_ids.include?(3), 'private query for all projects was visible'
997 assert !query_ids.include?(7), 'public query on private project was visible'
997 assert !query_ids.include?(7), 'public query on private project was visible'
998 end
998 end
999
999
1000 context "#available_filters" do
1000 context "#available_filters" do
1001 setup do
1001 setup do
1002 @query = Query.new(:name => "_")
1002 @query = Query.new(:name => "_")
1003 end
1003 end
1004
1004
1005 should "include users of visible projects in cross-project view" do
1005 should "include users of visible projects in cross-project view" do
1006 users = @query.available_filters["assigned_to_id"]
1006 users = @query.available_filters["assigned_to_id"]
1007 assert_not_nil users
1007 assert_not_nil users
1008 assert users[:values].map{|u|u[1]}.include?("3")
1008 assert users[:values].map{|u|u[1]}.include?("3")
1009 end
1009 end
1010
1010
1011 should "include users of subprojects" do
1011 should "include users of subprojects" do
1012 user1 = User.generate!
1012 user1 = User.generate!
1013 user2 = User.generate!
1013 user2 = User.generate!
1014 project = Project.find(1)
1014 project = Project.find(1)
1015 Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
1015 Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
1016 @query.project = project
1016 @query.project = project
1017
1017
1018 users = @query.available_filters["assigned_to_id"]
1018 users = @query.available_filters["assigned_to_id"]
1019 assert_not_nil users
1019 assert_not_nil users
1020 assert users[:values].map{|u|u[1]}.include?(user1.id.to_s)
1020 assert users[:values].map{|u|u[1]}.include?(user1.id.to_s)
1021 assert !users[:values].map{|u|u[1]}.include?(user2.id.to_s)
1021 assert !users[:values].map{|u|u[1]}.include?(user2.id.to_s)
1022 end
1022 end
1023
1023
1024 should "include visible projects in cross-project view" do
1024 should "include visible projects in cross-project view" do
1025 projects = @query.available_filters["project_id"]
1025 projects = @query.available_filters["project_id"]
1026 assert_not_nil projects
1026 assert_not_nil projects
1027 assert projects[:values].map{|u|u[1]}.include?("1")
1027 assert projects[:values].map{|u|u[1]}.include?("1")
1028 end
1028 end
1029
1029
1030 context "'member_of_group' filter" do
1030 context "'member_of_group' filter" do
1031 should "be present" do
1031 should "be present" do
1032 assert @query.available_filters.keys.include?("member_of_group")
1032 assert @query.available_filters.keys.include?("member_of_group")
1033 end
1033 end
1034
1034
1035 should "be an optional list" do
1035 should "be an optional list" do
1036 assert_equal :list_optional, @query.available_filters["member_of_group"][:type]
1036 assert_equal :list_optional, @query.available_filters["member_of_group"][:type]
1037 end
1037 end
1038
1038
1039 should "have a list of the groups as values" do
1039 should "have a list of the groups as values" do
1040 Group.destroy_all # No fixtures
1040 Group.destroy_all # No fixtures
1041 group1 = Group.generate!.reload
1041 group1 = Group.generate!.reload
1042 group2 = Group.generate!.reload
1042 group2 = Group.generate!.reload
1043
1043
1044 expected_group_list = [
1044 expected_group_list = [
1045 [group1.name, group1.id.to_s],
1045 [group1.name, group1.id.to_s],
1046 [group2.name, group2.id.to_s]
1046 [group2.name, group2.id.to_s]
1047 ]
1047 ]
1048 assert_equal expected_group_list.sort, @query.available_filters["member_of_group"][:values].sort
1048 assert_equal expected_group_list.sort, @query.available_filters["member_of_group"][:values].sort
1049 end
1049 end
1050
1050
1051 end
1051 end
1052
1052
1053 context "'assigned_to_role' filter" do
1053 context "'assigned_to_role' filter" do
1054 should "be present" do
1054 should "be present" do
1055 assert @query.available_filters.keys.include?("assigned_to_role")
1055 assert @query.available_filters.keys.include?("assigned_to_role")
1056 end
1056 end
1057
1057
1058 should "be an optional list" do
1058 should "be an optional list" do
1059 assert_equal :list_optional, @query.available_filters["assigned_to_role"][:type]
1059 assert_equal :list_optional, @query.available_filters["assigned_to_role"][:type]
1060 end
1060 end
1061
1061
1062 should "have a list of the Roles as values" do
1062 should "have a list of the Roles as values" do
1063 assert @query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
1063 assert @query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
1064 assert @query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
1064 assert @query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
1065 assert @query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
1065 assert @query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
1066 end
1066 end
1067
1067
1068 should "not include the built in Roles as values" do
1068 should "not include the built in Roles as values" do
1069 assert ! @query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
1069 assert ! @query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
1070 assert ! @query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
1070 assert ! @query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
1071 end
1071 end
1072
1072
1073 end
1073 end
1074
1074
1075 end
1075 end
1076
1076
1077 context "#statement" do
1077 context "#statement" do
1078 context "with 'member_of_group' filter" do
1078 context "with 'member_of_group' filter" do
1079 setup do
1079 setup do
1080 Group.destroy_all # No fixtures
1080 Group.destroy_all # No fixtures
1081 @user_in_group = User.generate!
1081 @user_in_group = User.generate!
1082 @second_user_in_group = User.generate!
1082 @second_user_in_group = User.generate!
1083 @user_in_group2 = User.generate!
1083 @user_in_group2 = User.generate!
1084 @user_not_in_group = User.generate!
1084 @user_not_in_group = User.generate!
1085
1085
1086 @group = Group.generate!.reload
1086 @group = Group.generate!.reload
1087 @group.users << @user_in_group
1087 @group.users << @user_in_group
1088 @group.users << @second_user_in_group
1088 @group.users << @second_user_in_group
1089
1089
1090 @group2 = Group.generate!.reload
1090 @group2 = Group.generate!.reload
1091 @group2.users << @user_in_group2
1091 @group2.users << @user_in_group2
1092
1092
1093 end
1093 end
1094
1094
1095 should "search assigned to for users in the group" do
1095 should "search assigned to for users in the group" do
1096 @query = Query.new(:name => '_')
1096 @query = Query.new(:name => '_')
1097 @query.add_filter('member_of_group', '=', [@group.id.to_s])
1097 @query.add_filter('member_of_group', '=', [@group.id.to_s])
1098
1098
1099 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}')"
1099 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}')"
1100 assert_find_issues_with_query_is_successful @query
1100 assert_find_issues_with_query_is_successful @query
1101 end
1101 end
1102
1102
1103 should "search not assigned to any group member (none)" do
1103 should "search not assigned to any group member (none)" do
1104 @query = Query.new(:name => '_')
1104 @query = Query.new(:name => '_')
1105 @query.add_filter('member_of_group', '!*', [''])
1105 @query.add_filter('member_of_group', '!*', [''])
1106
1106
1107 # Users not in a group
1107 # Users not in a group
1108 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
1108 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
1109 assert_find_issues_with_query_is_successful @query
1109 assert_find_issues_with_query_is_successful @query
1110 end
1110 end
1111
1111
1112 should "search assigned to any group member (all)" do
1112 should "search assigned to any group member (all)" do
1113 @query = Query.new(:name => '_')
1113 @query = Query.new(:name => '_')
1114 @query.add_filter('member_of_group', '*', [''])
1114 @query.add_filter('member_of_group', '*', [''])
1115
1115
1116 # Only users in a group
1116 # Only users in a group
1117 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
1117 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}')"
1118 assert_find_issues_with_query_is_successful @query
1118 assert_find_issues_with_query_is_successful @query
1119 end
1119 end
1120
1120
1121 should "return an empty set with = empty group" do
1121 should "return an empty set with = empty group" do
1122 @empty_group = Group.generate!
1122 @empty_group = Group.generate!
1123 @query = Query.new(:name => '_')
1123 @query = Query.new(:name => '_')
1124 @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
1124 @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
1125
1125
1126 assert_equal [], find_issues_with_query(@query)
1126 assert_equal [], find_issues_with_query(@query)
1127 end
1127 end
1128
1128
1129 should "return issues with ! empty group" do
1129 should "return issues with ! empty group" do
1130 @empty_group = Group.generate!
1130 @empty_group = Group.generate!
1131 @query = Query.new(:name => '_')
1131 @query = Query.new(:name => '_')
1132 @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
1132 @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
1133
1133
1134 assert_find_issues_with_query_is_successful @query
1134 assert_find_issues_with_query_is_successful @query
1135 end
1135 end
1136 end
1136 end
1137
1137
1138 context "with 'assigned_to_role' filter" do
1138 context "with 'assigned_to_role' filter" do
1139 setup do
1139 setup do
1140 @manager_role = Role.find_by_name('Manager')
1140 @manager_role = Role.find_by_name('Manager')
1141 @developer_role = Role.find_by_name('Developer')
1141 @developer_role = Role.find_by_name('Developer')
1142
1142
1143 @project = Project.generate!
1143 @project = Project.generate!
1144 @manager = User.generate!
1144 @manager = User.generate!
1145 @developer = User.generate!
1145 @developer = User.generate!
1146 @boss = User.generate!
1146 @boss = User.generate!
1147 @guest = User.generate!
1147 @guest = User.generate!
1148 User.add_to_project(@manager, @project, @manager_role)
1148 User.add_to_project(@manager, @project, @manager_role)
1149 User.add_to_project(@developer, @project, @developer_role)
1149 User.add_to_project(@developer, @project, @developer_role)
1150 User.add_to_project(@boss, @project, [@manager_role, @developer_role])
1150 User.add_to_project(@boss, @project, [@manager_role, @developer_role])
1151
1151
1152 @issue1 = Issue.generate_for_project!(@project, :assigned_to_id => @manager.id)
1152 @issue1 = Issue.generate!(:project => @project, :assigned_to_id => @manager.id)
1153 @issue2 = Issue.generate_for_project!(@project, :assigned_to_id => @developer.id)
1153 @issue2 = Issue.generate!(:project => @project, :assigned_to_id => @developer.id)
1154 @issue3 = Issue.generate_for_project!(@project, :assigned_to_id => @boss.id)
1154 @issue3 = Issue.generate!(:project => @project, :assigned_to_id => @boss.id)
1155 @issue4 = Issue.generate_for_project!(@project, :assigned_to_id => @guest.id)
1155 @issue4 = Issue.generate!(:project => @project, :assigned_to_id => @guest.id)
1156 @issue5 = Issue.generate_for_project!(@project)
1156 @issue5 = Issue.generate!(:project => @project)
1157 end
1157 end
1158
1158
1159 should "search assigned to for users with the Role" do
1159 should "search assigned to for users with the Role" do
1160 @query = Query.new(:name => '_', :project => @project)
1160 @query = Query.new(:name => '_', :project => @project)
1161 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1161 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1162
1162
1163 assert_query_result [@issue1, @issue3], @query
1163 assert_query_result [@issue1, @issue3], @query
1164 end
1164 end
1165
1165
1166 should "search assigned to for users with the Role on the issue project" do
1166 should "search assigned to for users with the Role on the issue project" do
1167 other_project = Project.generate!
1167 other_project = Project.generate!
1168 User.add_to_project(@developer, other_project, @manager_role)
1168 User.add_to_project(@developer, other_project, @manager_role)
1169
1169
1170 @query = Query.new(:name => '_', :project => @project)
1170 @query = Query.new(:name => '_', :project => @project)
1171 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1171 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1172
1172
1173 assert_query_result [@issue1, @issue3], @query
1173 assert_query_result [@issue1, @issue3], @query
1174 end
1174 end
1175
1175
1176 should "return an empty set with empty role" do
1176 should "return an empty set with empty role" do
1177 @empty_role = Role.generate!
1177 @empty_role = Role.generate!
1178 @query = Query.new(:name => '_', :project => @project)
1178 @query = Query.new(:name => '_', :project => @project)
1179 @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
1179 @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
1180
1180
1181 assert_query_result [], @query
1181 assert_query_result [], @query
1182 end
1182 end
1183
1183
1184 should "search assigned to for users without the Role" do
1184 should "search assigned to for users without the Role" do
1185 @query = Query.new(:name => '_', :project => @project)
1185 @query = Query.new(:name => '_', :project => @project)
1186 @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
1186 @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
1187
1187
1188 assert_query_result [@issue2, @issue4, @issue5], @query
1188 assert_query_result [@issue2, @issue4, @issue5], @query
1189 end
1189 end
1190
1190
1191 should "search assigned to for users not assigned to any Role (none)" do
1191 should "search assigned to for users not assigned to any Role (none)" do
1192 @query = Query.new(:name => '_', :project => @project)
1192 @query = Query.new(:name => '_', :project => @project)
1193 @query.add_filter('assigned_to_role', '!*', [''])
1193 @query.add_filter('assigned_to_role', '!*', [''])
1194
1194
1195 assert_query_result [@issue4, @issue5], @query
1195 assert_query_result [@issue4, @issue5], @query
1196 end
1196 end
1197
1197
1198 should "search assigned to for users assigned to any Role (all)" do
1198 should "search assigned to for users assigned to any Role (all)" do
1199 @query = Query.new(:name => '_', :project => @project)
1199 @query = Query.new(:name => '_', :project => @project)
1200 @query.add_filter('assigned_to_role', '*', [''])
1200 @query.add_filter('assigned_to_role', '*', [''])
1201
1201
1202 assert_query_result [@issue1, @issue2, @issue3], @query
1202 assert_query_result [@issue1, @issue2, @issue3], @query
1203 end
1203 end
1204
1204
1205 should "return issues with ! empty role" do
1205 should "return issues with ! empty role" do
1206 @empty_role = Role.generate!
1206 @empty_role = Role.generate!
1207 @query = Query.new(:name => '_', :project => @project)
1207 @query = Query.new(:name => '_', :project => @project)
1208 @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
1208 @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
1209
1209
1210 assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
1210 assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
1211 end
1211 end
1212 end
1212 end
1213 end
1213 end
1214
1214
1215 end
1215 end
@@ -1,1074 +1,1074
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UserTest < ActiveSupport::TestCase
20 class UserTest < ActiveSupport::TestCase
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :projects_trackers,
23 :projects_trackers,
24 :watchers,
24 :watchers,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :journals, :journal_details,
26 :journals, :journal_details,
27 :groups_users,
27 :groups_users,
28 :enabled_modules,
28 :enabled_modules,
29 :workflows
29 :workflows
30
30
31 def setup
31 def setup
32 @admin = User.find(1)
32 @admin = User.find(1)
33 @jsmith = User.find(2)
33 @jsmith = User.find(2)
34 @dlopper = User.find(3)
34 @dlopper = User.find(3)
35 end
35 end
36
36
37 def test_generate
37 def test_generate
38 User.generate!(:firstname => 'Testing connection')
38 User.generate!(:firstname => 'Testing connection')
39 User.generate!(:firstname => 'Testing connection')
39 User.generate!(:firstname => 'Testing connection')
40 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
40 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
41 end
41 end
42
42
43 def test_truth
43 def test_truth
44 assert_kind_of User, @jsmith
44 assert_kind_of User, @jsmith
45 end
45 end
46
46
47 def test_mail_should_be_stripped
47 def test_mail_should_be_stripped
48 u = User.new
48 u = User.new
49 u.mail = " foo@bar.com "
49 u.mail = " foo@bar.com "
50 assert_equal "foo@bar.com", u.mail
50 assert_equal "foo@bar.com", u.mail
51 end
51 end
52
52
53 def test_mail_validation
53 def test_mail_validation
54 u = User.new
54 u = User.new
55 u.mail = ''
55 u.mail = ''
56 assert !u.valid?
56 assert !u.valid?
57 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
57 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
58 end
58 end
59
59
60 def test_login_length_validation
60 def test_login_length_validation
61 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
61 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
62 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
62 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
63 assert !user.valid?
63 assert !user.valid?
64
64
65 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
65 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
66 assert user.valid?
66 assert user.valid?
67 assert user.save
67 assert user.save
68 end
68 end
69
69
70 def test_create
70 def test_create
71 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
71 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
72
72
73 user.login = "jsmith"
73 user.login = "jsmith"
74 user.password, user.password_confirmation = "password", "password"
74 user.password, user.password_confirmation = "password", "password"
75 # login uniqueness
75 # login uniqueness
76 assert !user.save
76 assert !user.save
77 assert_equal 1, user.errors.count
77 assert_equal 1, user.errors.count
78
78
79 user.login = "newuser"
79 user.login = "newuser"
80 user.password, user.password_confirmation = "passwd", "password"
80 user.password, user.password_confirmation = "passwd", "password"
81 # password confirmation
81 # password confirmation
82 assert !user.save
82 assert !user.save
83 assert_equal 1, user.errors.count
83 assert_equal 1, user.errors.count
84
84
85 user.password, user.password_confirmation = "password", "password"
85 user.password, user.password_confirmation = "password", "password"
86 assert user.save
86 assert user.save
87 end
87 end
88
88
89 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
89 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
90 @user1 = User.generate!
90 @user1 = User.generate!
91 assert_equal 'only_my_events', @user1.mail_notification
91 assert_equal 'only_my_events', @user1.mail_notification
92 with_settings :default_notification_option => 'all' do
92 with_settings :default_notification_option => 'all' do
93 @user2 = User.generate!
93 @user2 = User.generate!
94 assert_equal 'all', @user2.mail_notification
94 assert_equal 'all', @user2.mail_notification
95 end
95 end
96 end
96 end
97
97
98 def test_user_login_should_be_case_insensitive
98 def test_user_login_should_be_case_insensitive
99 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
99 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
100 u.login = 'newuser'
100 u.login = 'newuser'
101 u.password, u.password_confirmation = "password", "password"
101 u.password, u.password_confirmation = "password", "password"
102 assert u.save
102 assert u.save
103 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
103 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
104 u.login = 'NewUser'
104 u.login = 'NewUser'
105 u.password, u.password_confirmation = "password", "password"
105 u.password, u.password_confirmation = "password", "password"
106 assert !u.save
106 assert !u.save
107 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
107 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
108 end
108 end
109
109
110 def test_mail_uniqueness_should_not_be_case_sensitive
110 def test_mail_uniqueness_should_not_be_case_sensitive
111 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
111 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
112 u.login = 'newuser1'
112 u.login = 'newuser1'
113 u.password, u.password_confirmation = "password", "password"
113 u.password, u.password_confirmation = "password", "password"
114 assert u.save
114 assert u.save
115
115
116 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
116 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
117 u.login = 'newuser2'
117 u.login = 'newuser2'
118 u.password, u.password_confirmation = "password", "password"
118 u.password, u.password_confirmation = "password", "password"
119 assert !u.save
119 assert !u.save
120 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
120 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
121 end
121 end
122
122
123 def test_update
123 def test_update
124 assert_equal "admin", @admin.login
124 assert_equal "admin", @admin.login
125 @admin.login = "john"
125 @admin.login = "john"
126 assert @admin.save, @admin.errors.full_messages.join("; ")
126 assert @admin.save, @admin.errors.full_messages.join("; ")
127 @admin.reload
127 @admin.reload
128 assert_equal "john", @admin.login
128 assert_equal "john", @admin.login
129 end
129 end
130
130
131 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
131 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
132 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
132 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
133 u1.login = 'newuser1'
133 u1.login = 'newuser1'
134 assert u1.save
134 assert u1.save
135
135
136 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
136 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
137 u2.login = 'newuser1'
137 u2.login = 'newuser1'
138 assert u2.save(:validate => false)
138 assert u2.save(:validate => false)
139
139
140 user = User.find(u2.id)
140 user = User.find(u2.id)
141 user.firstname = "firstname"
141 user.firstname = "firstname"
142 assert user.save, "Save failed"
142 assert user.save, "Save failed"
143 end
143 end
144
144
145 def test_destroy_should_delete_members_and_roles
145 def test_destroy_should_delete_members_and_roles
146 members = Member.find_all_by_user_id(2)
146 members = Member.find_all_by_user_id(2)
147 ms = members.size
147 ms = members.size
148 rs = members.collect(&:roles).flatten.size
148 rs = members.collect(&:roles).flatten.size
149
149
150 assert_difference 'Member.count', - ms do
150 assert_difference 'Member.count', - ms do
151 assert_difference 'MemberRole.count', - rs do
151 assert_difference 'MemberRole.count', - rs do
152 User.find(2).destroy
152 User.find(2).destroy
153 end
153 end
154 end
154 end
155
155
156 assert_nil User.find_by_id(2)
156 assert_nil User.find_by_id(2)
157 assert Member.find_all_by_user_id(2).empty?
157 assert Member.find_all_by_user_id(2).empty?
158 end
158 end
159
159
160 def test_destroy_should_update_attachments
160 def test_destroy_should_update_attachments
161 attachment = Attachment.create!(:container => Project.find(1),
161 attachment = Attachment.create!(:container => Project.find(1),
162 :file => uploaded_test_file("testfile.txt", "text/plain"),
162 :file => uploaded_test_file("testfile.txt", "text/plain"),
163 :author_id => 2)
163 :author_id => 2)
164
164
165 User.find(2).destroy
165 User.find(2).destroy
166 assert_nil User.find_by_id(2)
166 assert_nil User.find_by_id(2)
167 assert_equal User.anonymous, attachment.reload.author
167 assert_equal User.anonymous, attachment.reload.author
168 end
168 end
169
169
170 def test_destroy_should_update_comments
170 def test_destroy_should_update_comments
171 comment = Comment.create!(
171 comment = Comment.create!(
172 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
172 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
173 :author => User.find(2),
173 :author => User.find(2),
174 :comments => 'foo'
174 :comments => 'foo'
175 )
175 )
176
176
177 User.find(2).destroy
177 User.find(2).destroy
178 assert_nil User.find_by_id(2)
178 assert_nil User.find_by_id(2)
179 assert_equal User.anonymous, comment.reload.author
179 assert_equal User.anonymous, comment.reload.author
180 end
180 end
181
181
182 def test_destroy_should_update_issues
182 def test_destroy_should_update_issues
183 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
183 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
184
184
185 User.find(2).destroy
185 User.find(2).destroy
186 assert_nil User.find_by_id(2)
186 assert_nil User.find_by_id(2)
187 assert_equal User.anonymous, issue.reload.author
187 assert_equal User.anonymous, issue.reload.author
188 end
188 end
189
189
190 def test_destroy_should_unassign_issues
190 def test_destroy_should_unassign_issues
191 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
191 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
192
192
193 User.find(2).destroy
193 User.find(2).destroy
194 assert_nil User.find_by_id(2)
194 assert_nil User.find_by_id(2)
195 assert_nil issue.reload.assigned_to
195 assert_nil issue.reload.assigned_to
196 end
196 end
197
197
198 def test_destroy_should_update_journals
198 def test_destroy_should_update_journals
199 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
199 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
200 issue.init_journal(User.find(2), "update")
200 issue.init_journal(User.find(2), "update")
201 issue.save!
201 issue.save!
202
202
203 User.find(2).destroy
203 User.find(2).destroy
204 assert_nil User.find_by_id(2)
204 assert_nil User.find_by_id(2)
205 assert_equal User.anonymous, issue.journals.first.reload.user
205 assert_equal User.anonymous, issue.journals.first.reload.user
206 end
206 end
207
207
208 def test_destroy_should_update_journal_details_old_value
208 def test_destroy_should_update_journal_details_old_value
209 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
209 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
210 issue.init_journal(User.find(1), "update")
210 issue.init_journal(User.find(1), "update")
211 issue.assigned_to_id = nil
211 issue.assigned_to_id = nil
212 assert_difference 'JournalDetail.count' do
212 assert_difference 'JournalDetail.count' do
213 issue.save!
213 issue.save!
214 end
214 end
215 journal_detail = JournalDetail.first(:order => 'id DESC')
215 journal_detail = JournalDetail.first(:order => 'id DESC')
216 assert_equal '2', journal_detail.old_value
216 assert_equal '2', journal_detail.old_value
217
217
218 User.find(2).destroy
218 User.find(2).destroy
219 assert_nil User.find_by_id(2)
219 assert_nil User.find_by_id(2)
220 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
220 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
221 end
221 end
222
222
223 def test_destroy_should_update_journal_details_value
223 def test_destroy_should_update_journal_details_value
224 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
224 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
225 issue.init_journal(User.find(1), "update")
225 issue.init_journal(User.find(1), "update")
226 issue.assigned_to_id = 2
226 issue.assigned_to_id = 2
227 assert_difference 'JournalDetail.count' do
227 assert_difference 'JournalDetail.count' do
228 issue.save!
228 issue.save!
229 end
229 end
230 journal_detail = JournalDetail.first(:order => 'id DESC')
230 journal_detail = JournalDetail.first(:order => 'id DESC')
231 assert_equal '2', journal_detail.value
231 assert_equal '2', journal_detail.value
232
232
233 User.find(2).destroy
233 User.find(2).destroy
234 assert_nil User.find_by_id(2)
234 assert_nil User.find_by_id(2)
235 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
235 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
236 end
236 end
237
237
238 def test_destroy_should_update_messages
238 def test_destroy_should_update_messages
239 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
239 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
240 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
240 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
241
241
242 User.find(2).destroy
242 User.find(2).destroy
243 assert_nil User.find_by_id(2)
243 assert_nil User.find_by_id(2)
244 assert_equal User.anonymous, message.reload.author
244 assert_equal User.anonymous, message.reload.author
245 end
245 end
246
246
247 def test_destroy_should_update_news
247 def test_destroy_should_update_news
248 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
248 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
249
249
250 User.find(2).destroy
250 User.find(2).destroy
251 assert_nil User.find_by_id(2)
251 assert_nil User.find_by_id(2)
252 assert_equal User.anonymous, news.reload.author
252 assert_equal User.anonymous, news.reload.author
253 end
253 end
254
254
255 def test_destroy_should_delete_private_queries
255 def test_destroy_should_delete_private_queries
256 query = Query.new(:name => 'foo', :is_public => false)
256 query = Query.new(:name => 'foo', :is_public => false)
257 query.project_id = 1
257 query.project_id = 1
258 query.user_id = 2
258 query.user_id = 2
259 query.save!
259 query.save!
260
260
261 User.find(2).destroy
261 User.find(2).destroy
262 assert_nil User.find_by_id(2)
262 assert_nil User.find_by_id(2)
263 assert_nil Query.find_by_id(query.id)
263 assert_nil Query.find_by_id(query.id)
264 end
264 end
265
265
266 def test_destroy_should_update_public_queries
266 def test_destroy_should_update_public_queries
267 query = Query.new(:name => 'foo', :is_public => true)
267 query = Query.new(:name => 'foo', :is_public => true)
268 query.project_id = 1
268 query.project_id = 1
269 query.user_id = 2
269 query.user_id = 2
270 query.save!
270 query.save!
271
271
272 User.find(2).destroy
272 User.find(2).destroy
273 assert_nil User.find_by_id(2)
273 assert_nil User.find_by_id(2)
274 assert_equal User.anonymous, query.reload.user
274 assert_equal User.anonymous, query.reload.user
275 end
275 end
276
276
277 def test_destroy_should_update_time_entries
277 def test_destroy_should_update_time_entries
278 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
278 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
279 entry.project_id = 1
279 entry.project_id = 1
280 entry.user_id = 2
280 entry.user_id = 2
281 entry.save!
281 entry.save!
282
282
283 User.find(2).destroy
283 User.find(2).destroy
284 assert_nil User.find_by_id(2)
284 assert_nil User.find_by_id(2)
285 assert_equal User.anonymous, entry.reload.user
285 assert_equal User.anonymous, entry.reload.user
286 end
286 end
287
287
288 def test_destroy_should_delete_tokens
288 def test_destroy_should_delete_tokens
289 token = Token.create!(:user_id => 2, :value => 'foo')
289 token = Token.create!(:user_id => 2, :value => 'foo')
290
290
291 User.find(2).destroy
291 User.find(2).destroy
292 assert_nil User.find_by_id(2)
292 assert_nil User.find_by_id(2)
293 assert_nil Token.find_by_id(token.id)
293 assert_nil Token.find_by_id(token.id)
294 end
294 end
295
295
296 def test_destroy_should_delete_watchers
296 def test_destroy_should_delete_watchers
297 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
297 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
298 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
298 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
299
299
300 User.find(2).destroy
300 User.find(2).destroy
301 assert_nil User.find_by_id(2)
301 assert_nil User.find_by_id(2)
302 assert_nil Watcher.find_by_id(watcher.id)
302 assert_nil Watcher.find_by_id(watcher.id)
303 end
303 end
304
304
305 def test_destroy_should_update_wiki_contents
305 def test_destroy_should_update_wiki_contents
306 wiki_content = WikiContent.create!(
306 wiki_content = WikiContent.create!(
307 :text => 'foo',
307 :text => 'foo',
308 :author_id => 2,
308 :author_id => 2,
309 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
309 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
310 )
310 )
311 wiki_content.text = 'bar'
311 wiki_content.text = 'bar'
312 assert_difference 'WikiContent::Version.count' do
312 assert_difference 'WikiContent::Version.count' do
313 wiki_content.save!
313 wiki_content.save!
314 end
314 end
315
315
316 User.find(2).destroy
316 User.find(2).destroy
317 assert_nil User.find_by_id(2)
317 assert_nil User.find_by_id(2)
318 assert_equal User.anonymous, wiki_content.reload.author
318 assert_equal User.anonymous, wiki_content.reload.author
319 wiki_content.versions.each do |version|
319 wiki_content.versions.each do |version|
320 assert_equal User.anonymous, version.reload.author
320 assert_equal User.anonymous, version.reload.author
321 end
321 end
322 end
322 end
323
323
324 def test_destroy_should_nullify_issue_categories
324 def test_destroy_should_nullify_issue_categories
325 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
325 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
326
326
327 User.find(2).destroy
327 User.find(2).destroy
328 assert_nil User.find_by_id(2)
328 assert_nil User.find_by_id(2)
329 assert_nil category.reload.assigned_to_id
329 assert_nil category.reload.assigned_to_id
330 end
330 end
331
331
332 def test_destroy_should_nullify_changesets
332 def test_destroy_should_nullify_changesets
333 changeset = Changeset.create!(
333 changeset = Changeset.create!(
334 :repository => Repository::Subversion.create!(
334 :repository => Repository::Subversion.create!(
335 :project_id => 1,
335 :project_id => 1,
336 :url => 'file:///tmp',
336 :url => 'file:///tmp',
337 :identifier => 'tmp'
337 :identifier => 'tmp'
338 ),
338 ),
339 :revision => '12',
339 :revision => '12',
340 :committed_on => Time.now,
340 :committed_on => Time.now,
341 :committer => 'jsmith'
341 :committer => 'jsmith'
342 )
342 )
343 assert_equal 2, changeset.user_id
343 assert_equal 2, changeset.user_id
344
344
345 User.find(2).destroy
345 User.find(2).destroy
346 assert_nil User.find_by_id(2)
346 assert_nil User.find_by_id(2)
347 assert_nil changeset.reload.user_id
347 assert_nil changeset.reload.user_id
348 end
348 end
349
349
350 def test_anonymous_user_should_not_be_destroyable
350 def test_anonymous_user_should_not_be_destroyable
351 assert_no_difference 'User.count' do
351 assert_no_difference 'User.count' do
352 assert_equal false, User.anonymous.destroy
352 assert_equal false, User.anonymous.destroy
353 end
353 end
354 end
354 end
355
355
356 def test_validate_login_presence
356 def test_validate_login_presence
357 @admin.login = ""
357 @admin.login = ""
358 assert !@admin.save
358 assert !@admin.save
359 assert_equal 1, @admin.errors.count
359 assert_equal 1, @admin.errors.count
360 end
360 end
361
361
362 def test_validate_mail_notification_inclusion
362 def test_validate_mail_notification_inclusion
363 u = User.new
363 u = User.new
364 u.mail_notification = 'foo'
364 u.mail_notification = 'foo'
365 u.save
365 u.save
366 assert_not_nil u.errors[:mail_notification]
366 assert_not_nil u.errors[:mail_notification]
367 end
367 end
368
368
369 context "User#try_to_login" do
369 context "User#try_to_login" do
370 should "fall-back to case-insensitive if user login is not found as-typed." do
370 should "fall-back to case-insensitive if user login is not found as-typed." do
371 user = User.try_to_login("AdMin", "admin")
371 user = User.try_to_login("AdMin", "admin")
372 assert_kind_of User, user
372 assert_kind_of User, user
373 assert_equal "admin", user.login
373 assert_equal "admin", user.login
374 end
374 end
375
375
376 should "select the exact matching user first" do
376 should "select the exact matching user first" do
377 case_sensitive_user = User.generate! do |user|
377 case_sensitive_user = User.generate! do |user|
378 user.password = "admin"
378 user.password = "admin"
379 end
379 end
380 # bypass validations to make it appear like existing data
380 # bypass validations to make it appear like existing data
381 case_sensitive_user.update_attribute(:login, 'ADMIN')
381 case_sensitive_user.update_attribute(:login, 'ADMIN')
382
382
383 user = User.try_to_login("ADMIN", "admin")
383 user = User.try_to_login("ADMIN", "admin")
384 assert_kind_of User, user
384 assert_kind_of User, user
385 assert_equal "ADMIN", user.login
385 assert_equal "ADMIN", user.login
386
386
387 end
387 end
388 end
388 end
389
389
390 def test_password
390 def test_password
391 user = User.try_to_login("admin", "admin")
391 user = User.try_to_login("admin", "admin")
392 assert_kind_of User, user
392 assert_kind_of User, user
393 assert_equal "admin", user.login
393 assert_equal "admin", user.login
394 user.password = "hello"
394 user.password = "hello"
395 assert user.save
395 assert user.save
396
396
397 user = User.try_to_login("admin", "hello")
397 user = User.try_to_login("admin", "hello")
398 assert_kind_of User, user
398 assert_kind_of User, user
399 assert_equal "admin", user.login
399 assert_equal "admin", user.login
400 end
400 end
401
401
402 def test_validate_password_length
402 def test_validate_password_length
403 with_settings :password_min_length => '100' do
403 with_settings :password_min_length => '100' do
404 user = User.new(:firstname => "new100", :lastname => "user100", :mail => "newuser100@somenet.foo")
404 user = User.new(:firstname => "new100", :lastname => "user100", :mail => "newuser100@somenet.foo")
405 user.login = "newuser100"
405 user.login = "newuser100"
406 user.password, user.password_confirmation = "password100", "password100"
406 user.password, user.password_confirmation = "password100", "password100"
407 assert !user.save
407 assert !user.save
408 assert_equal 1, user.errors.count
408 assert_equal 1, user.errors.count
409 end
409 end
410 end
410 end
411
411
412 def test_name_format
412 def test_name_format
413 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
413 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
414 with_settings :user_format => :firstname_lastname do
414 with_settings :user_format => :firstname_lastname do
415 assert_equal 'John Smith', @jsmith.reload.name
415 assert_equal 'John Smith', @jsmith.reload.name
416 end
416 end
417 with_settings :user_format => :username do
417 with_settings :user_format => :username do
418 assert_equal 'jsmith', @jsmith.reload.name
418 assert_equal 'jsmith', @jsmith.reload.name
419 end
419 end
420 with_settings :user_format => :lastname do
420 with_settings :user_format => :lastname do
421 assert_equal 'Smith', @jsmith.reload.name
421 assert_equal 'Smith', @jsmith.reload.name
422 end
422 end
423 end
423 end
424
424
425 def test_today_should_return_the_day_according_to_user_time_zone
425 def test_today_should_return_the_day_according_to_user_time_zone
426 preference = User.find(1).pref
426 preference = User.find(1).pref
427 date = Date.new(2012, 05, 15)
427 date = Date.new(2012, 05, 15)
428 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
428 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
429 Date.stubs(:today).returns(date)
429 Date.stubs(:today).returns(date)
430 Time.stubs(:now).returns(time)
430 Time.stubs(:now).returns(time)
431
431
432 preference.update_attribute :time_zone, 'Baku' # UTC+4
432 preference.update_attribute :time_zone, 'Baku' # UTC+4
433 assert_equal '2012-05-16', User.find(1).today.to_s
433 assert_equal '2012-05-16', User.find(1).today.to_s
434
434
435 preference.update_attribute :time_zone, 'La Paz' # UTC-4
435 preference.update_attribute :time_zone, 'La Paz' # UTC-4
436 assert_equal '2012-05-15', User.find(1).today.to_s
436 assert_equal '2012-05-15', User.find(1).today.to_s
437
437
438 preference.update_attribute :time_zone, ''
438 preference.update_attribute :time_zone, ''
439 assert_equal '2012-05-15', User.find(1).today.to_s
439 assert_equal '2012-05-15', User.find(1).today.to_s
440 end
440 end
441
441
442 def test_time_to_date_should_return_the_date_according_to_user_time_zone
442 def test_time_to_date_should_return_the_date_according_to_user_time_zone
443 preference = User.find(1).pref
443 preference = User.find(1).pref
444 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
444 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
445
445
446 preference.update_attribute :time_zone, 'Baku' # UTC+4
446 preference.update_attribute :time_zone, 'Baku' # UTC+4
447 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
447 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
448
448
449 preference.update_attribute :time_zone, 'La Paz' # UTC-4
449 preference.update_attribute :time_zone, 'La Paz' # UTC-4
450 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
450 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
451
451
452 preference.update_attribute :time_zone, ''
452 preference.update_attribute :time_zone, ''
453 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
453 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
454 end
454 end
455
455
456 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
456 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
457 with_settings :user_format => 'lastname_coma_firstname' do
457 with_settings :user_format => 'lastname_coma_firstname' do
458 assert_equal ['users.lastname', 'users.firstname', 'users.id'], User.fields_for_order_statement
458 assert_equal ['users.lastname', 'users.firstname', 'users.id'], User.fields_for_order_statement
459 end
459 end
460 end
460 end
461
461
462 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
462 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
463 with_settings :user_format => 'lastname_firstname' do
463 with_settings :user_format => 'lastname_firstname' do
464 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'], User.fields_for_order_statement('authors')
464 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'], User.fields_for_order_statement('authors')
465 end
465 end
466 end
466 end
467
467
468 def test_fields_for_order_statement_with_blank_format_should_return_default
468 def test_fields_for_order_statement_with_blank_format_should_return_default
469 with_settings :user_format => '' do
469 with_settings :user_format => '' do
470 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
470 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
471 end
471 end
472 end
472 end
473
473
474 def test_fields_for_order_statement_with_invalid_format_should_return_default
474 def test_fields_for_order_statement_with_invalid_format_should_return_default
475 with_settings :user_format => 'foo' do
475 with_settings :user_format => 'foo' do
476 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
476 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
477 end
477 end
478 end
478 end
479
479
480 def test_lock
480 def test_lock
481 user = User.try_to_login("jsmith", "jsmith")
481 user = User.try_to_login("jsmith", "jsmith")
482 assert_equal @jsmith, user
482 assert_equal @jsmith, user
483
483
484 @jsmith.status = User::STATUS_LOCKED
484 @jsmith.status = User::STATUS_LOCKED
485 assert @jsmith.save
485 assert @jsmith.save
486
486
487 user = User.try_to_login("jsmith", "jsmith")
487 user = User.try_to_login("jsmith", "jsmith")
488 assert_equal nil, user
488 assert_equal nil, user
489 end
489 end
490
490
491 context ".try_to_login" do
491 context ".try_to_login" do
492 context "with good credentials" do
492 context "with good credentials" do
493 should "return the user" do
493 should "return the user" do
494 user = User.try_to_login("admin", "admin")
494 user = User.try_to_login("admin", "admin")
495 assert_kind_of User, user
495 assert_kind_of User, user
496 assert_equal "admin", user.login
496 assert_equal "admin", user.login
497 end
497 end
498 end
498 end
499
499
500 context "with wrong credentials" do
500 context "with wrong credentials" do
501 should "return nil" do
501 should "return nil" do
502 assert_nil User.try_to_login("admin", "foo")
502 assert_nil User.try_to_login("admin", "foo")
503 end
503 end
504 end
504 end
505 end
505 end
506
506
507 if ldap_configured?
507 if ldap_configured?
508 context "#try_to_login using LDAP" do
508 context "#try_to_login using LDAP" do
509 context "with failed connection to the LDAP server" do
509 context "with failed connection to the LDAP server" do
510 should "return nil" do
510 should "return nil" do
511 @auth_source = AuthSourceLdap.find(1)
511 @auth_source = AuthSourceLdap.find(1)
512 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
512 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
513
513
514 assert_equal nil, User.try_to_login('edavis', 'wrong')
514 assert_equal nil, User.try_to_login('edavis', 'wrong')
515 end
515 end
516 end
516 end
517
517
518 context "with an unsuccessful authentication" do
518 context "with an unsuccessful authentication" do
519 should "return nil" do
519 should "return nil" do
520 assert_equal nil, User.try_to_login('edavis', 'wrong')
520 assert_equal nil, User.try_to_login('edavis', 'wrong')
521 end
521 end
522 end
522 end
523
523
524 context "binding with user's account" do
524 context "binding with user's account" do
525 setup do
525 setup do
526 @auth_source = AuthSourceLdap.find(1)
526 @auth_source = AuthSourceLdap.find(1)
527 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
527 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
528 @auth_source.account_password = ''
528 @auth_source.account_password = ''
529 @auth_source.save!
529 @auth_source.save!
530
530
531 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
531 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
532 @ldap_user.login = 'example1'
532 @ldap_user.login = 'example1'
533 @ldap_user.save!
533 @ldap_user.save!
534 end
534 end
535
535
536 context "with a successful authentication" do
536 context "with a successful authentication" do
537 should "return the user" do
537 should "return the user" do
538 assert_equal @ldap_user, User.try_to_login('example1', '123456')
538 assert_equal @ldap_user, User.try_to_login('example1', '123456')
539 end
539 end
540 end
540 end
541
541
542 context "with an unsuccessful authentication" do
542 context "with an unsuccessful authentication" do
543 should "return nil" do
543 should "return nil" do
544 assert_nil User.try_to_login('example1', '11111')
544 assert_nil User.try_to_login('example1', '11111')
545 end
545 end
546 end
546 end
547 end
547 end
548
548
549 context "on the fly registration" do
549 context "on the fly registration" do
550 setup do
550 setup do
551 @auth_source = AuthSourceLdap.find(1)
551 @auth_source = AuthSourceLdap.find(1)
552 @auth_source.update_attribute :onthefly_register, true
552 @auth_source.update_attribute :onthefly_register, true
553 end
553 end
554
554
555 context "with a successful authentication" do
555 context "with a successful authentication" do
556 should "create a new user account if it doesn't exist" do
556 should "create a new user account if it doesn't exist" do
557 assert_difference('User.count') do
557 assert_difference('User.count') do
558 user = User.try_to_login('edavis', '123456')
558 user = User.try_to_login('edavis', '123456')
559 assert !user.admin?
559 assert !user.admin?
560 end
560 end
561 end
561 end
562
562
563 should "retrieve existing user" do
563 should "retrieve existing user" do
564 user = User.try_to_login('edavis', '123456')
564 user = User.try_to_login('edavis', '123456')
565 user.admin = true
565 user.admin = true
566 user.save!
566 user.save!
567
567
568 assert_no_difference('User.count') do
568 assert_no_difference('User.count') do
569 user = User.try_to_login('edavis', '123456')
569 user = User.try_to_login('edavis', '123456')
570 assert user.admin?
570 assert user.admin?
571 end
571 end
572 end
572 end
573 end
573 end
574
574
575 context "binding with user's account" do
575 context "binding with user's account" do
576 setup do
576 setup do
577 @auth_source = AuthSourceLdap.find(1)
577 @auth_source = AuthSourceLdap.find(1)
578 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
578 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
579 @auth_source.account_password = ''
579 @auth_source.account_password = ''
580 @auth_source.save!
580 @auth_source.save!
581 end
581 end
582
582
583 context "with a successful authentication" do
583 context "with a successful authentication" do
584 should "create a new user account if it doesn't exist" do
584 should "create a new user account if it doesn't exist" do
585 assert_difference('User.count') do
585 assert_difference('User.count') do
586 user = User.try_to_login('example1', '123456')
586 user = User.try_to_login('example1', '123456')
587 assert_kind_of User, user
587 assert_kind_of User, user
588 end
588 end
589 end
589 end
590 end
590 end
591
591
592 context "with an unsuccessful authentication" do
592 context "with an unsuccessful authentication" do
593 should "return nil" do
593 should "return nil" do
594 assert_nil User.try_to_login('example1', '11111')
594 assert_nil User.try_to_login('example1', '11111')
595 end
595 end
596 end
596 end
597 end
597 end
598 end
598 end
599 end
599 end
600
600
601 else
601 else
602 puts "Skipping LDAP tests."
602 puts "Skipping LDAP tests."
603 end
603 end
604
604
605 def test_create_anonymous
605 def test_create_anonymous
606 AnonymousUser.delete_all
606 AnonymousUser.delete_all
607 anon = User.anonymous
607 anon = User.anonymous
608 assert !anon.new_record?
608 assert !anon.new_record?
609 assert_kind_of AnonymousUser, anon
609 assert_kind_of AnonymousUser, anon
610 end
610 end
611
611
612 def test_ensure_single_anonymous_user
612 def test_ensure_single_anonymous_user
613 AnonymousUser.delete_all
613 AnonymousUser.delete_all
614 anon1 = User.anonymous
614 anon1 = User.anonymous
615 assert !anon1.new_record?
615 assert !anon1.new_record?
616 assert_kind_of AnonymousUser, anon1
616 assert_kind_of AnonymousUser, anon1
617 anon2 = AnonymousUser.create(
617 anon2 = AnonymousUser.create(
618 :lastname => 'Anonymous', :firstname => '',
618 :lastname => 'Anonymous', :firstname => '',
619 :mail => '', :login => '', :status => 0)
619 :mail => '', :login => '', :status => 0)
620 assert_equal 1, anon2.errors.count
620 assert_equal 1, anon2.errors.count
621 end
621 end
622
622
623 def test_rss_key
623 def test_rss_key
624 assert_nil @jsmith.rss_token
624 assert_nil @jsmith.rss_token
625 key = @jsmith.rss_key
625 key = @jsmith.rss_key
626 assert_equal 40, key.length
626 assert_equal 40, key.length
627
627
628 @jsmith.reload
628 @jsmith.reload
629 assert_equal key, @jsmith.rss_key
629 assert_equal key, @jsmith.rss_key
630 end
630 end
631
631
632 def test_rss_key_should_not_be_generated_twice
632 def test_rss_key_should_not_be_generated_twice
633 assert_difference 'Token.count', 1 do
633 assert_difference 'Token.count', 1 do
634 key1 = @jsmith.rss_key
634 key1 = @jsmith.rss_key
635 key2 = @jsmith.rss_key
635 key2 = @jsmith.rss_key
636 assert_equal key1, key2
636 assert_equal key1, key2
637 end
637 end
638 end
638 end
639
639
640 def test_api_key_should_not_be_generated_twice
640 def test_api_key_should_not_be_generated_twice
641 assert_difference 'Token.count', 1 do
641 assert_difference 'Token.count', 1 do
642 key1 = @jsmith.api_key
642 key1 = @jsmith.api_key
643 key2 = @jsmith.api_key
643 key2 = @jsmith.api_key
644 assert_equal key1, key2
644 assert_equal key1, key2
645 end
645 end
646 end
646 end
647
647
648 context "User#api_key" do
648 context "User#api_key" do
649 should "generate a new one if the user doesn't have one" do
649 should "generate a new one if the user doesn't have one" do
650 user = User.generate!(:api_token => nil)
650 user = User.generate!(:api_token => nil)
651 assert_nil user.api_token
651 assert_nil user.api_token
652
652
653 key = user.api_key
653 key = user.api_key
654 assert_equal 40, key.length
654 assert_equal 40, key.length
655 user.reload
655 user.reload
656 assert_equal key, user.api_key
656 assert_equal key, user.api_key
657 end
657 end
658
658
659 should "return the existing api token value" do
659 should "return the existing api token value" do
660 user = User.generate!
660 user = User.generate!
661 token = Token.create!(:action => 'api')
661 token = Token.create!(:action => 'api')
662 user.api_token = token
662 user.api_token = token
663 assert user.save
663 assert user.save
664
664
665 assert_equal token.value, user.api_key
665 assert_equal token.value, user.api_key
666 end
666 end
667 end
667 end
668
668
669 context "User#find_by_api_key" do
669 context "User#find_by_api_key" do
670 should "return nil if no matching key is found" do
670 should "return nil if no matching key is found" do
671 assert_nil User.find_by_api_key('zzzzzzzzz')
671 assert_nil User.find_by_api_key('zzzzzzzzz')
672 end
672 end
673
673
674 should "return nil if the key is found for an inactive user" do
674 should "return nil if the key is found for an inactive user" do
675 user = User.generate!
675 user = User.generate!
676 user.status = User::STATUS_LOCKED
676 user.status = User::STATUS_LOCKED
677 token = Token.create!(:action => 'api')
677 token = Token.create!(:action => 'api')
678 user.api_token = token
678 user.api_token = token
679 user.save
679 user.save
680
680
681 assert_nil User.find_by_api_key(token.value)
681 assert_nil User.find_by_api_key(token.value)
682 end
682 end
683
683
684 should "return the user if the key is found for an active user" do
684 should "return the user if the key is found for an active user" do
685 user = User.generate!
685 user = User.generate!
686 token = Token.create!(:action => 'api')
686 token = Token.create!(:action => 'api')
687 user.api_token = token
687 user.api_token = token
688 user.save
688 user.save
689
689
690 assert_equal user, User.find_by_api_key(token.value)
690 assert_equal user, User.find_by_api_key(token.value)
691 end
691 end
692 end
692 end
693
693
694 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
694 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
695 user = User.find_by_login("admin")
695 user = User.find_by_login("admin")
696 user.password = "admin"
696 user.password = "admin"
697 user.save!
697 user.save!
698
698
699 assert_equal false, User.default_admin_account_changed?
699 assert_equal false, User.default_admin_account_changed?
700 end
700 end
701
701
702 def test_default_admin_account_changed_should_return_true_if_password_was_changed
702 def test_default_admin_account_changed_should_return_true_if_password_was_changed
703 user = User.find_by_login("admin")
703 user = User.find_by_login("admin")
704 user.password = "newpassword"
704 user.password = "newpassword"
705 user.save!
705 user.save!
706
706
707 assert_equal true, User.default_admin_account_changed?
707 assert_equal true, User.default_admin_account_changed?
708 end
708 end
709
709
710 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
710 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
711 user = User.find_by_login("admin")
711 user = User.find_by_login("admin")
712 user.password = "admin"
712 user.password = "admin"
713 user.status = User::STATUS_LOCKED
713 user.status = User::STATUS_LOCKED
714 user.save!
714 user.save!
715
715
716 assert_equal true, User.default_admin_account_changed?
716 assert_equal true, User.default_admin_account_changed?
717 end
717 end
718
718
719 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
719 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
720 user = User.find_by_login("admin")
720 user = User.find_by_login("admin")
721 user.destroy
721 user.destroy
722
722
723 assert_equal true, User.default_admin_account_changed?
723 assert_equal true, User.default_admin_account_changed?
724 end
724 end
725
725
726 def test_roles_for_project
726 def test_roles_for_project
727 # user with a role
727 # user with a role
728 roles = @jsmith.roles_for_project(Project.find(1))
728 roles = @jsmith.roles_for_project(Project.find(1))
729 assert_kind_of Role, roles.first
729 assert_kind_of Role, roles.first
730 assert_equal "Manager", roles.first.name
730 assert_equal "Manager", roles.first.name
731
731
732 # user with no role
732 # user with no role
733 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
733 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
734 end
734 end
735
735
736 def test_projects_by_role_for_user_with_role
736 def test_projects_by_role_for_user_with_role
737 user = User.find(2)
737 user = User.find(2)
738 assert_kind_of Hash, user.projects_by_role
738 assert_kind_of Hash, user.projects_by_role
739 assert_equal 2, user.projects_by_role.size
739 assert_equal 2, user.projects_by_role.size
740 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
740 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
741 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
741 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
742 end
742 end
743
743
744 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
744 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
745 user = User.find(2)
745 user = User.find(2)
746 assert_equal [], user.projects_by_role[Role.find(3)]
746 assert_equal [], user.projects_by_role[Role.find(3)]
747 # should not update the hash
747 # should not update the hash
748 assert_nil user.projects_by_role.values.detect(&:blank?)
748 assert_nil user.projects_by_role.values.detect(&:blank?)
749 end
749 end
750
750
751 def test_projects_by_role_for_user_with_no_role
751 def test_projects_by_role_for_user_with_no_role
752 user = User.generate!
752 user = User.generate!
753 assert_equal({}, user.projects_by_role)
753 assert_equal({}, user.projects_by_role)
754 end
754 end
755
755
756 def test_projects_by_role_for_anonymous
756 def test_projects_by_role_for_anonymous
757 assert_equal({}, User.anonymous.projects_by_role)
757 assert_equal({}, User.anonymous.projects_by_role)
758 end
758 end
759
759
760 def test_valid_notification_options
760 def test_valid_notification_options
761 # without memberships
761 # without memberships
762 assert_equal 5, User.find(7).valid_notification_options.size
762 assert_equal 5, User.find(7).valid_notification_options.size
763 # with memberships
763 # with memberships
764 assert_equal 6, User.find(2).valid_notification_options.size
764 assert_equal 6, User.find(2).valid_notification_options.size
765 end
765 end
766
766
767 def test_valid_notification_options_class_method
767 def test_valid_notification_options_class_method
768 assert_equal 5, User.valid_notification_options.size
768 assert_equal 5, User.valid_notification_options.size
769 assert_equal 5, User.valid_notification_options(User.find(7)).size
769 assert_equal 5, User.valid_notification_options(User.find(7)).size
770 assert_equal 6, User.valid_notification_options(User.find(2)).size
770 assert_equal 6, User.valid_notification_options(User.find(2)).size
771 end
771 end
772
772
773 def test_mail_notification_all
773 def test_mail_notification_all
774 @jsmith.mail_notification = 'all'
774 @jsmith.mail_notification = 'all'
775 @jsmith.notified_project_ids = []
775 @jsmith.notified_project_ids = []
776 @jsmith.save
776 @jsmith.save
777 @jsmith.reload
777 @jsmith.reload
778 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
778 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
779 end
779 end
780
780
781 def test_mail_notification_selected
781 def test_mail_notification_selected
782 @jsmith.mail_notification = 'selected'
782 @jsmith.mail_notification = 'selected'
783 @jsmith.notified_project_ids = [1]
783 @jsmith.notified_project_ids = [1]
784 @jsmith.save
784 @jsmith.save
785 @jsmith.reload
785 @jsmith.reload
786 assert Project.find(1).recipients.include?(@jsmith.mail)
786 assert Project.find(1).recipients.include?(@jsmith.mail)
787 end
787 end
788
788
789 def test_mail_notification_only_my_events
789 def test_mail_notification_only_my_events
790 @jsmith.mail_notification = 'only_my_events'
790 @jsmith.mail_notification = 'only_my_events'
791 @jsmith.notified_project_ids = []
791 @jsmith.notified_project_ids = []
792 @jsmith.save
792 @jsmith.save
793 @jsmith.reload
793 @jsmith.reload
794 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
794 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
795 end
795 end
796
796
797 def test_comments_sorting_preference
797 def test_comments_sorting_preference
798 assert !@jsmith.wants_comments_in_reverse_order?
798 assert !@jsmith.wants_comments_in_reverse_order?
799 @jsmith.pref.comments_sorting = 'asc'
799 @jsmith.pref.comments_sorting = 'asc'
800 assert !@jsmith.wants_comments_in_reverse_order?
800 assert !@jsmith.wants_comments_in_reverse_order?
801 @jsmith.pref.comments_sorting = 'desc'
801 @jsmith.pref.comments_sorting = 'desc'
802 assert @jsmith.wants_comments_in_reverse_order?
802 assert @jsmith.wants_comments_in_reverse_order?
803 end
803 end
804
804
805 def test_find_by_mail_should_be_case_insensitive
805 def test_find_by_mail_should_be_case_insensitive
806 u = User.find_by_mail('JSmith@somenet.foo')
806 u = User.find_by_mail('JSmith@somenet.foo')
807 assert_not_nil u
807 assert_not_nil u
808 assert_equal 'jsmith@somenet.foo', u.mail
808 assert_equal 'jsmith@somenet.foo', u.mail
809 end
809 end
810
810
811 def test_random_password
811 def test_random_password
812 u = User.new
812 u = User.new
813 u.random_password
813 u.random_password
814 assert !u.password.blank?
814 assert !u.password.blank?
815 assert !u.password_confirmation.blank?
815 assert !u.password_confirmation.blank?
816 end
816 end
817
817
818 context "#change_password_allowed?" do
818 context "#change_password_allowed?" do
819 should "be allowed if no auth source is set" do
819 should "be allowed if no auth source is set" do
820 user = User.generate!
820 user = User.generate!
821 assert user.change_password_allowed?
821 assert user.change_password_allowed?
822 end
822 end
823
823
824 should "delegate to the auth source" do
824 should "delegate to the auth source" do
825 user = User.generate!
825 user = User.generate!
826
826
827 allowed_auth_source = AuthSource.generate!
827 allowed_auth_source = AuthSource.generate!
828 def allowed_auth_source.allow_password_changes?; true; end
828 def allowed_auth_source.allow_password_changes?; true; end
829
829
830 denied_auth_source = AuthSource.generate!
830 denied_auth_source = AuthSource.generate!
831 def denied_auth_source.allow_password_changes?; false; end
831 def denied_auth_source.allow_password_changes?; false; end
832
832
833 assert user.change_password_allowed?
833 assert user.change_password_allowed?
834
834
835 user.auth_source = allowed_auth_source
835 user.auth_source = allowed_auth_source
836 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
836 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
837
837
838 user.auth_source = denied_auth_source
838 user.auth_source = denied_auth_source
839 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
839 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
840 end
840 end
841 end
841 end
842
842
843 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
843 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
844 with_settings :unsubscribe => '1' do
844 with_settings :unsubscribe => '1' do
845 assert_equal true, User.find(2).own_account_deletable?
845 assert_equal true, User.find(2).own_account_deletable?
846 end
846 end
847 end
847 end
848
848
849 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
849 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
850 with_settings :unsubscribe => '0' do
850 with_settings :unsubscribe => '0' do
851 assert_equal false, User.find(2).own_account_deletable?
851 assert_equal false, User.find(2).own_account_deletable?
852 end
852 end
853 end
853 end
854
854
855 def test_own_account_deletable_should_be_false_for_a_single_admin
855 def test_own_account_deletable_should_be_false_for_a_single_admin
856 User.delete_all(["admin = ? AND id <> ?", true, 1])
856 User.delete_all(["admin = ? AND id <> ?", true, 1])
857
857
858 with_settings :unsubscribe => '1' do
858 with_settings :unsubscribe => '1' do
859 assert_equal false, User.find(1).own_account_deletable?
859 assert_equal false, User.find(1).own_account_deletable?
860 end
860 end
861 end
861 end
862
862
863 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
863 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
864 User.generate! do |user|
864 User.generate! do |user|
865 user.admin = true
865 user.admin = true
866 end
866 end
867
867
868 with_settings :unsubscribe => '1' do
868 with_settings :unsubscribe => '1' do
869 assert_equal true, User.find(1).own_account_deletable?
869 assert_equal true, User.find(1).own_account_deletable?
870 end
870 end
871 end
871 end
872
872
873 context "#allowed_to?" do
873 context "#allowed_to?" do
874 context "with a unique project" do
874 context "with a unique project" do
875 should "return false if project is archived" do
875 should "return false if project is archived" do
876 project = Project.find(1)
876 project = Project.find(1)
877 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
877 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
878 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
878 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
879 end
879 end
880
880
881 should "return false for write action if project is closed" do
881 should "return false for write action if project is closed" do
882 project = Project.find(1)
882 project = Project.find(1)
883 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
883 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
884 assert ! @admin.allowed_to?(:edit_project, Project.find(1))
884 assert ! @admin.allowed_to?(:edit_project, Project.find(1))
885 end
885 end
886
886
887 should "return true for read action if project is closed" do
887 should "return true for read action if project is closed" do
888 project = Project.find(1)
888 project = Project.find(1)
889 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
889 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
890 assert @admin.allowed_to?(:view_project, Project.find(1))
890 assert @admin.allowed_to?(:view_project, Project.find(1))
891 end
891 end
892
892
893 should "return false if related module is disabled" do
893 should "return false if related module is disabled" do
894 project = Project.find(1)
894 project = Project.find(1)
895 project.enabled_module_names = ["issue_tracking"]
895 project.enabled_module_names = ["issue_tracking"]
896 assert @admin.allowed_to?(:add_issues, project)
896 assert @admin.allowed_to?(:add_issues, project)
897 assert ! @admin.allowed_to?(:view_wiki_pages, project)
897 assert ! @admin.allowed_to?(:view_wiki_pages, project)
898 end
898 end
899
899
900 should "authorize nearly everything for admin users" do
900 should "authorize nearly everything for admin users" do
901 project = Project.find(1)
901 project = Project.find(1)
902 assert ! @admin.member_of?(project)
902 assert ! @admin.member_of?(project)
903 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
903 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
904 assert @admin.allowed_to?(p.to_sym, project)
904 assert @admin.allowed_to?(p.to_sym, project)
905 end
905 end
906 end
906 end
907
907
908 should "authorize normal users depending on their roles" do
908 should "authorize normal users depending on their roles" do
909 project = Project.find(1)
909 project = Project.find(1)
910 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
910 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
911 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
911 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
912 end
912 end
913 end
913 end
914
914
915 context "with multiple projects" do
915 context "with multiple projects" do
916 should "return false if array is empty" do
916 should "return false if array is empty" do
917 assert ! @admin.allowed_to?(:view_project, [])
917 assert ! @admin.allowed_to?(:view_project, [])
918 end
918 end
919
919
920 should "return true only if user has permission on all these projects" do
920 should "return true only if user has permission on all these projects" do
921 assert @admin.allowed_to?(:view_project, Project.all)
921 assert @admin.allowed_to?(:view_project, Project.all)
922 assert ! @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
922 assert ! @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
923 assert @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
923 assert @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
924 assert ! @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
924 assert ! @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
925 end
925 end
926
926
927 should "behave correctly with arrays of 1 project" do
927 should "behave correctly with arrays of 1 project" do
928 assert ! User.anonymous.allowed_to?(:delete_issues, [Project.first])
928 assert ! User.anonymous.allowed_to?(:delete_issues, [Project.first])
929 end
929 end
930 end
930 end
931
931
932 context "with options[:global]" do
932 context "with options[:global]" do
933 should "authorize if user has at least one role that has this permission" do
933 should "authorize if user has at least one role that has this permission" do
934 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
934 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
935 @anonymous = User.find(6)
935 @anonymous = User.find(6)
936 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
936 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
937 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
937 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
938 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
938 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
939 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
939 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
940 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
940 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
941 end
941 end
942 end
942 end
943 end
943 end
944
944
945 context "User#notify_about?" do
945 context "User#notify_about?" do
946 context "Issues" do
946 context "Issues" do
947 setup do
947 setup do
948 @project = Project.find(1)
948 @project = Project.find(1)
949 @author = User.generate!
949 @author = User.generate!
950 @assignee = User.generate!
950 @assignee = User.generate!
951 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
951 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
952 end
952 end
953
953
954 should "be true for a user with :all" do
954 should "be true for a user with :all" do
955 @author.update_attribute(:mail_notification, 'all')
955 @author.update_attribute(:mail_notification, 'all')
956 assert @author.notify_about?(@issue)
956 assert @author.notify_about?(@issue)
957 end
957 end
958
958
959 should "be false for a user with :none" do
959 should "be false for a user with :none" do
960 @author.update_attribute(:mail_notification, 'none')
960 @author.update_attribute(:mail_notification, 'none')
961 assert ! @author.notify_about?(@issue)
961 assert ! @author.notify_about?(@issue)
962 end
962 end
963
963
964 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
964 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
965 @user = User.generate!(:mail_notification => 'only_my_events')
965 @user = User.generate!(:mail_notification => 'only_my_events')
966 Member.create!(:user => @user, :project => @project, :role_ids => [1])
966 Member.create!(:user => @user, :project => @project, :role_ids => [1])
967 assert ! @user.notify_about?(@issue)
967 assert ! @user.notify_about?(@issue)
968 end
968 end
969
969
970 should "be true for a user with :only_my_events and is the author" do
970 should "be true for a user with :only_my_events and is the author" do
971 @author.update_attribute(:mail_notification, 'only_my_events')
971 @author.update_attribute(:mail_notification, 'only_my_events')
972 assert @author.notify_about?(@issue)
972 assert @author.notify_about?(@issue)
973 end
973 end
974
974
975 should "be true for a user with :only_my_events and is the assignee" do
975 should "be true for a user with :only_my_events and is the assignee" do
976 @assignee.update_attribute(:mail_notification, 'only_my_events')
976 @assignee.update_attribute(:mail_notification, 'only_my_events')
977 assert @assignee.notify_about?(@issue)
977 assert @assignee.notify_about?(@issue)
978 end
978 end
979
979
980 should "be true for a user with :only_assigned and is the assignee" do
980 should "be true for a user with :only_assigned and is the assignee" do
981 @assignee.update_attribute(:mail_notification, 'only_assigned')
981 @assignee.update_attribute(:mail_notification, 'only_assigned')
982 assert @assignee.notify_about?(@issue)
982 assert @assignee.notify_about?(@issue)
983 end
983 end
984
984
985 should "be false for a user with :only_assigned and is not the assignee" do
985 should "be false for a user with :only_assigned and is not the assignee" do
986 @author.update_attribute(:mail_notification, 'only_assigned')
986 @author.update_attribute(:mail_notification, 'only_assigned')
987 assert ! @author.notify_about?(@issue)
987 assert ! @author.notify_about?(@issue)
988 end
988 end
989
989
990 should "be true for a user with :only_owner and is the author" do
990 should "be true for a user with :only_owner and is the author" do
991 @author.update_attribute(:mail_notification, 'only_owner')
991 @author.update_attribute(:mail_notification, 'only_owner')
992 assert @author.notify_about?(@issue)
992 assert @author.notify_about?(@issue)
993 end
993 end
994
994
995 should "be false for a user with :only_owner and is not the author" do
995 should "be false for a user with :only_owner and is not the author" do
996 @assignee.update_attribute(:mail_notification, 'only_owner')
996 @assignee.update_attribute(:mail_notification, 'only_owner')
997 assert ! @assignee.notify_about?(@issue)
997 assert ! @assignee.notify_about?(@issue)
998 end
998 end
999
999
1000 should "be true for a user with :selected and is the author" do
1000 should "be true for a user with :selected and is the author" do
1001 @author.update_attribute(:mail_notification, 'selected')
1001 @author.update_attribute(:mail_notification, 'selected')
1002 assert @author.notify_about?(@issue)
1002 assert @author.notify_about?(@issue)
1003 end
1003 end
1004
1004
1005 should "be true for a user with :selected and is the assignee" do
1005 should "be true for a user with :selected and is the assignee" do
1006 @assignee.update_attribute(:mail_notification, 'selected')
1006 @assignee.update_attribute(:mail_notification, 'selected')
1007 assert @assignee.notify_about?(@issue)
1007 assert @assignee.notify_about?(@issue)
1008 end
1008 end
1009
1009
1010 should "be false for a user with :selected and is not the author or assignee" do
1010 should "be false for a user with :selected and is not the author or assignee" do
1011 @user = User.generate!(:mail_notification => 'selected')
1011 @user = User.generate!(:mail_notification => 'selected')
1012 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1012 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1013 assert ! @user.notify_about?(@issue)
1013 assert ! @user.notify_about?(@issue)
1014 end
1014 end
1015 end
1015 end
1016
1016
1017 context "other events" do
1017 context "other events" do
1018 should 'be added and tested'
1018 should 'be added and tested'
1019 end
1019 end
1020 end
1020 end
1021
1021
1022 def test_salt_unsalted_passwords
1022 def test_salt_unsalted_passwords
1023 # Restore a user with an unsalted password
1023 # Restore a user with an unsalted password
1024 user = User.find(1)
1024 user = User.find(1)
1025 user.salt = nil
1025 user.salt = nil
1026 user.hashed_password = User.hash_password("unsalted")
1026 user.hashed_password = User.hash_password("unsalted")
1027 user.save!
1027 user.save!
1028
1028
1029 User.salt_unsalted_passwords!
1029 User.salt_unsalted_passwords!
1030
1030
1031 user.reload
1031 user.reload
1032 # Salt added
1032 # Salt added
1033 assert !user.salt.blank?
1033 assert !user.salt.blank?
1034 # Password still valid
1034 # Password still valid
1035 assert user.check_password?("unsalted")
1035 assert user.check_password?("unsalted")
1036 assert_equal user, User.try_to_login(user.login, "unsalted")
1036 assert_equal user, User.try_to_login(user.login, "unsalted")
1037 end
1037 end
1038
1038
1039 if Object.const_defined?(:OpenID)
1039 if Object.const_defined?(:OpenID)
1040
1040
1041 def test_setting_identity_url
1041 def test_setting_identity_url
1042 normalized_open_id_url = 'http://example.com/'
1042 normalized_open_id_url = 'http://example.com/'
1043 u = User.new( :identity_url => 'http://example.com/' )
1043 u = User.new( :identity_url => 'http://example.com/' )
1044 assert_equal normalized_open_id_url, u.identity_url
1044 assert_equal normalized_open_id_url, u.identity_url
1045 end
1045 end
1046
1046
1047 def test_setting_identity_url_without_trailing_slash
1047 def test_setting_identity_url_without_trailing_slash
1048 normalized_open_id_url = 'http://example.com/'
1048 normalized_open_id_url = 'http://example.com/'
1049 u = User.new( :identity_url => 'http://example.com' )
1049 u = User.new( :identity_url => 'http://example.com' )
1050 assert_equal normalized_open_id_url, u.identity_url
1050 assert_equal normalized_open_id_url, u.identity_url
1051 end
1051 end
1052
1052
1053 def test_setting_identity_url_without_protocol
1053 def test_setting_identity_url_without_protocol
1054 normalized_open_id_url = 'http://example.com/'
1054 normalized_open_id_url = 'http://example.com/'
1055 u = User.new( :identity_url => 'example.com' )
1055 u = User.new( :identity_url => 'example.com' )
1056 assert_equal normalized_open_id_url, u.identity_url
1056 assert_equal normalized_open_id_url, u.identity_url
1057 end
1057 end
1058
1058
1059 def test_setting_blank_identity_url
1059 def test_setting_blank_identity_url
1060 u = User.new( :identity_url => 'example.com' )
1060 u = User.new( :identity_url => 'example.com' )
1061 u.identity_url = ''
1061 u.identity_url = ''
1062 assert u.identity_url.blank?
1062 assert u.identity_url.blank?
1063 end
1063 end
1064
1064
1065 def test_setting_invalid_identity_url
1065 def test_setting_invalid_identity_url
1066 u = User.new( :identity_url => 'this is not an openid url' )
1066 u = User.new( :identity_url => 'this is not an openid url' )
1067 assert u.identity_url.blank?
1067 assert u.identity_url.blank?
1068 end
1068 end
1069
1069
1070 else
1070 else
1071 puts "Skipping openid tests."
1071 puts "Skipping openid tests."
1072 end
1072 end
1073
1073
1074 end
1074 end
General Comments 0
You need to be logged in to leave comments. Login now