##// END OF EJS Templates
Fixed that Project.uniq.visible errors under certain conditions (#21182)....
Jean-Philippe Lang -
r14475:151892c3131a
parent child
Show More
@@ -1,185 +1,184
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Principal < ActiveRecord::Base
19 19 self.table_name = "#{table_name_prefix}users#{table_name_suffix}"
20 20
21 21 # Account statuses
22 22 STATUS_ANONYMOUS = 0
23 23 STATUS_ACTIVE = 1
24 24 STATUS_REGISTERED = 2
25 25 STATUS_LOCKED = 3
26 26
27 27 has_many :members, :foreign_key => 'user_id', :dependent => :destroy
28 28 has_many :memberships,
29 29 lambda {preload(:project, :roles).
30 30 joins(:project).
31 where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}").
32 order("#{Project.table_name}.name")},
31 where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}")},
33 32 :class_name => 'Member',
34 33 :foreign_key => 'user_id'
35 34 has_many :projects, :through => :memberships
36 35 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
37 36
38 37 # Groups and active users
39 38 scope :active, lambda { where(:status => STATUS_ACTIVE) }
40 39
41 40 scope :visible, lambda {|*args|
42 41 user = args.first || User.current
43 42
44 43 if user.admin?
45 44 all
46 45 else
47 46 view_all_active = false
48 47 if user.memberships.to_a.any?
49 48 view_all_active = user.memberships.any? {|m| m.roles.any? {|r| r.users_visibility == 'all'}}
50 49 else
51 50 view_all_active = user.builtin_role.users_visibility == 'all'
52 51 end
53 52
54 53 if view_all_active
55 54 active
56 55 else
57 56 # self and members of visible projects
58 57 active.where("#{table_name}.id = ? OR #{table_name}.id IN (SELECT user_id FROM #{Member.table_name} WHERE project_id IN (?))",
59 58 user.id, user.visible_project_ids
60 59 )
61 60 end
62 61 end
63 62 }
64 63
65 64 scope :like, lambda {|q|
66 65 q = q.to_s
67 66 if q.blank?
68 67 where({})
69 68 else
70 69 pattern = "%#{q}%"
71 70 sql = %w(login firstname lastname).map {|column| "LOWER(#{table_name}.#{column}) LIKE LOWER(:p)"}.join(" OR ")
72 71 sql << " OR #{table_name}.id IN (SELECT user_id FROM #{EmailAddress.table_name} WHERE LOWER(address) LIKE LOWER(:p))"
73 72 params = {:p => pattern}
74 73 if q =~ /^(.+)\s+(.+)$/
75 74 a, b = "#{$1}%", "#{$2}%"
76 75 sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:a) AND LOWER(#{table_name}.lastname) LIKE LOWER(:b))"
77 76 sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:b) AND LOWER(#{table_name}.lastname) LIKE LOWER(:a))"
78 77 params.merge!(:a => a, :b => b)
79 78 end
80 79 where(sql, params)
81 80 end
82 81 }
83 82
84 83 # Principals that are members of a collection of projects
85 84 scope :member_of, lambda {|projects|
86 85 projects = [projects] if projects.is_a?(Project)
87 86 if projects.blank?
88 87 where("1=0")
89 88 else
90 89 ids = projects.map(&:id)
91 90 active.where("#{Principal.table_name}.id IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids)
92 91 end
93 92 }
94 93 # Principals that are not members of projects
95 94 scope :not_member_of, lambda {|projects|
96 95 projects = [projects] unless projects.is_a?(Array)
97 96 if projects.empty?
98 97 where("1=0")
99 98 else
100 99 ids = projects.map(&:id)
101 100 where("#{Principal.table_name}.id NOT IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids)
102 101 end
103 102 }
104 103 scope :sorted, lambda { order(*Principal.fields_for_order_statement)}
105 104
106 105 before_create :set_default_empty_values
107 106
108 107 def name(formatter = nil)
109 108 to_s
110 109 end
111 110
112 111 def mail=(*args)
113 112 nil
114 113 end
115 114
116 115 def mail
117 116 nil
118 117 end
119 118
120 119 def visible?(user=User.current)
121 120 Principal.visible(user).where(:id => id).first == self
122 121 end
123 122
124 123 # Return true if the principal is a member of project
125 124 def member_of?(project)
126 125 projects.to_a.include?(project)
127 126 end
128 127
129 128 def <=>(principal)
130 129 if principal.nil?
131 130 -1
132 131 elsif self.class.name == principal.class.name
133 132 self.to_s.casecmp(principal.to_s)
134 133 else
135 134 # groups after users
136 135 principal.class.name <=> self.class.name
137 136 end
138 137 end
139 138
140 139 # Returns an array of fields names than can be used to make an order statement for principals.
141 140 # Users are sorted before Groups.
142 141 # Examples:
143 142 def self.fields_for_order_statement(table=nil)
144 143 table ||= table_name
145 144 columns = ['type DESC'] + (User.name_formatter[:order] - ['id']) + ['lastname', 'id']
146 145 columns.uniq.map {|field| "#{table}.#{field}"}
147 146 end
148 147
149 148 # Returns the principal that matches the keyword among principals
150 149 def self.detect_by_keyword(principals, keyword)
151 150 keyword = keyword.to_s
152 151 return nil if keyword.blank?
153 152
154 153 principal = nil
155 154 principal ||= principals.detect {|a| keyword.casecmp(a.login.to_s) == 0}
156 155 principal ||= principals.detect {|a| keyword.casecmp(a.mail.to_s) == 0}
157 156
158 157 if principal.nil? && keyword.match(/ /)
159 158 firstname, lastname = *(keyword.split) # "First Last Throwaway"
160 159 principal ||= principals.detect {|a|
161 160 a.is_a?(User) &&
162 161 firstname.casecmp(a.firstname.to_s) == 0 &&
163 162 lastname.casecmp(a.lastname.to_s) == 0
164 163 }
165 164 end
166 165 if principal.nil?
167 166 principal ||= principals.detect {|a| keyword.casecmp(a.name) == 0}
168 167 end
169 168 principal
170 169 end
171 170
172 171 protected
173 172
174 173 # Make sure we don't try to insert NULL values (see #4632)
175 174 def set_default_empty_values
176 175 self.login ||= ''
177 176 self.hashed_password ||= ''
178 177 self.firstname ||= ''
179 178 self.lastname ||= ''
180 179 true
181 180 end
182 181 end
183 182
184 183 require_dependency "user"
185 184 require_dependency "group"
@@ -1,994 +1,1003
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class ProjectTest < ActiveSupport::TestCase
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :journals, :journal_details,
23 23 :enumerations, :users, :issue_categories,
24 24 :projects_trackers,
25 25 :custom_fields,
26 26 :custom_fields_projects,
27 27 :custom_fields_trackers,
28 28 :custom_values,
29 29 :roles,
30 30 :member_roles,
31 31 :members,
32 32 :enabled_modules,
33 33 :versions,
34 34 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
35 35 :groups_users,
36 36 :boards, :messages,
37 37 :repositories,
38 38 :news, :comments,
39 39 :documents,
40 40 :workflows
41 41
42 42 def setup
43 43 @ecookbook = Project.find(1)
44 44 @ecookbook_sub1 = Project.find(3)
45 45 set_tmp_attachments_directory
46 46 User.current = nil
47 47 end
48 48
49 49 def test_truth
50 50 assert_kind_of Project, @ecookbook
51 51 assert_equal "eCookbook", @ecookbook.name
52 52 end
53 53
54 54 def test_default_attributes
55 55 with_settings :default_projects_public => '1' do
56 56 assert_equal true, Project.new.is_public
57 57 assert_equal false, Project.new(:is_public => false).is_public
58 58 end
59 59
60 60 with_settings :default_projects_public => '0' do
61 61 assert_equal false, Project.new.is_public
62 62 assert_equal true, Project.new(:is_public => true).is_public
63 63 end
64 64
65 65 with_settings :sequential_project_identifiers => '1' do
66 66 assert !Project.new.identifier.blank?
67 67 assert Project.new(:identifier => '').identifier.blank?
68 68 end
69 69
70 70 with_settings :sequential_project_identifiers => '0' do
71 71 assert Project.new.identifier.blank?
72 72 assert !Project.new(:identifier => 'test').blank?
73 73 end
74 74
75 75 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
76 76 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
77 77 end
78 78 end
79 79
80 80 def test_default_trackers_should_match_default_tracker_ids_setting
81 81 with_settings :default_projects_tracker_ids => ['1', '3'] do
82 82 assert_equal Tracker.find(1, 3).sort, Project.new.trackers.sort
83 83 end
84 84 end
85 85
86 86 def test_default_trackers_should_be_all_trackers_with_blank_setting
87 87 with_settings :default_projects_tracker_ids => nil do
88 88 assert_equal Tracker.all.sort, Project.new.trackers.sort
89 89 end
90 90 end
91 91
92 92 def test_default_trackers_should_be_empty_with_empty_setting
93 93 with_settings :default_projects_tracker_ids => [] do
94 94 assert_equal [], Project.new.trackers
95 95 end
96 96 end
97 97
98 98 def test_default_trackers_should_not_replace_initialized_trackers
99 99 with_settings :default_projects_tracker_ids => ['1', '3'] do
100 100 assert_equal Tracker.find(1, 2).sort, Project.new(:tracker_ids => [1, 2]).trackers.sort
101 101 end
102 102 end
103 103
104 104 def test_update
105 105 assert_equal "eCookbook", @ecookbook.name
106 106 @ecookbook.name = "eCook"
107 107 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
108 108 @ecookbook.reload
109 109 assert_equal "eCook", @ecookbook.name
110 110 end
111 111
112 112 def test_validate_identifier
113 113 to_test = {"abc" => true,
114 114 "ab12" => true,
115 115 "ab-12" => true,
116 116 "ab_12" => true,
117 117 "12" => false,
118 118 "new" => false}
119 119
120 120 to_test.each do |identifier, valid|
121 121 p = Project.new
122 122 p.identifier = identifier
123 123 p.valid?
124 124 if valid
125 125 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
126 126 else
127 127 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
128 128 end
129 129 end
130 130 end
131 131
132 132 def test_identifier_should_not_be_frozen_for_a_new_project
133 133 assert_equal false, Project.new.identifier_frozen?
134 134 end
135 135
136 136 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
137 137 Project.where(:id => 1).update_all(["identifier = ''"])
138 138 assert_equal false, Project.find(1).identifier_frozen?
139 139 end
140 140
141 141 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
142 142 assert_equal true, Project.find(1).identifier_frozen?
143 143 end
144 144
145 145 def test_to_param_should_be_nil_for_new_records
146 146 project = Project.new
147 147 project.identifier = "foo"
148 148 assert_nil project.to_param
149 149 end
150 150
151 151 def test_members_should_be_active_users
152 152 Project.all.each do |project|
153 153 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
154 154 end
155 155 end
156 156
157 157 def test_users_should_be_active_users
158 158 Project.all.each do |project|
159 159 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
160 160 end
161 161 end
162 162
163 163 def test_open_scope_on_issues_association
164 164 assert_kind_of Issue, Project.find(1).issues.open.first
165 165 end
166 166
167 167 def test_archive
168 168 user = @ecookbook.members.first.user
169 169 @ecookbook.archive
170 170 @ecookbook.reload
171 171
172 172 assert !@ecookbook.active?
173 173 assert @ecookbook.archived?
174 174 assert !user.projects.include?(@ecookbook)
175 175 # Subproject are also archived
176 176 assert !@ecookbook.children.empty?
177 177 assert @ecookbook.descendants.active.empty?
178 178 end
179 179
180 180 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
181 181 # Assign an issue of a project to a version of a child project
182 182 Issue.find(4).update_attribute :fixed_version_id, 4
183 183
184 184 assert_no_difference "Project.where(:status => Project::STATUS_ARCHIVED).count" do
185 185 assert_equal false, @ecookbook.archive
186 186 end
187 187 @ecookbook.reload
188 188 assert @ecookbook.active?
189 189 end
190 190
191 191 def test_unarchive
192 192 user = @ecookbook.members.first.user
193 193 @ecookbook.archive
194 194 # A subproject of an archived project can not be unarchived
195 195 assert !@ecookbook_sub1.unarchive
196 196
197 197 # Unarchive project
198 198 assert @ecookbook.unarchive
199 199 @ecookbook.reload
200 200 assert @ecookbook.active?
201 201 assert !@ecookbook.archived?
202 202 assert user.projects.include?(@ecookbook)
203 203 # Subproject can now be unarchived
204 204 @ecookbook_sub1.reload
205 205 assert @ecookbook_sub1.unarchive
206 206 end
207 207
208 208 def test_destroy
209 209 # 2 active members
210 210 assert_equal 2, @ecookbook.members.size
211 211 # and 1 is locked
212 212 assert_equal 3, Member.where(:project_id => @ecookbook.id).count
213 213 # some boards
214 214 assert @ecookbook.boards.any?
215 215
216 216 @ecookbook.destroy
217 217 # make sure that the project non longer exists
218 218 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
219 219 # make sure related data was removed
220 220 assert_nil Member.where(:project_id => @ecookbook.id).first
221 221 assert_nil Board.where(:project_id => @ecookbook.id).first
222 222 assert_nil Issue.where(:project_id => @ecookbook.id).first
223 223 end
224 224
225 225 def test_destroy_should_destroy_subtasks
226 226 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
227 227 issues[0].update_attribute :parent_issue_id, issues[1].id
228 228 issues[2].update_attribute :parent_issue_id, issues[1].id
229 229 assert_equal 2, issues[1].children.count
230 230
231 231 assert_nothing_raised do
232 232 Project.find(1).destroy
233 233 end
234 234 assert_equal 0, Issue.where(:id => issues.map(&:id)).count
235 235 end
236 236
237 237 def test_destroying_root_projects_should_clear_data
238 238 Project.roots.each do |root|
239 239 root.destroy
240 240 end
241 241
242 242 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
243 243 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
244 244 assert_equal 0, MemberRole.count
245 245 assert_equal 0, Issue.count
246 246 assert_equal 0, Journal.count
247 247 assert_equal 0, JournalDetail.count
248 248 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
249 249 assert_equal 0, EnabledModule.count
250 250 assert_equal 0, IssueCategory.count
251 251 assert_equal 0, IssueRelation.count
252 252 assert_equal 0, Board.count
253 253 assert_equal 0, Message.count
254 254 assert_equal 0, News.count
255 255 assert_equal 0, Query.where("project_id IS NOT NULL").count
256 256 assert_equal 0, Repository.count
257 257 assert_equal 0, Changeset.count
258 258 assert_equal 0, Change.count
259 259 assert_equal 0, Comment.count
260 260 assert_equal 0, TimeEntry.count
261 261 assert_equal 0, Version.count
262 262 assert_equal 0, Watcher.count
263 263 assert_equal 0, Wiki.count
264 264 assert_equal 0, WikiPage.count
265 265 assert_equal 0, WikiContent.count
266 266 assert_equal 0, WikiContent::Version.count
267 267 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").count
268 268 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").count
269 269 assert_equal 0, CustomValue.where(:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']).count
270 270 end
271 271
272 272 def test_destroy_should_delete_time_entries_custom_values
273 273 project = Project.generate!
274 274 time_entry = TimeEntry.generate!(:project => project, :custom_field_values => {10 => '1'})
275 275
276 276 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
277 277 assert project.destroy
278 278 end
279 279 end
280 280
281 281 def test_move_an_orphan_project_to_a_root_project
282 282 sub = Project.find(2)
283 283 sub.set_parent! @ecookbook
284 284 assert_equal @ecookbook.id, sub.parent.id
285 285 @ecookbook.reload
286 286 assert_equal 4, @ecookbook.children.size
287 287 end
288 288
289 289 def test_move_an_orphan_project_to_a_subproject
290 290 sub = Project.find(2)
291 291 assert sub.set_parent!(@ecookbook_sub1)
292 292 end
293 293
294 294 def test_move_a_root_project_to_a_project
295 295 sub = @ecookbook
296 296 assert sub.set_parent!(Project.find(2))
297 297 end
298 298
299 299 def test_should_not_move_a_project_to_its_children
300 300 sub = @ecookbook
301 301 assert !(sub.set_parent!(Project.find(3)))
302 302 end
303 303
304 304 def test_set_parent_should_add_roots_in_alphabetical_order
305 305 ProjectCustomField.delete_all
306 306 Project.delete_all
307 307 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
308 308 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
309 309 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
310 310 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
311 311
312 312 assert_equal 4, Project.count
313 313 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
314 314 end
315 315
316 316 def test_set_parent_should_add_children_in_alphabetical_order
317 317 ProjectCustomField.delete_all
318 318 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
319 319 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
320 320 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
321 321 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
322 322 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
323 323
324 324 parent.reload
325 325 assert_equal 4, parent.children.size
326 326 assert_equal parent.children.sort_by(&:name), parent.children.to_a
327 327 end
328 328
329 329 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
330 330 # Parent issue with a hierarchy project's fixed version
331 331 parent_issue = Issue.find(1)
332 332 parent_issue.update_attribute(:fixed_version_id, 4)
333 333 parent_issue.reload
334 334 assert_equal 4, parent_issue.fixed_version_id
335 335
336 336 # Should keep fixed versions for the issues
337 337 issue_with_local_fixed_version = Issue.find(5)
338 338 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
339 339 issue_with_local_fixed_version.reload
340 340 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
341 341
342 342 # Local issue with hierarchy fixed_version
343 343 issue_with_hierarchy_fixed_version = Issue.find(13)
344 344 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
345 345 issue_with_hierarchy_fixed_version.reload
346 346 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
347 347
348 348 # Move project out of the issue's hierarchy
349 349 moved_project = Project.find(3)
350 350 moved_project.set_parent!(Project.find(2))
351 351 parent_issue.reload
352 352 issue_with_local_fixed_version.reload
353 353 issue_with_hierarchy_fixed_version.reload
354 354
355 355 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
356 356 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"
357 357 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
358 358 end
359 359
360 360 def test_parent
361 361 p = Project.find(6).parent
362 362 assert p.is_a?(Project)
363 363 assert_equal 5, p.id
364 364 end
365 365
366 366 def test_ancestors
367 367 a = Project.find(6).ancestors
368 368 assert a.first.is_a?(Project)
369 369 assert_equal [1, 5], a.collect(&:id)
370 370 end
371 371
372 372 def test_root
373 373 r = Project.find(6).root
374 374 assert r.is_a?(Project)
375 375 assert_equal 1, r.id
376 376 end
377 377
378 378 def test_children
379 379 c = Project.find(1).children
380 380 assert c.first.is_a?(Project)
381 381 assert_equal [5, 3, 4], c.collect(&:id)
382 382 end
383 383
384 384 def test_descendants
385 385 d = Project.find(1).descendants
386 386 assert d.first.is_a?(Project)
387 387 assert_equal [5, 6, 3, 4], d.collect(&:id)
388 388 end
389 389
390 390 def test_allowed_parents_should_be_empty_for_non_member_user
391 391 Role.non_member.add_permission!(:add_project)
392 392 user = User.find(9)
393 393 assert user.memberships.empty?
394 394 User.current = user
395 395 assert Project.new.allowed_parents.compact.empty?
396 396 end
397 397
398 398 def test_allowed_parents_with_add_subprojects_permission
399 399 Role.find(1).remove_permission!(:add_project)
400 400 Role.find(1).add_permission!(:add_subprojects)
401 401 User.current = User.find(2)
402 402 # new project
403 403 assert !Project.new.allowed_parents.include?(nil)
404 404 assert Project.new.allowed_parents.include?(Project.find(1))
405 405 # existing root project
406 406 assert Project.find(1).allowed_parents.include?(nil)
407 407 # existing child
408 408 assert Project.find(3).allowed_parents.include?(Project.find(1))
409 409 assert !Project.find(3).allowed_parents.include?(nil)
410 410 end
411 411
412 412 def test_allowed_parents_with_add_project_permission
413 413 Role.find(1).add_permission!(:add_project)
414 414 Role.find(1).remove_permission!(:add_subprojects)
415 415 User.current = User.find(2)
416 416 # new project
417 417 assert Project.new.allowed_parents.include?(nil)
418 418 assert !Project.new.allowed_parents.include?(Project.find(1))
419 419 # existing root project
420 420 assert Project.find(1).allowed_parents.include?(nil)
421 421 # existing child
422 422 assert Project.find(3).allowed_parents.include?(Project.find(1))
423 423 assert Project.find(3).allowed_parents.include?(nil)
424 424 end
425 425
426 426 def test_allowed_parents_with_add_project_and_subprojects_permission
427 427 Role.find(1).add_permission!(:add_project)
428 428 Role.find(1).add_permission!(:add_subprojects)
429 429 User.current = User.find(2)
430 430 # new project
431 431 assert Project.new.allowed_parents.include?(nil)
432 432 assert Project.new.allowed_parents.include?(Project.find(1))
433 433 # existing root project
434 434 assert Project.find(1).allowed_parents.include?(nil)
435 435 # existing child
436 436 assert Project.find(3).allowed_parents.include?(Project.find(1))
437 437 assert Project.find(3).allowed_parents.include?(nil)
438 438 end
439 439
440 440 def test_users_by_role
441 441 users_by_role = Project.find(1).users_by_role
442 442 assert_kind_of Hash, users_by_role
443 443 role = Role.find(1)
444 444 assert_kind_of Array, users_by_role[role]
445 445 assert users_by_role[role].include?(User.find(2))
446 446 end
447 447
448 448 def test_rolled_up_trackers
449 449 parent = Project.find(1)
450 450 parent.trackers = Tracker.find([1,2])
451 451 child = parent.children.find(3)
452 452
453 453 assert_equal [1, 2], parent.tracker_ids
454 454 assert_equal [2, 3], child.trackers.collect(&:id)
455 455
456 456 assert_kind_of Tracker, parent.rolled_up_trackers.first
457 457 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
458 458
459 459 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
460 460 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
461 461 end
462 462
463 463 def test_rolled_up_trackers_should_ignore_archived_subprojects
464 464 parent = Project.find(1)
465 465 parent.trackers = Tracker.find([1,2])
466 466 child = parent.children.find(3)
467 467 child.trackers = Tracker.find([1,3])
468 468 parent.children.each(&:archive)
469 469
470 470 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
471 471 end
472 472
473 473 test "#rolled_up_trackers should ignore projects with issue_tracking module disabled" do
474 474 parent = Project.generate!
475 475 parent.trackers = Tracker.find([1, 2])
476 476 child = Project.generate_with_parent!(parent)
477 477 child.trackers = Tracker.find([2, 3])
478 478
479 479 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id).sort
480 480
481 481 assert child.disable_module!(:issue_tracking)
482 482 parent.reload
483 483 assert_equal [1, 2], parent.rolled_up_trackers.collect(&:id).sort
484 484 end
485 485
486 486 test "#rolled_up_versions should include the versions for the current project" do
487 487 project = Project.generate!
488 488 parent_version_1 = Version.generate!(:project => project)
489 489 parent_version_2 = Version.generate!(:project => project)
490 490 assert_equal [parent_version_1, parent_version_2].sort,
491 491 project.rolled_up_versions.sort
492 492 end
493 493
494 494 test "#rolled_up_versions should include versions for a subproject" do
495 495 project = Project.generate!
496 496 parent_version_1 = Version.generate!(:project => project)
497 497 parent_version_2 = Version.generate!(:project => project)
498 498 subproject = Project.generate_with_parent!(project)
499 499 subproject_version = Version.generate!(:project => subproject)
500 500
501 501 assert_equal [parent_version_1, parent_version_2, subproject_version].sort,
502 502 project.rolled_up_versions.sort
503 503 end
504 504
505 505 test "#rolled_up_versions should include versions for a sub-subproject" do
506 506 project = Project.generate!
507 507 parent_version_1 = Version.generate!(:project => project)
508 508 parent_version_2 = Version.generate!(:project => project)
509 509 subproject = Project.generate_with_parent!(project)
510 510 sub_subproject = Project.generate_with_parent!(subproject)
511 511 sub_subproject_version = Version.generate!(:project => sub_subproject)
512 512 project.reload
513 513
514 514 assert_equal [parent_version_1, parent_version_2, sub_subproject_version].sort,
515 515 project.rolled_up_versions.sort
516 516 end
517 517
518 518 test "#rolled_up_versions should only check active projects" do
519 519 project = Project.generate!
520 520 parent_version_1 = Version.generate!(:project => project)
521 521 parent_version_2 = Version.generate!(:project => project)
522 522 subproject = Project.generate_with_parent!(project)
523 523 subproject_version = Version.generate!(:project => subproject)
524 524 assert subproject.archive
525 525 project.reload
526 526
527 527 assert !subproject.active?
528 528 assert_equal [parent_version_1, parent_version_2].sort,
529 529 project.rolled_up_versions.sort
530 530 end
531 531
532 532 def test_shared_versions_none_sharing
533 533 p = Project.find(5)
534 534 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
535 535 assert p.shared_versions.include?(v)
536 536 assert !p.children.first.shared_versions.include?(v)
537 537 assert !p.root.shared_versions.include?(v)
538 538 assert !p.siblings.first.shared_versions.include?(v)
539 539 assert !p.root.siblings.first.shared_versions.include?(v)
540 540 end
541 541
542 542 def test_shared_versions_descendants_sharing
543 543 p = Project.find(5)
544 544 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
545 545 assert p.shared_versions.include?(v)
546 546 assert p.children.first.shared_versions.include?(v)
547 547 assert !p.root.shared_versions.include?(v)
548 548 assert !p.siblings.first.shared_versions.include?(v)
549 549 assert !p.root.siblings.first.shared_versions.include?(v)
550 550 end
551 551
552 552 def test_shared_versions_hierarchy_sharing
553 553 p = Project.find(5)
554 554 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
555 555 assert p.shared_versions.include?(v)
556 556 assert p.children.first.shared_versions.include?(v)
557 557 assert p.root.shared_versions.include?(v)
558 558 assert !p.siblings.first.shared_versions.include?(v)
559 559 assert !p.root.siblings.first.shared_versions.include?(v)
560 560 end
561 561
562 562 def test_shared_versions_tree_sharing
563 563 p = Project.find(5)
564 564 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
565 565 assert p.shared_versions.include?(v)
566 566 assert p.children.first.shared_versions.include?(v)
567 567 assert p.root.shared_versions.include?(v)
568 568 assert p.siblings.first.shared_versions.include?(v)
569 569 assert !p.root.siblings.first.shared_versions.include?(v)
570 570 end
571 571
572 572 def test_shared_versions_system_sharing
573 573 p = Project.find(5)
574 574 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
575 575 assert p.shared_versions.include?(v)
576 576 assert p.children.first.shared_versions.include?(v)
577 577 assert p.root.shared_versions.include?(v)
578 578 assert p.siblings.first.shared_versions.include?(v)
579 579 assert p.root.siblings.first.shared_versions.include?(v)
580 580 end
581 581
582 582 def test_shared_versions
583 583 parent = Project.find(1)
584 584 child = parent.children.find(3)
585 585 private_child = parent.children.find(5)
586 586
587 587 assert_equal [1,2,3], parent.version_ids.sort
588 588 assert_equal [4], child.version_ids
589 589 assert_equal [6], private_child.version_ids
590 590 assert_equal [7], Version.where(:sharing => 'system').collect(&:id)
591 591
592 592 assert_equal 6, parent.shared_versions.size
593 593 parent.shared_versions.each do |version|
594 594 assert_kind_of Version, version
595 595 end
596 596
597 597 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
598 598 end
599 599
600 600 def test_shared_versions_should_ignore_archived_subprojects
601 601 parent = Project.find(1)
602 602 child = parent.children.find(3)
603 603 child.archive
604 604 parent.reload
605 605
606 606 assert_equal [1,2,3], parent.version_ids.sort
607 607 assert_equal [4], child.version_ids
608 608 assert !parent.shared_versions.collect(&:id).include?(4)
609 609 end
610 610
611 611 def test_shared_versions_visible_to_user
612 612 user = User.find(3)
613 613 parent = Project.find(1)
614 614 child = parent.children.find(5)
615 615
616 616 assert_equal [1,2,3], parent.version_ids.sort
617 617 assert_equal [6], child.version_ids
618 618
619 619 versions = parent.shared_versions.visible(user)
620 620
621 621 assert_equal 4, versions.size
622 622 versions.each do |version|
623 623 assert_kind_of Version, version
624 624 end
625 625
626 626 assert !versions.collect(&:id).include?(6)
627 627 end
628 628
629 629 def test_shared_versions_for_new_project_should_include_system_shared_versions
630 630 p = Project.find(5)
631 631 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
632 632
633 633 assert_include v, Project.new.shared_versions
634 634 end
635 635
636 636 def test_next_identifier
637 637 ProjectCustomField.delete_all
638 638 Project.create!(:name => 'last', :identifier => 'p2008040')
639 639 assert_equal 'p2008041', Project.next_identifier
640 640 end
641 641
642 642 def test_next_identifier_first_project
643 643 Project.delete_all
644 644 assert_nil Project.next_identifier
645 645 end
646 646
647 647 def test_enabled_module_names
648 648 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
649 649 project = Project.new
650 650
651 651 project.enabled_module_names = %w(issue_tracking news)
652 652 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
653 653 end
654 654 end
655 655
656 656 def test_enabled_modules_names_with_nil_should_clear_modules
657 657 p = Project.find(1)
658 658 p.enabled_module_names = nil
659 659 assert_equal [], p.enabled_modules
660 660 end
661 661
662 662 test "enabled_modules should define module by names and preserve ids" do
663 663 @project = Project.find(1)
664 664 # Remove one module
665 665 modules = @project.enabled_modules.slice(0..-2)
666 666 assert modules.any?
667 667 assert_difference 'EnabledModule.count', -1 do
668 668 @project.enabled_module_names = modules.collect(&:name)
669 669 end
670 670 @project.reload
671 671 # Ids should be preserved
672 672 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
673 673 end
674 674
675 675 test "enabled_modules should enable a module" do
676 676 @project = Project.find(1)
677 677 @project.enabled_module_names = []
678 678 @project.reload
679 679 assert_equal [], @project.enabled_module_names
680 680 #with string
681 681 @project.enable_module!("issue_tracking")
682 682 assert_equal ["issue_tracking"], @project.enabled_module_names
683 683 #with symbol
684 684 @project.enable_module!(:gantt)
685 685 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
686 686 #don't add a module twice
687 687 @project.enable_module!("issue_tracking")
688 688 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
689 689 end
690 690
691 691 test "enabled_modules should disable a module" do
692 692 @project = Project.find(1)
693 693 #with string
694 694 assert @project.enabled_module_names.include?("issue_tracking")
695 695 @project.disable_module!("issue_tracking")
696 696 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
697 697 #with symbol
698 698 assert @project.enabled_module_names.include?("gantt")
699 699 @project.disable_module!(:gantt)
700 700 assert ! @project.reload.enabled_module_names.include?("gantt")
701 701 #with EnabledModule object
702 702 first_module = @project.enabled_modules.first
703 703 @project.disable_module!(first_module)
704 704 assert ! @project.reload.enabled_module_names.include?(first_module.name)
705 705 end
706 706
707 707 def test_enabled_module_names_should_not_recreate_enabled_modules
708 708 project = Project.find(1)
709 709 # Remove one module
710 710 modules = project.enabled_modules.slice(0..-2)
711 711 assert modules.any?
712 712 assert_difference 'EnabledModule.count', -1 do
713 713 project.enabled_module_names = modules.collect(&:name)
714 714 end
715 715 project.reload
716 716 # Ids should be preserved
717 717 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
718 718 end
719 719
720 720 def test_copy_from_existing_project
721 721 source_project = Project.find(1)
722 722 copied_project = Project.copy_from(1)
723 723
724 724 assert copied_project
725 725 # Cleared attributes
726 726 assert copied_project.id.blank?
727 727 assert copied_project.name.blank?
728 728 assert copied_project.identifier.blank?
729 729
730 730 # Duplicated attributes
731 731 assert_equal source_project.description, copied_project.description
732 732 assert_equal source_project.trackers, copied_project.trackers
733 733
734 734 # Default attributes
735 735 assert_equal 1, copied_project.status
736 736 end
737 737
738 738 def test_copy_from_should_copy_enabled_modules
739 739 source = Project.generate!
740 740 source.enabled_module_names = %w(issue_tracking wiki)
741 741
742 742 copy = Project.copy_from(source)
743 743 copy.name = 'Copy'
744 744 copy.identifier = 'copy'
745 745 assert_difference 'EnabledModule.count', 2 do
746 746 copy.save!
747 747 end
748 748 assert_equal 2, copy.reload.enabled_modules.count
749 749 assert_equal 2, source.reload.enabled_modules.count
750 750 end
751 751
752 752 def test_activities_should_use_the_system_activities
753 753 project = Project.find(1)
754 754 assert_equal project.activities.to_a, TimeEntryActivity.where(:active => true).to_a
755 755 assert_kind_of ActiveRecord::Relation, project.activities
756 756 end
757 757
758 758
759 759 def test_activities_should_use_the_project_specific_activities
760 760 project = Project.find(1)
761 761 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
762 762 assert overridden_activity.save!
763 763
764 764 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
765 765 assert_kind_of ActiveRecord::Relation, project.activities
766 766 end
767 767
768 768 def test_activities_should_not_include_the_inactive_project_specific_activities
769 769 project = Project.find(1)
770 770 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
771 771 assert overridden_activity.save!
772 772
773 773 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
774 774 end
775 775
776 776 def test_activities_should_not_include_project_specific_activities_from_other_projects
777 777 project = Project.find(1)
778 778 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
779 779 assert overridden_activity.save!
780 780
781 781 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
782 782 end
783 783
784 784 def test_activities_should_handle_nils
785 785 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.first})
786 786 TimeEntryActivity.delete_all
787 787
788 788 # No activities
789 789 project = Project.find(1)
790 790 assert project.activities.empty?
791 791
792 792 # No system, one overridden
793 793 assert overridden_activity.save!
794 794 project.reload
795 795 assert_equal [overridden_activity], project.activities
796 796 end
797 797
798 798 def test_activities_should_override_system_activities_with_project_activities
799 799 project = Project.find(1)
800 800 parent_activity = TimeEntryActivity.first
801 801 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
802 802 assert overridden_activity.save!
803 803
804 804 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
805 805 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
806 806 end
807 807
808 808 def test_activities_should_include_inactive_activities_if_specified
809 809 project = Project.find(1)
810 810 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
811 811 assert overridden_activity.save!
812 812
813 813 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
814 814 end
815 815
816 816 test 'activities should not include active System activities if the project has an override that is inactive' do
817 817 project = Project.find(1)
818 818 system_activity = TimeEntryActivity.find_by_name('Design')
819 819 assert system_activity.active?
820 820 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
821 821 assert overridden_activity.save!
822 822
823 823 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
824 824 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
825 825 end
826 826
827 827 def test_close_completed_versions
828 828 Version.update_all("status = 'open'")
829 829 project = Project.find(1)
830 830 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
831 831 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
832 832 project.close_completed_versions
833 833 project.reload
834 834 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
835 835 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
836 836 end
837 837
838 838 test "#start_date should be nil if there are no issues on the project" do
839 839 project = Project.generate!
840 840 assert_nil project.start_date
841 841 end
842 842
843 843 test "#start_date should be nil when issues have no start date" do
844 844 project = Project.generate!
845 845 project.trackers << Tracker.generate!
846 846 early = 7.days.ago.to_date
847 847 Issue.generate!(:project => project, :start_date => nil)
848 848
849 849 assert_nil project.start_date
850 850 end
851 851
852 852 test "#start_date should be the earliest start date of it's issues" do
853 853 project = Project.generate!
854 854 project.trackers << Tracker.generate!
855 855 early = 7.days.ago.to_date
856 856 Issue.generate!(:project => project, :start_date => Date.today)
857 857 Issue.generate!(:project => project, :start_date => early)
858 858
859 859 assert_equal early, project.start_date
860 860 end
861 861
862 862 test "#due_date should be nil if there are no issues on the project" do
863 863 project = Project.generate!
864 864 assert_nil project.due_date
865 865 end
866 866
867 867 test "#due_date should be nil if there are no issues with due dates" do
868 868 project = Project.generate!
869 869 project.trackers << Tracker.generate!
870 870 Issue.generate!(:project => project, :due_date => nil)
871 871
872 872 assert_nil project.due_date
873 873 end
874 874
875 875 test "#due_date should be the latest due date of it's issues" do
876 876 project = Project.generate!
877 877 project.trackers << Tracker.generate!
878 878 future = 7.days.from_now.to_date
879 879 Issue.generate!(:project => project, :due_date => future)
880 880 Issue.generate!(:project => project, :due_date => Date.today)
881 881
882 882 assert_equal future, project.due_date
883 883 end
884 884
885 885 test "#due_date should be the latest due date of it's versions" do
886 886 project = Project.generate!
887 887 future = 7.days.from_now.to_date
888 888 project.versions << Version.generate!(:effective_date => future)
889 889 project.versions << Version.generate!(:effective_date => Date.today)
890 890
891 891 assert_equal future, project.due_date
892 892 end
893 893
894 894 test "#due_date should pick the latest date from it's issues and versions" do
895 895 project = Project.generate!
896 896 project.trackers << Tracker.generate!
897 897 future = 7.days.from_now.to_date
898 898 far_future = 14.days.from_now.to_date
899 899 Issue.generate!(:project => project, :due_date => far_future)
900 900 project.versions << Version.generate!(:effective_date => future)
901 901
902 902 assert_equal far_future, project.due_date
903 903 end
904 904
905 905 test "#completed_percent with no versions should be 100" do
906 906 project = Project.generate!
907 907 assert_equal 100, project.completed_percent
908 908 end
909 909
910 910 test "#completed_percent with versions should return 0 if the versions have no issues" do
911 911 project = Project.generate!
912 912 Version.generate!(:project => project)
913 913 Version.generate!(:project => project)
914 914
915 915 assert_equal 0, project.completed_percent
916 916 end
917 917
918 918 test "#completed_percent with versions should return 100 if the version has only closed issues" do
919 919 project = Project.generate!
920 920 project.trackers << Tracker.generate!
921 921 v1 = Version.generate!(:project => project)
922 922 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
923 923 v2 = Version.generate!(:project => project)
924 924 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
925 925
926 926 assert_equal 100, project.completed_percent
927 927 end
928 928
929 929 test "#completed_percent with versions should return the averaged completed percent of the versions (not weighted)" do
930 930 project = Project.generate!
931 931 project.trackers << Tracker.generate!
932 932 v1 = Version.generate!(:project => project)
933 933 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
934 934 v2 = Version.generate!(:project => project)
935 935 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
936 936
937 937 assert_equal 50, project.completed_percent
938 938 end
939 939
940 940 test "#notified_users" do
941 941 project = Project.generate!
942 942 role = Role.generate!
943 943
944 944 user_with_membership_notification = User.generate!(:mail_notification => 'selected')
945 945 Member.create!(:project => project, :roles => [role], :principal => user_with_membership_notification, :mail_notification => true)
946 946
947 947 all_events_user = User.generate!(:mail_notification => 'all')
948 948 Member.create!(:project => project, :roles => [role], :principal => all_events_user)
949 949
950 950 no_events_user = User.generate!(:mail_notification => 'none')
951 951 Member.create!(:project => project, :roles => [role], :principal => no_events_user)
952 952
953 953 only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
954 954 Member.create!(:project => project, :roles => [role], :principal => only_my_events_user)
955 955
956 956 only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
957 957 Member.create!(:project => project, :roles => [role], :principal => only_assigned_user)
958 958
959 959 only_owned_user = User.generate!(:mail_notification => 'only_owner')
960 960 Member.create!(:project => project, :roles => [role], :principal => only_owned_user)
961 961
962 962 assert project.notified_users.include?(user_with_membership_notification), "should include members with a mail notification"
963 963 assert project.notified_users.include?(all_events_user), "should include users with the 'all' notification option"
964 964 assert !project.notified_users.include?(no_events_user), "should not include users with the 'none' notification option"
965 965 assert !project.notified_users.include?(only_my_events_user), "should not include users with the 'only_my_events' notification option"
966 966 assert !project.notified_users.include?(only_assigned_user), "should not include users with the 'only_assigned' notification option"
967 967 assert !project.notified_users.include?(only_owned_user), "should not include users with the 'only_owner' notification option"
968 968 end
969 969
970 970 def test_override_roles_without_builtin_group_memberships
971 971 project = Project.generate!
972 972 assert_equal [Role.anonymous], project.override_roles(Role.anonymous)
973 973 assert_equal [Role.non_member], project.override_roles(Role.non_member)
974 974 end
975 975
976 976 def test_css_classes
977 977 p = Project.new
978 978 assert_kind_of String, p.css_classes
979 979 assert_not_include 'archived', p.css_classes.split
980 980 assert_not_include 'closed', p.css_classes.split
981 981 end
982 982
983 983 def test_css_classes_for_archived_project
984 984 p = Project.new
985 985 p.status = Project::STATUS_ARCHIVED
986 986 assert_include 'archived', p.css_classes.split
987 987 end
988 988
989 989 def test_css_classes_for_closed_project
990 990 p = Project.new
991 991 p.status = Project::STATUS_CLOSED
992 992 assert_include 'closed', p.css_classes.split
993 993 end
994
995 def test_combination_of_visible_and_uniq_scopes_in_case_anonymous_group_has_memberships_should_not_error
996 project = Project.find(1)
997 member = Member.create!(:project => project, :principal => Group.anonymous, :roles => [Role.generate!])
998 project.members << member
999 assert_nothing_raised do
1000 Project.uniq.visible.to_a
1001 end
1002 end
994 1003 end
General Comments 0
You need to be logged in to leave comments. Login now