##// END OF EJS Templates
Removing shoulda context....
Jean-Philippe Lang -
r11085:14b50dfbabc6
parent child
Show More
@@ -1,285 +1,283
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class AttachmentTest < ActiveSupport::TestCase
22 class AttachmentTest < ActiveSupport::TestCase
23 fixtures :users, :projects, :roles, :members, :member_roles,
23 fixtures :users, :projects, :roles, :members, :member_roles,
24 :enabled_modules, :issues, :trackers, :attachments
24 :enabled_modules, :issues, :trackers, :attachments
25
25
26 class MockFile
26 class MockFile
27 attr_reader :original_filename, :content_type, :content, :size
27 attr_reader :original_filename, :content_type, :content, :size
28
28
29 def initialize(attributes)
29 def initialize(attributes)
30 @original_filename = attributes[:original_filename]
30 @original_filename = attributes[:original_filename]
31 @content_type = attributes[:content_type]
31 @content_type = attributes[:content_type]
32 @content = attributes[:content] || "Content"
32 @content = attributes[:content] || "Content"
33 @size = content.size
33 @size = content.size
34 end
34 end
35 end
35 end
36
36
37 def setup
37 def setup
38 set_tmp_attachments_directory
38 set_tmp_attachments_directory
39 end
39 end
40
40
41 def test_container_for_new_attachment_should_be_nil
41 def test_container_for_new_attachment_should_be_nil
42 assert_nil Attachment.new.container
42 assert_nil Attachment.new.container
43 end
43 end
44
44
45 def test_create
45 def test_create
46 a = Attachment.new(:container => Issue.find(1),
46 a = Attachment.new(:container => Issue.find(1),
47 :file => uploaded_test_file("testfile.txt", "text/plain"),
47 :file => uploaded_test_file("testfile.txt", "text/plain"),
48 :author => User.find(1))
48 :author => User.find(1))
49 assert a.save
49 assert a.save
50 assert_equal 'testfile.txt', a.filename
50 assert_equal 'testfile.txt', a.filename
51 assert_equal 59, a.filesize
51 assert_equal 59, a.filesize
52 assert_equal 'text/plain', a.content_type
52 assert_equal 'text/plain', a.content_type
53 assert_equal 0, a.downloads
53 assert_equal 0, a.downloads
54 assert_equal '1478adae0d4eb06d35897518540e25d6', a.digest
54 assert_equal '1478adae0d4eb06d35897518540e25d6', a.digest
55
55
56 assert a.disk_directory
56 assert a.disk_directory
57 assert_match %r{\A\d{4}/\d{2}\z}, a.disk_directory
57 assert_match %r{\A\d{4}/\d{2}\z}, a.disk_directory
58
58
59 assert File.exist?(a.diskfile)
59 assert File.exist?(a.diskfile)
60 assert_equal 59, File.size(a.diskfile)
60 assert_equal 59, File.size(a.diskfile)
61 end
61 end
62
62
63 def test_copy_should_preserve_attributes
63 def test_copy_should_preserve_attributes
64 a = Attachment.find(1)
64 a = Attachment.find(1)
65 copy = a.copy
65 copy = a.copy
66
66
67 assert_save copy
67 assert_save copy
68 copy = Attachment.order('id DESC').first
68 copy = Attachment.order('id DESC').first
69 %w(filename filesize content_type author_id created_on description digest disk_filename disk_directory diskfile).each do |attribute|
69 %w(filename filesize content_type author_id created_on description digest disk_filename disk_directory diskfile).each do |attribute|
70 assert_equal a.send(attribute), copy.send(attribute), "#{attribute} was different"
70 assert_equal a.send(attribute), copy.send(attribute), "#{attribute} was different"
71 end
71 end
72 end
72 end
73
73
74 def test_size_should_be_validated_for_new_file
74 def test_size_should_be_validated_for_new_file
75 with_settings :attachment_max_size => 0 do
75 with_settings :attachment_max_size => 0 do
76 a = Attachment.new(:container => Issue.find(1),
76 a = Attachment.new(:container => Issue.find(1),
77 :file => uploaded_test_file("testfile.txt", "text/plain"),
77 :file => uploaded_test_file("testfile.txt", "text/plain"),
78 :author => User.find(1))
78 :author => User.find(1))
79 assert !a.save
79 assert !a.save
80 end
80 end
81 end
81 end
82
82
83 def test_size_should_not_be_validated_when_copying
83 def test_size_should_not_be_validated_when_copying
84 a = Attachment.create!(:container => Issue.find(1),
84 a = Attachment.create!(:container => Issue.find(1),
85 :file => uploaded_test_file("testfile.txt", "text/plain"),
85 :file => uploaded_test_file("testfile.txt", "text/plain"),
86 :author => User.find(1))
86 :author => User.find(1))
87 with_settings :attachment_max_size => 0 do
87 with_settings :attachment_max_size => 0 do
88 copy = a.copy
88 copy = a.copy
89 assert copy.save
89 assert copy.save
90 end
90 end
91 end
91 end
92
92
93 def test_description_length_should_be_validated
93 def test_description_length_should_be_validated
94 a = Attachment.new(:description => 'a' * 300)
94 a = Attachment.new(:description => 'a' * 300)
95 assert !a.save
95 assert !a.save
96 assert_not_nil a.errors[:description]
96 assert_not_nil a.errors[:description]
97 end
97 end
98
98
99 def test_destroy
99 def test_destroy
100 a = Attachment.new(:container => Issue.find(1),
100 a = Attachment.new(:container => Issue.find(1),
101 :file => uploaded_test_file("testfile.txt", "text/plain"),
101 :file => uploaded_test_file("testfile.txt", "text/plain"),
102 :author => User.find(1))
102 :author => User.find(1))
103 assert a.save
103 assert a.save
104 assert_equal 'testfile.txt', a.filename
104 assert_equal 'testfile.txt', a.filename
105 assert_equal 59, a.filesize
105 assert_equal 59, a.filesize
106 assert_equal 'text/plain', a.content_type
106 assert_equal 'text/plain', a.content_type
107 assert_equal 0, a.downloads
107 assert_equal 0, a.downloads
108 assert_equal '1478adae0d4eb06d35897518540e25d6', a.digest
108 assert_equal '1478adae0d4eb06d35897518540e25d6', a.digest
109 diskfile = a.diskfile
109 diskfile = a.diskfile
110 assert File.exist?(diskfile)
110 assert File.exist?(diskfile)
111 assert_equal 59, File.size(a.diskfile)
111 assert_equal 59, File.size(a.diskfile)
112 assert a.destroy
112 assert a.destroy
113 assert !File.exist?(diskfile)
113 assert !File.exist?(diskfile)
114 end
114 end
115
115
116 def test_destroy_should_not_delete_file_referenced_by_other_attachment
116 def test_destroy_should_not_delete_file_referenced_by_other_attachment
117 a = Attachment.create!(:container => Issue.find(1),
117 a = Attachment.create!(:container => Issue.find(1),
118 :file => uploaded_test_file("testfile.txt", "text/plain"),
118 :file => uploaded_test_file("testfile.txt", "text/plain"),
119 :author => User.find(1))
119 :author => User.find(1))
120 diskfile = a.diskfile
120 diskfile = a.diskfile
121
121
122 copy = a.copy
122 copy = a.copy
123 copy.save!
123 copy.save!
124
124
125 assert File.exists?(diskfile)
125 assert File.exists?(diskfile)
126 a.destroy
126 a.destroy
127 assert File.exists?(diskfile)
127 assert File.exists?(diskfile)
128 copy.destroy
128 copy.destroy
129 assert !File.exists?(diskfile)
129 assert !File.exists?(diskfile)
130 end
130 end
131
131
132 def test_create_should_auto_assign_content_type
132 def test_create_should_auto_assign_content_type
133 a = Attachment.new(:container => Issue.find(1),
133 a = Attachment.new(:container => Issue.find(1),
134 :file => uploaded_test_file("testfile.txt", ""),
134 :file => uploaded_test_file("testfile.txt", ""),
135 :author => User.find(1))
135 :author => User.find(1))
136 assert a.save
136 assert a.save
137 assert_equal 'text/plain', a.content_type
137 assert_equal 'text/plain', a.content_type
138 end
138 end
139
139
140 def test_identical_attachments_at_the_same_time_should_not_overwrite
140 def test_identical_attachments_at_the_same_time_should_not_overwrite
141 a1 = Attachment.create!(:container => Issue.find(1),
141 a1 = Attachment.create!(:container => Issue.find(1),
142 :file => uploaded_test_file("testfile.txt", ""),
142 :file => uploaded_test_file("testfile.txt", ""),
143 :author => User.find(1))
143 :author => User.find(1))
144 a2 = Attachment.create!(:container => Issue.find(1),
144 a2 = Attachment.create!(:container => Issue.find(1),
145 :file => uploaded_test_file("testfile.txt", ""),
145 :file => uploaded_test_file("testfile.txt", ""),
146 :author => User.find(1))
146 :author => User.find(1))
147 assert a1.disk_filename != a2.disk_filename
147 assert a1.disk_filename != a2.disk_filename
148 end
148 end
149
149
150 def test_filename_should_be_basenamed
150 def test_filename_should_be_basenamed
151 a = Attachment.new(:file => MockFile.new(:original_filename => "path/to/the/file"))
151 a = Attachment.new(:file => MockFile.new(:original_filename => "path/to/the/file"))
152 assert_equal 'file', a.filename
152 assert_equal 'file', a.filename
153 end
153 end
154
154
155 def test_filename_should_be_sanitized
155 def test_filename_should_be_sanitized
156 a = Attachment.new(:file => MockFile.new(:original_filename => "valid:[] invalid:?%*|\"'<>chars"))
156 a = Attachment.new(:file => MockFile.new(:original_filename => "valid:[] invalid:?%*|\"'<>chars"))
157 assert_equal 'valid_[] invalid_chars', a.filename
157 assert_equal 'valid_[] invalid_chars', a.filename
158 end
158 end
159
159
160 def test_diskfilename
160 def test_diskfilename
161 assert Attachment.disk_filename("test_file.txt") =~ /^\d{12}_test_file.txt$/
161 assert Attachment.disk_filename("test_file.txt") =~ /^\d{12}_test_file.txt$/
162 assert_equal 'test_file.txt', Attachment.disk_filename("test_file.txt")[13..-1]
162 assert_equal 'test_file.txt', Attachment.disk_filename("test_file.txt")[13..-1]
163 assert_equal '770c509475505f37c2b8fb6030434d6b.txt', Attachment.disk_filename("test_accentuΓ©.txt")[13..-1]
163 assert_equal '770c509475505f37c2b8fb6030434d6b.txt', Attachment.disk_filename("test_accentuΓ©.txt")[13..-1]
164 assert_equal 'f8139524ebb8f32e51976982cd20a85d', Attachment.disk_filename("test_accentuΓ©")[13..-1]
164 assert_equal 'f8139524ebb8f32e51976982cd20a85d', Attachment.disk_filename("test_accentuΓ©")[13..-1]
165 assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentuΓ©.Γ§a")[13..-1]
165 assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentuΓ©.Γ§a")[13..-1]
166 end
166 end
167
167
168 def test_title
168 def test_title
169 a = Attachment.new(:filename => "test.png")
169 a = Attachment.new(:filename => "test.png")
170 assert_equal "test.png", a.title
170 assert_equal "test.png", a.title
171
171
172 a = Attachment.new(:filename => "test.png", :description => "Cool image")
172 a = Attachment.new(:filename => "test.png", :description => "Cool image")
173 assert_equal "test.png (Cool image)", a.title
173 assert_equal "test.png (Cool image)", a.title
174 end
174 end
175
175
176 def test_prune_should_destroy_old_unattached_attachments
176 def test_prune_should_destroy_old_unattached_attachments
177 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1, :created_on => 2.days.ago)
177 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1, :created_on => 2.days.ago)
178 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1, :created_on => 2.days.ago)
178 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1, :created_on => 2.days.ago)
179 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1)
179 Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1)
180
180
181 assert_difference 'Attachment.count', -2 do
181 assert_difference 'Attachment.count', -2 do
182 Attachment.prune
182 Attachment.prune
183 end
183 end
184 end
184 end
185
185
186 def test_move_from_root_to_target_directory_should_move_root_files
186 def test_move_from_root_to_target_directory_should_move_root_files
187 a = Attachment.find(20)
187 a = Attachment.find(20)
188 assert a.disk_directory.blank?
188 assert a.disk_directory.blank?
189 # Create a real file for this fixture
189 # Create a real file for this fixture
190 File.open(a.diskfile, "w") do |f|
190 File.open(a.diskfile, "w") do |f|
191 f.write "test file at the root of files directory"
191 f.write "test file at the root of files directory"
192 end
192 end
193 assert a.readable?
193 assert a.readable?
194 Attachment.move_from_root_to_target_directory
194 Attachment.move_from_root_to_target_directory
195
195
196 a.reload
196 a.reload
197 assert_equal '2012/05', a.disk_directory
197 assert_equal '2012/05', a.disk_directory
198 assert a.readable?
198 assert a.readable?
199 end
199 end
200
200
201 context "Attachmnet.attach_files" do
201 test "Attachmnet.attach_files should attach the file" do
202 should "attach the file" do
202 issue = Issue.first
203 issue = Issue.first
203 assert_difference 'Attachment.count' do
204 assert_difference 'Attachment.count' do
204 Attachment.attach_files(issue,
205 Attachment.attach_files(issue,
205 '1' => {
206 '1' => {
206 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
207 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
207 'description' => 'test'
208 'description' => 'test'
208 })
209 })
210 end
211
212 attachment = Attachment.first(:order => 'id DESC')
213 assert_equal issue, attachment.container
214 assert_equal 'testfile.txt', attachment.filename
215 assert_equal 59, attachment.filesize
216 assert_equal 'test', attachment.description
217 assert_equal 'text/plain', attachment.content_type
218 assert File.exists?(attachment.diskfile)
219 assert_equal 59, File.size(attachment.diskfile)
220 end
209 end
221
210
222 should "add unsaved files to the object as unsaved attachments" do
211 attachment = Attachment.first(:order => 'id DESC')
223 # Max size of 0 to force Attachment creation failures
212 assert_equal issue, attachment.container
224 with_settings(:attachment_max_size => 0) do
213 assert_equal 'testfile.txt', attachment.filename
225 @project = Project.find(1)
214 assert_equal 59, attachment.filesize
226 response = Attachment.attach_files(@project, {
215 assert_equal 'test', attachment.description
227 '1' => {'file' => mock_file, 'description' => 'test'},
216 assert_equal 'text/plain', attachment.content_type
228 '2' => {'file' => mock_file, 'description' => 'test'}
217 assert File.exists?(attachment.diskfile)
229 })
218 assert_equal 59, File.size(attachment.diskfile)
230
219 end
231 assert response[:unsaved].present?
220
232 assert_equal 2, response[:unsaved].length
221 test "Attachmnet.attach_files should add unsaved files to the object as unsaved attachments" do
233 assert response[:unsaved].first.new_record?
222 # Max size of 0 to force Attachment creation failures
234 assert response[:unsaved].second.new_record?
223 with_settings(:attachment_max_size => 0) do
235 assert_equal response[:unsaved], @project.unsaved_attachments
224 @project = Project.find(1)
236 end
225 response = Attachment.attach_files(@project, {
226 '1' => {'file' => mock_file, 'description' => 'test'},
227 '2' => {'file' => mock_file, 'description' => 'test'}
228 })
229
230 assert response[:unsaved].present?
231 assert_equal 2, response[:unsaved].length
232 assert response[:unsaved].first.new_record?
233 assert response[:unsaved].second.new_record?
234 assert_equal response[:unsaved], @project.unsaved_attachments
237 end
235 end
238 end
236 end
239
237
240 def test_latest_attach
238 def test_latest_attach
241 set_fixtures_attachments_directory
239 set_fixtures_attachments_directory
242 a1 = Attachment.find(16)
240 a1 = Attachment.find(16)
243 assert_equal "testfile.png", a1.filename
241 assert_equal "testfile.png", a1.filename
244 assert a1.readable?
242 assert a1.readable?
245 assert (! a1.visible?(User.anonymous))
243 assert (! a1.visible?(User.anonymous))
246 assert a1.visible?(User.find(2))
244 assert a1.visible?(User.find(2))
247 a2 = Attachment.find(17)
245 a2 = Attachment.find(17)
248 assert_equal "testfile.PNG", a2.filename
246 assert_equal "testfile.PNG", a2.filename
249 assert a2.readable?
247 assert a2.readable?
250 assert (! a2.visible?(User.anonymous))
248 assert (! a2.visible?(User.anonymous))
251 assert a2.visible?(User.find(2))
249 assert a2.visible?(User.find(2))
252 assert a1.created_on < a2.created_on
250 assert a1.created_on < a2.created_on
253
251
254 la1 = Attachment.latest_attach([a1, a2], "testfile.png")
252 la1 = Attachment.latest_attach([a1, a2], "testfile.png")
255 assert_equal 17, la1.id
253 assert_equal 17, la1.id
256 la2 = Attachment.latest_attach([a1, a2], "Testfile.PNG")
254 la2 = Attachment.latest_attach([a1, a2], "Testfile.PNG")
257 assert_equal 17, la2.id
255 assert_equal 17, la2.id
258
256
259 set_tmp_attachments_directory
257 set_tmp_attachments_directory
260 end
258 end
261
259
262 def test_thumbnailable_should_be_true_for_images
260 def test_thumbnailable_should_be_true_for_images
263 assert_equal true, Attachment.new(:filename => 'test.jpg').thumbnailable?
261 assert_equal true, Attachment.new(:filename => 'test.jpg').thumbnailable?
264 end
262 end
265
263
266 def test_thumbnailable_should_be_true_for_non_images
264 def test_thumbnailable_should_be_true_for_non_images
267 assert_equal false, Attachment.new(:filename => 'test.txt').thumbnailable?
265 assert_equal false, Attachment.new(:filename => 'test.txt').thumbnailable?
268 end
266 end
269
267
270 if convert_installed?
268 if convert_installed?
271 def test_thumbnail_should_generate_the_thumbnail
269 def test_thumbnail_should_generate_the_thumbnail
272 set_fixtures_attachments_directory
270 set_fixtures_attachments_directory
273 attachment = Attachment.find(16)
271 attachment = Attachment.find(16)
274 Attachment.clear_thumbnails
272 Attachment.clear_thumbnails
275
273
276 assert_difference "Dir.glob(File.join(Attachment.thumbnails_storage_path, '*.thumb')).size" do
274 assert_difference "Dir.glob(File.join(Attachment.thumbnails_storage_path, '*.thumb')).size" do
277 thumbnail = attachment.thumbnail
275 thumbnail = attachment.thumbnail
278 assert_equal "16_8e0294de2441577c529f170b6fb8f638_100.thumb", File.basename(thumbnail)
276 assert_equal "16_8e0294de2441577c529f170b6fb8f638_100.thumb", File.basename(thumbnail)
279 assert File.exists?(thumbnail)
277 assert File.exists?(thumbnail)
280 end
278 end
281 end
279 end
282 else
280 else
283 puts '(ImageMagick convert not available)'
281 puts '(ImageMagick convert not available)'
284 end
282 end
285 end
283 end
@@ -1,154 +1,141
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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 AuthSourceLdapTest < ActiveSupport::TestCase
20 class AuthSourceLdapTest < ActiveSupport::TestCase
21 include Redmine::I18n
21 include Redmine::I18n
22 fixtures :auth_sources
22 fixtures :auth_sources
23
23
24 def setup
24 def setup
25 end
25 end
26
26
27 def test_create
27 def test_create
28 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName')
28 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName')
29 assert a.save
29 assert a.save
30 end
30 end
31
31
32 def test_should_strip_ldap_attributes
32 def test_should_strip_ldap_attributes
33 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
33 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
34 :attr_firstname => 'givenName ')
34 :attr_firstname => 'givenName ')
35 assert a.save
35 assert a.save
36 assert_equal 'givenName', a.reload.attr_firstname
36 assert_equal 'givenName', a.reload.attr_firstname
37 end
37 end
38
38
39 def test_replace_port_zero_to_389
39 def test_replace_port_zero_to_389
40 a = AuthSourceLdap.new(
40 a = AuthSourceLdap.new(
41 :name => 'My LDAP', :host => 'ldap.example.net', :port => 0,
41 :name => 'My LDAP', :host => 'ldap.example.net', :port => 0,
42 :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
42 :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
43 :attr_firstname => 'givenName ')
43 :attr_firstname => 'givenName ')
44 assert a.save
44 assert a.save
45 assert_equal 389, a.port
45 assert_equal 389, a.port
46 end
46 end
47
47
48 def test_filter_should_be_validated
48 def test_filter_should_be_validated
49 set_language_if_valid 'en'
49 set_language_if_valid 'en'
50
50
51 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :attr_login => 'sn')
51 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :attr_login => 'sn')
52 a.filter = "(mail=*@redmine.org"
52 a.filter = "(mail=*@redmine.org"
53 assert !a.valid?
53 assert !a.valid?
54 assert_include "LDAP filter is invalid", a.errors.full_messages
54 assert_include "LDAP filter is invalid", a.errors.full_messages
55
55
56 a.filter = "(mail=*@redmine.org)"
56 a.filter = "(mail=*@redmine.org)"
57 assert a.valid?
57 assert a.valid?
58 end
58 end
59
59
60 if ldap_configured?
60 if ldap_configured?
61 context '#authenticate' do
61 test '#authenticate with a valid LDAP user should return the user attributes' do
62 setup do
62 auth = AuthSourceLdap.find(1)
63 @auth = AuthSourceLdap.find(1)
63 auth.update_attribute :onthefly_register, true
64 @auth.update_attribute :onthefly_register, true
64
65 end
65 attributes = auth.authenticate('example1','123456')
66
66 assert attributes.is_a?(Hash), "An hash was not returned"
67 context 'with a valid LDAP user' do
67 assert_equal 'Example', attributes[:firstname]
68 should 'return the user attributes' do
68 assert_equal 'One', attributes[:lastname]
69 attributes = @auth.authenticate('example1','123456')
69 assert_equal 'example1@redmine.org', attributes[:mail]
70 assert attributes.is_a?(Hash), "An hash was not returned"
70 assert_equal auth.id, attributes[:auth_source_id]
71 assert_equal 'Example', attributes[:firstname]
71 attributes.keys.each do |attribute|
72 assert_equal 'One', attributes[:lastname]
72 assert User.new.respond_to?("#{attribute}="), "Unexpected :#{attribute} attribute returned"
73 assert_equal 'example1@redmine.org', attributes[:mail]
74 assert_equal @auth.id, attributes[:auth_source_id]
75 attributes.keys.each do |attribute|
76 assert User.new.respond_to?("#{attribute}="), "Unexpected :#{attribute} attribute returned"
77 end
78 end
79 end
73 end
74 end
80
75
81 context 'with an invalid LDAP user' do
76 test '#authenticate with an invalid LDAP user should return nil' do
82 should 'return nil' do
77 auth = AuthSourceLdap.find(1)
83 assert_equal nil, @auth.authenticate('nouser','123456')
78 assert_equal nil, auth.authenticate('nouser','123456')
84 end
79 end
85 end
86
80
87 context 'without a login' do
81 test '#authenticate without a login should return nil' do
88 should 'return nil' do
82 auth = AuthSourceLdap.find(1)
89 assert_equal nil, @auth.authenticate('','123456')
83 assert_equal nil, auth.authenticate('','123456')
90 end
84 end
91 end
92
85
93 context 'without a password' do
86 test '#authenticate without a password should return nil' do
94 should 'return nil' do
87 auth = AuthSourceLdap.find(1)
95 assert_equal nil, @auth.authenticate('edavis','')
88 assert_equal nil, auth.authenticate('edavis','')
96 end
89 end
97 end
98
90
99 context 'without filter' do
91 test '#authenticate without filter should return any user' do
100 should 'return any user' do
92 auth = AuthSourceLdap.find(1)
101 assert @auth.authenticate('example1','123456')
93 assert auth.authenticate('example1','123456')
102 assert @auth.authenticate('edavis', '123456')
94 assert auth.authenticate('edavis', '123456')
103 end
95 end
104 end
105
96
106 context 'with filter' do
97 test '#authenticate with filter should return user who matches the filter only' do
107 setup do
98 auth = AuthSourceLdap.find(1)
108 @auth.filter = "(mail=*@redmine.org)"
99 auth.filter = "(mail=*@redmine.org)"
109 end
110
100
111 should 'return user who matches the filter only' do
101 assert auth.authenticate('example1','123456')
112 assert @auth.authenticate('example1','123456')
102 assert_nil auth.authenticate('edavis', '123456')
113 assert_nil @auth.authenticate('edavis', '123456')
114 end
115 end
116 end
103 end
117
104
118 def test_authenticate_should_timeout
105 def test_authenticate_should_timeout
119 auth_source = AuthSourceLdap.find(1)
106 auth_source = AuthSourceLdap.find(1)
120 auth_source.timeout = 1
107 auth_source.timeout = 1
121 def auth_source.initialize_ldap_con(*args); sleep(5); end
108 def auth_source.initialize_ldap_con(*args); sleep(5); end
122
109
123 assert_raise AuthSourceTimeoutException do
110 assert_raise AuthSourceTimeoutException do
124 auth_source.authenticate 'example1', '123456'
111 auth_source.authenticate 'example1', '123456'
125 end
112 end
126 end
113 end
127
114
128 def test_search_should_return_matching_entries
115 def test_search_should_return_matching_entries
129 results = AuthSource.search("exa")
116 results = AuthSource.search("exa")
130 assert_equal 1, results.size
117 assert_equal 1, results.size
131 result = results.first
118 result = results.first
132 assert_kind_of Hash, result
119 assert_kind_of Hash, result
133 assert_equal "example1", result[:login]
120 assert_equal "example1", result[:login]
134 assert_equal "Example", result[:firstname]
121 assert_equal "Example", result[:firstname]
135 assert_equal "One", result[:lastname]
122 assert_equal "One", result[:lastname]
136 assert_equal "example1@redmine.org", result[:mail]
123 assert_equal "example1@redmine.org", result[:mail]
137 assert_equal 1, result[:auth_source_id]
124 assert_equal 1, result[:auth_source_id]
138 end
125 end
139
126
140 def test_search_with_no_match_should_return_an_empty_array
127 def test_search_with_no_match_should_return_an_empty_array
141 results = AuthSource.search("wro")
128 results = AuthSource.search("wro")
142 assert_equal [], results
129 assert_equal [], results
143 end
130 end
144
131
145 def test_search_with_exception_should_return_an_empty_array
132 def test_search_with_exception_should_return_an_empty_array
146 Net::LDAP.stubs(:new).raises(Net::LDAP::LdapError, 'Cannot connect')
133 Net::LDAP.stubs(:new).raises(Net::LDAP::LdapError, 'Cannot connect')
147
134
148 results = AuthSource.search("exa")
135 results = AuthSource.search("exa")
149 assert_equal [], results
136 assert_equal [], results
150 end
137 end
151 else
138 else
152 puts '(Test LDAP server not configured)'
139 puts '(Test LDAP server not configured)'
153 end
140 end
154 end
141 end
@@ -1,1957 +1,1905
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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_start_date_format_should_be_validated
57 def test_start_date_format_should_be_validated
58 set_language_if_valid 'en'
58 set_language_if_valid 'en'
59 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
59 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
60 issue = Issue.new(:start_date => invalid_date)
60 issue = Issue.new(:start_date => invalid_date)
61 assert !issue.valid?
61 assert !issue.valid?
62 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
62 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
63 end
63 end
64 end
64 end
65
65
66 def test_due_date_format_should_be_validated
66 def test_due_date_format_should_be_validated
67 set_language_if_valid 'en'
67 set_language_if_valid 'en'
68 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
68 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
69 issue = Issue.new(:due_date => invalid_date)
69 issue = Issue.new(:due_date => invalid_date)
70 assert !issue.valid?
70 assert !issue.valid?
71 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
71 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
72 end
72 end
73 end
73 end
74
74
75 def test_due_date_lesser_than_start_date_should_not_validate
75 def test_due_date_lesser_than_start_date_should_not_validate
76 set_language_if_valid 'en'
76 set_language_if_valid 'en'
77 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
77 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
78 assert !issue.valid?
78 assert !issue.valid?
79 assert_include 'Due date must be greater than start date', issue.errors.full_messages
79 assert_include 'Due date must be greater than start date', issue.errors.full_messages
80 end
80 end
81
81
82 def test_estimated_hours_should_be_validated
82 def test_estimated_hours_should_be_validated
83 set_language_if_valid 'en'
83 set_language_if_valid 'en'
84 ['-2'].each do |invalid|
84 ['-2'].each do |invalid|
85 issue = Issue.new(:estimated_hours => invalid)
85 issue = Issue.new(:estimated_hours => invalid)
86 assert !issue.valid?
86 assert !issue.valid?
87 assert_include 'Estimated time is invalid', issue.errors.full_messages
87 assert_include 'Estimated time is invalid', issue.errors.full_messages
88 end
88 end
89 end
89 end
90
90
91 def test_create_with_required_custom_field
91 def test_create_with_required_custom_field
92 set_language_if_valid 'en'
92 set_language_if_valid 'en'
93 field = IssueCustomField.find_by_name('Database')
93 field = IssueCustomField.find_by_name('Database')
94 field.update_attribute(:is_required, true)
94 field.update_attribute(:is_required, true)
95
95
96 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
96 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
97 :status_id => 1, :subject => 'test_create',
97 :status_id => 1, :subject => 'test_create',
98 :description => 'IssueTest#test_create_with_required_custom_field')
98 :description => 'IssueTest#test_create_with_required_custom_field')
99 assert issue.available_custom_fields.include?(field)
99 assert issue.available_custom_fields.include?(field)
100 # No value for the custom field
100 # No value for the custom field
101 assert !issue.save
101 assert !issue.save
102 assert_equal ["Database can't be blank"], issue.errors.full_messages
102 assert_equal ["Database can't be blank"], issue.errors.full_messages
103 # Blank value
103 # Blank value
104 issue.custom_field_values = { field.id => '' }
104 issue.custom_field_values = { field.id => '' }
105 assert !issue.save
105 assert !issue.save
106 assert_equal ["Database can't be blank"], issue.errors.full_messages
106 assert_equal ["Database can't be blank"], issue.errors.full_messages
107 # Invalid value
107 # Invalid value
108 issue.custom_field_values = { field.id => 'SQLServer' }
108 issue.custom_field_values = { field.id => 'SQLServer' }
109 assert !issue.save
109 assert !issue.save
110 assert_equal ["Database is not included in the list"], issue.errors.full_messages
110 assert_equal ["Database is not included in the list"], issue.errors.full_messages
111 # Valid value
111 # Valid value
112 issue.custom_field_values = { field.id => 'PostgreSQL' }
112 issue.custom_field_values = { field.id => 'PostgreSQL' }
113 assert issue.save
113 assert issue.save
114 issue.reload
114 issue.reload
115 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
115 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
116 end
116 end
117
117
118 def test_create_with_group_assignment
118 def test_create_with_group_assignment
119 with_settings :issue_group_assignment => '1' do
119 with_settings :issue_group_assignment => '1' do
120 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
120 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
121 :subject => 'Group assignment',
121 :subject => 'Group assignment',
122 :assigned_to_id => 11).save
122 :assigned_to_id => 11).save
123 issue = Issue.first(:order => 'id DESC')
123 issue = Issue.first(:order => 'id DESC')
124 assert_kind_of Group, issue.assigned_to
124 assert_kind_of Group, issue.assigned_to
125 assert_equal Group.find(11), issue.assigned_to
125 assert_equal Group.find(11), issue.assigned_to
126 end
126 end
127 end
127 end
128
128
129 def test_create_with_parent_issue_id
129 def test_create_with_parent_issue_id
130 issue = Issue.new(:project_id => 1, :tracker_id => 1,
130 issue = Issue.new(:project_id => 1, :tracker_id => 1,
131 :author_id => 1, :subject => 'Group assignment',
131 :author_id => 1, :subject => 'Group assignment',
132 :parent_issue_id => 1)
132 :parent_issue_id => 1)
133 assert_save issue
133 assert_save issue
134 assert_equal 1, issue.parent_issue_id
134 assert_equal 1, issue.parent_issue_id
135 assert_equal Issue.find(1), issue.parent
135 assert_equal Issue.find(1), issue.parent
136 end
136 end
137
137
138 def test_create_with_sharp_parent_issue_id
138 def test_create_with_sharp_parent_issue_id
139 issue = Issue.new(:project_id => 1, :tracker_id => 1,
139 issue = Issue.new(:project_id => 1, :tracker_id => 1,
140 :author_id => 1, :subject => 'Group assignment',
140 :author_id => 1, :subject => 'Group assignment',
141 :parent_issue_id => "#1")
141 :parent_issue_id => "#1")
142 assert_save issue
142 assert_save issue
143 assert_equal 1, issue.parent_issue_id
143 assert_equal 1, issue.parent_issue_id
144 assert_equal Issue.find(1), issue.parent
144 assert_equal Issue.find(1), issue.parent
145 end
145 end
146
146
147 def test_create_with_invalid_parent_issue_id
147 def test_create_with_invalid_parent_issue_id
148 set_language_if_valid 'en'
148 set_language_if_valid 'en'
149 issue = Issue.new(:project_id => 1, :tracker_id => 1,
149 issue = Issue.new(:project_id => 1, :tracker_id => 1,
150 :author_id => 1, :subject => 'Group assignment',
150 :author_id => 1, :subject => 'Group assignment',
151 :parent_issue_id => '01ABC')
151 :parent_issue_id => '01ABC')
152 assert !issue.save
152 assert !issue.save
153 assert_equal '01ABC', issue.parent_issue_id
153 assert_equal '01ABC', issue.parent_issue_id
154 assert_include 'Parent task is invalid', issue.errors.full_messages
154 assert_include 'Parent task is invalid', issue.errors.full_messages
155 end
155 end
156
156
157 def test_create_with_invalid_sharp_parent_issue_id
157 def test_create_with_invalid_sharp_parent_issue_id
158 set_language_if_valid 'en'
158 set_language_if_valid 'en'
159 issue = Issue.new(:project_id => 1, :tracker_id => 1,
159 issue = Issue.new(:project_id => 1, :tracker_id => 1,
160 :author_id => 1, :subject => 'Group assignment',
160 :author_id => 1, :subject => 'Group assignment',
161 :parent_issue_id => '#01ABC')
161 :parent_issue_id => '#01ABC')
162 assert !issue.save
162 assert !issue.save
163 assert_equal '#01ABC', issue.parent_issue_id
163 assert_equal '#01ABC', issue.parent_issue_id
164 assert_include 'Parent task is invalid', issue.errors.full_messages
164 assert_include 'Parent task is invalid', issue.errors.full_messages
165 end
165 end
166
166
167 def assert_visibility_match(user, issues)
167 def assert_visibility_match(user, issues)
168 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
168 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
169 end
169 end
170
170
171 def test_visible_scope_for_anonymous
171 def test_visible_scope_for_anonymous
172 # Anonymous user should see issues of public projects only
172 # Anonymous user should see issues of public projects only
173 issues = Issue.visible(User.anonymous).all
173 issues = Issue.visible(User.anonymous).all
174 assert issues.any?
174 assert issues.any?
175 assert_nil issues.detect {|issue| !issue.project.is_public?}
175 assert_nil issues.detect {|issue| !issue.project.is_public?}
176 assert_nil issues.detect {|issue| issue.is_private?}
176 assert_nil issues.detect {|issue| issue.is_private?}
177 assert_visibility_match User.anonymous, issues
177 assert_visibility_match User.anonymous, issues
178 end
178 end
179
179
180 def test_visible_scope_for_anonymous_without_view_issues_permissions
180 def test_visible_scope_for_anonymous_without_view_issues_permissions
181 # Anonymous user should not see issues without permission
181 # Anonymous user should not see issues without permission
182 Role.anonymous.remove_permission!(:view_issues)
182 Role.anonymous.remove_permission!(:view_issues)
183 issues = Issue.visible(User.anonymous).all
183 issues = Issue.visible(User.anonymous).all
184 assert issues.empty?
184 assert issues.empty?
185 assert_visibility_match User.anonymous, issues
185 assert_visibility_match User.anonymous, issues
186 end
186 end
187
187
188 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
188 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
189 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
189 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
190 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
190 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
191 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
191 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
192 assert !issue.visible?(User.anonymous)
192 assert !issue.visible?(User.anonymous)
193 end
193 end
194
194
195 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
195 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
196 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
196 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
197 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
197 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
198 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
198 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
199 assert !issue.visible?(User.anonymous)
199 assert !issue.visible?(User.anonymous)
200 end
200 end
201
201
202 def test_visible_scope_for_non_member
202 def test_visible_scope_for_non_member
203 user = User.find(9)
203 user = User.find(9)
204 assert user.projects.empty?
204 assert user.projects.empty?
205 # Non member user should see issues of public projects only
205 # Non member user should see issues of public projects only
206 issues = Issue.visible(user).all
206 issues = Issue.visible(user).all
207 assert issues.any?
207 assert issues.any?
208 assert_nil issues.detect {|issue| !issue.project.is_public?}
208 assert_nil issues.detect {|issue| !issue.project.is_public?}
209 assert_nil issues.detect {|issue| issue.is_private?}
209 assert_nil issues.detect {|issue| issue.is_private?}
210 assert_visibility_match user, issues
210 assert_visibility_match user, issues
211 end
211 end
212
212
213 def test_visible_scope_for_non_member_with_own_issues_visibility
213 def test_visible_scope_for_non_member_with_own_issues_visibility
214 Role.non_member.update_attribute :issues_visibility, 'own'
214 Role.non_member.update_attribute :issues_visibility, 'own'
215 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
215 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
216 user = User.find(9)
216 user = User.find(9)
217
217
218 issues = Issue.visible(user).all
218 issues = Issue.visible(user).all
219 assert issues.any?
219 assert issues.any?
220 assert_nil issues.detect {|issue| issue.author != user}
220 assert_nil issues.detect {|issue| issue.author != user}
221 assert_visibility_match user, issues
221 assert_visibility_match user, issues
222 end
222 end
223
223
224 def test_visible_scope_for_non_member_without_view_issues_permissions
224 def test_visible_scope_for_non_member_without_view_issues_permissions
225 # Non member user should not see issues without permission
225 # Non member user should not see issues without permission
226 Role.non_member.remove_permission!(:view_issues)
226 Role.non_member.remove_permission!(:view_issues)
227 user = User.find(9)
227 user = User.find(9)
228 assert user.projects.empty?
228 assert user.projects.empty?
229 issues = Issue.visible(user).all
229 issues = Issue.visible(user).all
230 assert issues.empty?
230 assert issues.empty?
231 assert_visibility_match user, issues
231 assert_visibility_match user, issues
232 end
232 end
233
233
234 def test_visible_scope_for_member
234 def test_visible_scope_for_member
235 user = User.find(9)
235 user = User.find(9)
236 # User should see issues of projects for which he has view_issues permissions only
236 # User should see issues of projects for which he has view_issues permissions only
237 Role.non_member.remove_permission!(:view_issues)
237 Role.non_member.remove_permission!(:view_issues)
238 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
238 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
239 issues = Issue.visible(user).all
239 issues = Issue.visible(user).all
240 assert issues.any?
240 assert issues.any?
241 assert_nil issues.detect {|issue| issue.project_id != 3}
241 assert_nil issues.detect {|issue| issue.project_id != 3}
242 assert_nil issues.detect {|issue| issue.is_private?}
242 assert_nil issues.detect {|issue| issue.is_private?}
243 assert_visibility_match user, issues
243 assert_visibility_match user, issues
244 end
244 end
245
245
246 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
246 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
247 user = User.find(8)
247 user = User.find(8)
248 assert user.groups.any?
248 assert user.groups.any?
249 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
249 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
250 Role.non_member.remove_permission!(:view_issues)
250 Role.non_member.remove_permission!(:view_issues)
251
251
252 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
252 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
253 :status_id => 1, :priority => IssuePriority.all.first,
253 :status_id => 1, :priority => IssuePriority.all.first,
254 :subject => 'Assignment test',
254 :subject => 'Assignment test',
255 :assigned_to => user.groups.first,
255 :assigned_to => user.groups.first,
256 :is_private => true)
256 :is_private => true)
257
257
258 Role.find(2).update_attribute :issues_visibility, 'default'
258 Role.find(2).update_attribute :issues_visibility, 'default'
259 issues = Issue.visible(User.find(8)).all
259 issues = Issue.visible(User.find(8)).all
260 assert issues.any?
260 assert issues.any?
261 assert issues.include?(issue)
261 assert issues.include?(issue)
262
262
263 Role.find(2).update_attribute :issues_visibility, 'own'
263 Role.find(2).update_attribute :issues_visibility, 'own'
264 issues = Issue.visible(User.find(8)).all
264 issues = Issue.visible(User.find(8)).all
265 assert issues.any?
265 assert issues.any?
266 assert issues.include?(issue)
266 assert issues.include?(issue)
267 end
267 end
268
268
269 def test_visible_scope_for_admin
269 def test_visible_scope_for_admin
270 user = User.find(1)
270 user = User.find(1)
271 user.members.each(&:destroy)
271 user.members.each(&:destroy)
272 assert user.projects.empty?
272 assert user.projects.empty?
273 issues = Issue.visible(user).all
273 issues = Issue.visible(user).all
274 assert issues.any?
274 assert issues.any?
275 # Admin should see issues on private projects that he does not belong to
275 # Admin should see issues on private projects that he does not belong to
276 assert issues.detect {|issue| !issue.project.is_public?}
276 assert issues.detect {|issue| !issue.project.is_public?}
277 # Admin should see private issues of other users
277 # Admin should see private issues of other users
278 assert issues.detect {|issue| issue.is_private? && issue.author != user}
278 assert issues.detect {|issue| issue.is_private? && issue.author != user}
279 assert_visibility_match user, issues
279 assert_visibility_match user, issues
280 end
280 end
281
281
282 def test_visible_scope_with_project
282 def test_visible_scope_with_project
283 project = Project.find(1)
283 project = Project.find(1)
284 issues = Issue.visible(User.find(2), :project => project).all
284 issues = Issue.visible(User.find(2), :project => project).all
285 projects = issues.collect(&:project).uniq
285 projects = issues.collect(&:project).uniq
286 assert_equal 1, projects.size
286 assert_equal 1, projects.size
287 assert_equal project, projects.first
287 assert_equal project, projects.first
288 end
288 end
289
289
290 def test_visible_scope_with_project_and_subprojects
290 def test_visible_scope_with_project_and_subprojects
291 project = Project.find(1)
291 project = Project.find(1)
292 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
292 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
293 projects = issues.collect(&:project).uniq
293 projects = issues.collect(&:project).uniq
294 assert projects.size > 1
294 assert projects.size > 1
295 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
295 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
296 end
296 end
297
297
298 def test_visible_and_nested_set_scopes
298 def test_visible_and_nested_set_scopes
299 assert_equal 0, Issue.find(1).descendants.visible.all.size
299 assert_equal 0, Issue.find(1).descendants.visible.all.size
300 end
300 end
301
301
302 def test_open_scope
302 def test_open_scope
303 issues = Issue.open.all
303 issues = Issue.open.all
304 assert_nil issues.detect(&:closed?)
304 assert_nil issues.detect(&:closed?)
305 end
305 end
306
306
307 def test_open_scope_with_arg
307 def test_open_scope_with_arg
308 issues = Issue.open(false).all
308 issues = Issue.open(false).all
309 assert_equal issues, issues.select(&:closed?)
309 assert_equal issues, issues.select(&:closed?)
310 end
310 end
311
311
312 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
312 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
313 version = Version.find(2)
313 version = Version.find(2)
314 assert version.fixed_issues.any?
314 assert version.fixed_issues.any?
315 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
315 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
316 end
316 end
317
317
318 def test_fixed_version_scope_with_empty_array_should_return_no_result
318 def test_fixed_version_scope_with_empty_array_should_return_no_result
319 assert_equal 0, Issue.fixed_version([]).count
319 assert_equal 0, Issue.fixed_version([]).count
320 end
320 end
321
321
322 def test_errors_full_messages_should_include_custom_fields_errors
322 def test_errors_full_messages_should_include_custom_fields_errors
323 field = IssueCustomField.find_by_name('Database')
323 field = IssueCustomField.find_by_name('Database')
324
324
325 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
325 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
326 :status_id => 1, :subject => 'test_create',
326 :status_id => 1, :subject => 'test_create',
327 :description => 'IssueTest#test_create_with_required_custom_field')
327 :description => 'IssueTest#test_create_with_required_custom_field')
328 assert issue.available_custom_fields.include?(field)
328 assert issue.available_custom_fields.include?(field)
329 # Invalid value
329 # Invalid value
330 issue.custom_field_values = { field.id => 'SQLServer' }
330 issue.custom_field_values = { field.id => 'SQLServer' }
331
331
332 assert !issue.valid?
332 assert !issue.valid?
333 assert_equal 1, issue.errors.full_messages.size
333 assert_equal 1, issue.errors.full_messages.size
334 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
334 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
335 issue.errors.full_messages.first
335 issue.errors.full_messages.first
336 end
336 end
337
337
338 def test_update_issue_with_required_custom_field
338 def test_update_issue_with_required_custom_field
339 field = IssueCustomField.find_by_name('Database')
339 field = IssueCustomField.find_by_name('Database')
340 field.update_attribute(:is_required, true)
340 field.update_attribute(:is_required, true)
341
341
342 issue = Issue.find(1)
342 issue = Issue.find(1)
343 assert_nil issue.custom_value_for(field)
343 assert_nil issue.custom_value_for(field)
344 assert issue.available_custom_fields.include?(field)
344 assert issue.available_custom_fields.include?(field)
345 # No change to custom values, issue can be saved
345 # No change to custom values, issue can be saved
346 assert issue.save
346 assert issue.save
347 # Blank value
347 # Blank value
348 issue.custom_field_values = { field.id => '' }
348 issue.custom_field_values = { field.id => '' }
349 assert !issue.save
349 assert !issue.save
350 # Valid value
350 # Valid value
351 issue.custom_field_values = { field.id => 'PostgreSQL' }
351 issue.custom_field_values = { field.id => 'PostgreSQL' }
352 assert issue.save
352 assert issue.save
353 issue.reload
353 issue.reload
354 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
354 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
355 end
355 end
356
356
357 def test_should_not_update_attributes_if_custom_fields_validation_fails
357 def test_should_not_update_attributes_if_custom_fields_validation_fails
358 issue = Issue.find(1)
358 issue = Issue.find(1)
359 field = IssueCustomField.find_by_name('Database')
359 field = IssueCustomField.find_by_name('Database')
360 assert issue.available_custom_fields.include?(field)
360 assert issue.available_custom_fields.include?(field)
361
361
362 issue.custom_field_values = { field.id => 'Invalid' }
362 issue.custom_field_values = { field.id => 'Invalid' }
363 issue.subject = 'Should be not be saved'
363 issue.subject = 'Should be not be saved'
364 assert !issue.save
364 assert !issue.save
365
365
366 issue.reload
366 issue.reload
367 assert_equal "Can't print recipes", issue.subject
367 assert_equal "Can't print recipes", issue.subject
368 end
368 end
369
369
370 def test_should_not_recreate_custom_values_objects_on_update
370 def test_should_not_recreate_custom_values_objects_on_update
371 field = IssueCustomField.find_by_name('Database')
371 field = IssueCustomField.find_by_name('Database')
372
372
373 issue = Issue.find(1)
373 issue = Issue.find(1)
374 issue.custom_field_values = { field.id => 'PostgreSQL' }
374 issue.custom_field_values = { field.id => 'PostgreSQL' }
375 assert issue.save
375 assert issue.save
376 custom_value = issue.custom_value_for(field)
376 custom_value = issue.custom_value_for(field)
377 issue.reload
377 issue.reload
378 issue.custom_field_values = { field.id => 'MySQL' }
378 issue.custom_field_values = { field.id => 'MySQL' }
379 assert issue.save
379 assert issue.save
380 issue.reload
380 issue.reload
381 assert_equal custom_value.id, issue.custom_value_for(field).id
381 assert_equal custom_value.id, issue.custom_value_for(field).id
382 end
382 end
383
383
384 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
384 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
385 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
385 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
386 :status_id => 1, :subject => 'Test',
386 :status_id => 1, :subject => 'Test',
387 :custom_field_values => {'2' => 'Test'})
387 :custom_field_values => {'2' => 'Test'})
388 assert !Tracker.find(2).custom_field_ids.include?(2)
388 assert !Tracker.find(2).custom_field_ids.include?(2)
389
389
390 issue = Issue.find(issue.id)
390 issue = Issue.find(issue.id)
391 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
391 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
392
392
393 issue = Issue.find(issue.id)
393 issue = Issue.find(issue.id)
394 custom_value = issue.custom_value_for(2)
394 custom_value = issue.custom_value_for(2)
395 assert_not_nil custom_value
395 assert_not_nil custom_value
396 assert_equal 'Test', custom_value.value
396 assert_equal 'Test', custom_value.value
397 end
397 end
398
398
399 def test_assigning_tracker_id_should_reload_custom_fields_values
399 def test_assigning_tracker_id_should_reload_custom_fields_values
400 issue = Issue.new(:project => Project.find(1))
400 issue = Issue.new(:project => Project.find(1))
401 assert issue.custom_field_values.empty?
401 assert issue.custom_field_values.empty?
402 issue.tracker_id = 1
402 issue.tracker_id = 1
403 assert issue.custom_field_values.any?
403 assert issue.custom_field_values.any?
404 end
404 end
405
405
406 def test_assigning_attributes_should_assign_project_and_tracker_first
406 def test_assigning_attributes_should_assign_project_and_tracker_first
407 seq = sequence('seq')
407 seq = sequence('seq')
408 issue = Issue.new
408 issue = Issue.new
409 issue.expects(:project_id=).in_sequence(seq)
409 issue.expects(:project_id=).in_sequence(seq)
410 issue.expects(:tracker_id=).in_sequence(seq)
410 issue.expects(:tracker_id=).in_sequence(seq)
411 issue.expects(:subject=).in_sequence(seq)
411 issue.expects(:subject=).in_sequence(seq)
412 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
412 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
413 end
413 end
414
414
415 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
415 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
416 attributes = ActiveSupport::OrderedHash.new
416 attributes = ActiveSupport::OrderedHash.new
417 attributes['custom_field_values'] = { '1' => 'MySQL' }
417 attributes['custom_field_values'] = { '1' => 'MySQL' }
418 attributes['tracker_id'] = '1'
418 attributes['tracker_id'] = '1'
419 issue = Issue.new(:project => Project.find(1))
419 issue = Issue.new(:project => Project.find(1))
420 issue.attributes = attributes
420 issue.attributes = attributes
421 assert_equal 'MySQL', issue.custom_field_value(1)
421 assert_equal 'MySQL', issue.custom_field_value(1)
422 end
422 end
423
423
424 def test_should_update_issue_with_disabled_tracker
424 def test_should_update_issue_with_disabled_tracker
425 p = Project.find(1)
425 p = Project.find(1)
426 issue = Issue.find(1)
426 issue = Issue.find(1)
427
427
428 p.trackers.delete(issue.tracker)
428 p.trackers.delete(issue.tracker)
429 assert !p.trackers.include?(issue.tracker)
429 assert !p.trackers.include?(issue.tracker)
430
430
431 issue.reload
431 issue.reload
432 issue.subject = 'New subject'
432 issue.subject = 'New subject'
433 assert issue.save
433 assert issue.save
434 end
434 end
435
435
436 def test_should_not_set_a_disabled_tracker
436 def test_should_not_set_a_disabled_tracker
437 p = Project.find(1)
437 p = Project.find(1)
438 p.trackers.delete(Tracker.find(2))
438 p.trackers.delete(Tracker.find(2))
439
439
440 issue = Issue.find(1)
440 issue = Issue.find(1)
441 issue.tracker_id = 2
441 issue.tracker_id = 2
442 issue.subject = 'New subject'
442 issue.subject = 'New subject'
443 assert !issue.save
443 assert !issue.save
444 assert_not_nil issue.errors[:tracker_id]
444 assert_not_nil issue.errors[:tracker_id]
445 end
445 end
446
446
447 def test_category_based_assignment
447 def test_category_based_assignment
448 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
448 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
449 :status_id => 1, :priority => IssuePriority.all.first,
449 :status_id => 1, :priority => IssuePriority.all.first,
450 :subject => 'Assignment test',
450 :subject => 'Assignment test',
451 :description => 'Assignment test', :category_id => 1)
451 :description => 'Assignment test', :category_id => 1)
452 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
452 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
453 end
453 end
454
454
455 def test_new_statuses_allowed_to
455 def test_new_statuses_allowed_to
456 WorkflowTransition.delete_all
456 WorkflowTransition.delete_all
457 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
457 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
458 :old_status_id => 1, :new_status_id => 2,
458 :old_status_id => 1, :new_status_id => 2,
459 :author => false, :assignee => false)
459 :author => false, :assignee => false)
460 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
460 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
461 :old_status_id => 1, :new_status_id => 3,
461 :old_status_id => 1, :new_status_id => 3,
462 :author => true, :assignee => false)
462 :author => true, :assignee => false)
463 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
463 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
464 :new_status_id => 4, :author => false,
464 :new_status_id => 4, :author => false,
465 :assignee => true)
465 :assignee => true)
466 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
466 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
467 :old_status_id => 1, :new_status_id => 5,
467 :old_status_id => 1, :new_status_id => 5,
468 :author => true, :assignee => true)
468 :author => true, :assignee => true)
469 status = IssueStatus.find(1)
469 status = IssueStatus.find(1)
470 role = Role.find(1)
470 role = Role.find(1)
471 tracker = Tracker.find(1)
471 tracker = Tracker.find(1)
472 user = User.find(2)
472 user = User.find(2)
473
473
474 issue = Issue.generate!(:tracker => tracker, :status => status,
474 issue = Issue.generate!(:tracker => tracker, :status => status,
475 :project_id => 1, :author_id => 1)
475 :project_id => 1, :author_id => 1)
476 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
476 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
477
477
478 issue = Issue.generate!(:tracker => tracker, :status => status,
478 issue = Issue.generate!(:tracker => tracker, :status => status,
479 :project_id => 1, :author => user)
479 :project_id => 1, :author => user)
480 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
480 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
481
481
482 issue = Issue.generate!(:tracker => tracker, :status => status,
482 issue = Issue.generate!(:tracker => tracker, :status => status,
483 :project_id => 1, :author_id => 1,
483 :project_id => 1, :author_id => 1,
484 :assigned_to => user)
484 :assigned_to => user)
485 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
485 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
486
486
487 issue = Issue.generate!(:tracker => tracker, :status => status,
487 issue = Issue.generate!(:tracker => tracker, :status => status,
488 :project_id => 1, :author => user,
488 :project_id => 1, :author => user,
489 :assigned_to => user)
489 :assigned_to => user)
490 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
490 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
491 end
491 end
492
492
493 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
493 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
494 admin = User.find(1)
494 admin = User.find(1)
495 issue = Issue.find(1)
495 issue = Issue.find(1)
496 assert !admin.member_of?(issue.project)
496 assert !admin.member_of?(issue.project)
497 expected_statuses = [issue.status] +
497 expected_statuses = [issue.status] +
498 WorkflowTransition.find_all_by_old_status_id(
498 WorkflowTransition.find_all_by_old_status_id(
499 issue.status_id).map(&:new_status).uniq.sort
499 issue.status_id).map(&:new_status).uniq.sort
500 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
500 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
501 end
501 end
502
502
503 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
503 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
504 issue = Issue.find(1).copy
504 issue = Issue.find(1).copy
505 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
505 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
506
506
507 issue = Issue.find(2).copy
507 issue = Issue.find(2).copy
508 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
508 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
509 end
509 end
510
510
511 def test_safe_attributes_names_should_not_include_disabled_field
511 def test_safe_attributes_names_should_not_include_disabled_field
512 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
512 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
513
513
514 issue = Issue.new(:tracker => tracker)
514 issue = Issue.new(:tracker => tracker)
515 assert_include 'tracker_id', issue.safe_attribute_names
515 assert_include 'tracker_id', issue.safe_attribute_names
516 assert_include 'status_id', issue.safe_attribute_names
516 assert_include 'status_id', issue.safe_attribute_names
517 assert_include 'subject', issue.safe_attribute_names
517 assert_include 'subject', issue.safe_attribute_names
518 assert_include 'description', issue.safe_attribute_names
518 assert_include 'description', issue.safe_attribute_names
519 assert_include 'custom_field_values', issue.safe_attribute_names
519 assert_include 'custom_field_values', issue.safe_attribute_names
520 assert_include 'custom_fields', issue.safe_attribute_names
520 assert_include 'custom_fields', issue.safe_attribute_names
521 assert_include 'lock_version', issue.safe_attribute_names
521 assert_include 'lock_version', issue.safe_attribute_names
522
522
523 tracker.core_fields.each do |field|
523 tracker.core_fields.each do |field|
524 assert_include field, issue.safe_attribute_names
524 assert_include field, issue.safe_attribute_names
525 end
525 end
526
526
527 tracker.disabled_core_fields.each do |field|
527 tracker.disabled_core_fields.each do |field|
528 assert_not_include field, issue.safe_attribute_names
528 assert_not_include field, issue.safe_attribute_names
529 end
529 end
530 end
530 end
531
531
532 def test_safe_attributes_should_ignore_disabled_fields
532 def test_safe_attributes_should_ignore_disabled_fields
533 tracker = Tracker.find(1)
533 tracker = Tracker.find(1)
534 tracker.core_fields = %w(assigned_to_id due_date)
534 tracker.core_fields = %w(assigned_to_id due_date)
535 tracker.save!
535 tracker.save!
536
536
537 issue = Issue.new(:tracker => tracker)
537 issue = Issue.new(:tracker => tracker)
538 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
538 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
539 assert_nil issue.start_date
539 assert_nil issue.start_date
540 assert_equal Date.parse('2012-07-14'), issue.due_date
540 assert_equal Date.parse('2012-07-14'), issue.due_date
541 end
541 end
542
542
543 def test_safe_attributes_should_accept_target_tracker_enabled_fields
543 def test_safe_attributes_should_accept_target_tracker_enabled_fields
544 source = Tracker.find(1)
544 source = Tracker.find(1)
545 source.core_fields = []
545 source.core_fields = []
546 source.save!
546 source.save!
547 target = Tracker.find(2)
547 target = Tracker.find(2)
548 target.core_fields = %w(assigned_to_id due_date)
548 target.core_fields = %w(assigned_to_id due_date)
549 target.save!
549 target.save!
550
550
551 issue = Issue.new(:tracker => source)
551 issue = Issue.new(:tracker => source)
552 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
552 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
553 assert_equal target, issue.tracker
553 assert_equal target, issue.tracker
554 assert_equal Date.parse('2012-07-14'), issue.due_date
554 assert_equal Date.parse('2012-07-14'), issue.due_date
555 end
555 end
556
556
557 def test_safe_attributes_should_not_include_readonly_fields
557 def test_safe_attributes_should_not_include_readonly_fields
558 WorkflowPermission.delete_all
558 WorkflowPermission.delete_all
559 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
559 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
560 :role_id => 1, :field_name => 'due_date',
560 :role_id => 1, :field_name => 'due_date',
561 :rule => 'readonly')
561 :rule => 'readonly')
562 user = User.find(2)
562 user = User.find(2)
563
563
564 issue = Issue.new(:project_id => 1, :tracker_id => 1)
564 issue = Issue.new(:project_id => 1, :tracker_id => 1)
565 assert_equal %w(due_date), issue.read_only_attribute_names(user)
565 assert_equal %w(due_date), issue.read_only_attribute_names(user)
566 assert_not_include 'due_date', issue.safe_attribute_names(user)
566 assert_not_include 'due_date', issue.safe_attribute_names(user)
567
567
568 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
568 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
569 assert_equal Date.parse('2012-07-14'), issue.start_date
569 assert_equal Date.parse('2012-07-14'), issue.start_date
570 assert_nil issue.due_date
570 assert_nil issue.due_date
571 end
571 end
572
572
573 def test_safe_attributes_should_not_include_readonly_custom_fields
573 def test_safe_attributes_should_not_include_readonly_custom_fields
574 cf1 = IssueCustomField.create!(:name => 'Writable field',
574 cf1 = IssueCustomField.create!(:name => 'Writable field',
575 :field_format => 'string',
575 :field_format => 'string',
576 :is_for_all => true, :tracker_ids => [1])
576 :is_for_all => true, :tracker_ids => [1])
577 cf2 = IssueCustomField.create!(:name => 'Readonly field',
577 cf2 = IssueCustomField.create!(:name => 'Readonly field',
578 :field_format => 'string',
578 :field_format => 'string',
579 :is_for_all => true, :tracker_ids => [1])
579 :is_for_all => true, :tracker_ids => [1])
580 WorkflowPermission.delete_all
580 WorkflowPermission.delete_all
581 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
581 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
582 :role_id => 1, :field_name => cf2.id.to_s,
582 :role_id => 1, :field_name => cf2.id.to_s,
583 :rule => 'readonly')
583 :rule => 'readonly')
584 user = User.find(2)
584 user = User.find(2)
585 issue = Issue.new(:project_id => 1, :tracker_id => 1)
585 issue = Issue.new(:project_id => 1, :tracker_id => 1)
586 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
586 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
587 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
587 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
588
588
589 issue.send :safe_attributes=, {'custom_field_values' => {
589 issue.send :safe_attributes=, {'custom_field_values' => {
590 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
590 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
591 }}, user
591 }}, user
592 assert_equal 'value1', issue.custom_field_value(cf1)
592 assert_equal 'value1', issue.custom_field_value(cf1)
593 assert_nil issue.custom_field_value(cf2)
593 assert_nil issue.custom_field_value(cf2)
594
594
595 issue.send :safe_attributes=, {'custom_fields' => [
595 issue.send :safe_attributes=, {'custom_fields' => [
596 {'id' => cf1.id.to_s, 'value' => 'valuea'},
596 {'id' => cf1.id.to_s, 'value' => 'valuea'},
597 {'id' => cf2.id.to_s, 'value' => 'valueb'}
597 {'id' => cf2.id.to_s, 'value' => 'valueb'}
598 ]}, user
598 ]}, user
599 assert_equal 'valuea', issue.custom_field_value(cf1)
599 assert_equal 'valuea', issue.custom_field_value(cf1)
600 assert_nil issue.custom_field_value(cf2)
600 assert_nil issue.custom_field_value(cf2)
601 end
601 end
602
602
603 def test_editable_custom_field_values_should_return_non_readonly_custom_values
603 def test_editable_custom_field_values_should_return_non_readonly_custom_values
604 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
604 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
605 :is_for_all => true, :tracker_ids => [1, 2])
605 :is_for_all => true, :tracker_ids => [1, 2])
606 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
606 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
607 :is_for_all => true, :tracker_ids => [1, 2])
607 :is_for_all => true, :tracker_ids => [1, 2])
608 WorkflowPermission.delete_all
608 WorkflowPermission.delete_all
609 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
609 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
610 :field_name => cf2.id.to_s, :rule => 'readonly')
610 :field_name => cf2.id.to_s, :rule => 'readonly')
611 user = User.find(2)
611 user = User.find(2)
612
612
613 issue = Issue.new(:project_id => 1, :tracker_id => 1)
613 issue = Issue.new(:project_id => 1, :tracker_id => 1)
614 values = issue.editable_custom_field_values(user)
614 values = issue.editable_custom_field_values(user)
615 assert values.detect {|value| value.custom_field == cf1}
615 assert values.detect {|value| value.custom_field == cf1}
616 assert_nil values.detect {|value| value.custom_field == cf2}
616 assert_nil values.detect {|value| value.custom_field == cf2}
617
617
618 issue.tracker_id = 2
618 issue.tracker_id = 2
619 values = issue.editable_custom_field_values(user)
619 values = issue.editable_custom_field_values(user)
620 assert values.detect {|value| value.custom_field == cf1}
620 assert values.detect {|value| value.custom_field == cf1}
621 assert values.detect {|value| value.custom_field == cf2}
621 assert values.detect {|value| value.custom_field == cf2}
622 end
622 end
623
623
624 def test_safe_attributes_should_accept_target_tracker_writable_fields
624 def test_safe_attributes_should_accept_target_tracker_writable_fields
625 WorkflowPermission.delete_all
625 WorkflowPermission.delete_all
626 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
626 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
627 :role_id => 1, :field_name => 'due_date',
627 :role_id => 1, :field_name => 'due_date',
628 :rule => 'readonly')
628 :rule => 'readonly')
629 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
629 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
630 :role_id => 1, :field_name => 'start_date',
630 :role_id => 1, :field_name => 'start_date',
631 :rule => 'readonly')
631 :rule => 'readonly')
632 user = User.find(2)
632 user = User.find(2)
633
633
634 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
634 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
635
635
636 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
636 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
637 'due_date' => '2012-07-14'}, user
637 'due_date' => '2012-07-14'}, user
638 assert_equal Date.parse('2012-07-12'), issue.start_date
638 assert_equal Date.parse('2012-07-12'), issue.start_date
639 assert_nil issue.due_date
639 assert_nil issue.due_date
640
640
641 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
641 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
642 'due_date' => '2012-07-16',
642 'due_date' => '2012-07-16',
643 'tracker_id' => 2}, user
643 'tracker_id' => 2}, user
644 assert_equal Date.parse('2012-07-12'), issue.start_date
644 assert_equal Date.parse('2012-07-12'), issue.start_date
645 assert_equal Date.parse('2012-07-16'), issue.due_date
645 assert_equal Date.parse('2012-07-16'), issue.due_date
646 end
646 end
647
647
648 def test_safe_attributes_should_accept_target_status_writable_fields
648 def test_safe_attributes_should_accept_target_status_writable_fields
649 WorkflowPermission.delete_all
649 WorkflowPermission.delete_all
650 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
650 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
651 :role_id => 1, :field_name => 'due_date',
651 :role_id => 1, :field_name => 'due_date',
652 :rule => 'readonly')
652 :rule => 'readonly')
653 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
653 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
654 :role_id => 1, :field_name => 'start_date',
654 :role_id => 1, :field_name => 'start_date',
655 :rule => 'readonly')
655 :rule => 'readonly')
656 user = User.find(2)
656 user = User.find(2)
657
657
658 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
658 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
659
659
660 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
660 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
661 'due_date' => '2012-07-14'},
661 'due_date' => '2012-07-14'},
662 user
662 user
663 assert_equal Date.parse('2012-07-12'), issue.start_date
663 assert_equal Date.parse('2012-07-12'), issue.start_date
664 assert_nil issue.due_date
664 assert_nil issue.due_date
665
665
666 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
666 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
667 'due_date' => '2012-07-16',
667 'due_date' => '2012-07-16',
668 'status_id' => 2},
668 'status_id' => 2},
669 user
669 user
670 assert_equal Date.parse('2012-07-12'), issue.start_date
670 assert_equal Date.parse('2012-07-12'), issue.start_date
671 assert_equal Date.parse('2012-07-16'), issue.due_date
671 assert_equal Date.parse('2012-07-16'), issue.due_date
672 end
672 end
673
673
674 def test_required_attributes_should_be_validated
674 def test_required_attributes_should_be_validated
675 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
675 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
676 :is_for_all => true, :tracker_ids => [1, 2])
676 :is_for_all => true, :tracker_ids => [1, 2])
677
677
678 WorkflowPermission.delete_all
678 WorkflowPermission.delete_all
679 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
679 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
680 :role_id => 1, :field_name => 'due_date',
680 :role_id => 1, :field_name => 'due_date',
681 :rule => 'required')
681 :rule => 'required')
682 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
682 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
683 :role_id => 1, :field_name => 'category_id',
683 :role_id => 1, :field_name => 'category_id',
684 :rule => 'required')
684 :rule => 'required')
685 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
685 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
686 :role_id => 1, :field_name => cf.id.to_s,
686 :role_id => 1, :field_name => cf.id.to_s,
687 :rule => 'required')
687 :rule => 'required')
688
688
689 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
689 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
690 :role_id => 1, :field_name => 'start_date',
690 :role_id => 1, :field_name => 'start_date',
691 :rule => 'required')
691 :rule => 'required')
692 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
692 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
693 :role_id => 1, :field_name => cf.id.to_s,
693 :role_id => 1, :field_name => cf.id.to_s,
694 :rule => 'required')
694 :rule => 'required')
695 user = User.find(2)
695 user = User.find(2)
696
696
697 issue = Issue.new(:project_id => 1, :tracker_id => 1,
697 issue = Issue.new(:project_id => 1, :tracker_id => 1,
698 :status_id => 1, :subject => 'Required fields',
698 :status_id => 1, :subject => 'Required fields',
699 :author => user)
699 :author => user)
700 assert_equal [cf.id.to_s, "category_id", "due_date"],
700 assert_equal [cf.id.to_s, "category_id", "due_date"],
701 issue.required_attribute_names(user).sort
701 issue.required_attribute_names(user).sort
702 assert !issue.save, "Issue was saved"
702 assert !issue.save, "Issue was saved"
703 assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"],
703 assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"],
704 issue.errors.full_messages.sort
704 issue.errors.full_messages.sort
705
705
706 issue.tracker_id = 2
706 issue.tracker_id = 2
707 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
707 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
708 assert !issue.save, "Issue was saved"
708 assert !issue.save, "Issue was saved"
709 assert_equal ["Foo can't be blank", "Start date can't be blank"],
709 assert_equal ["Foo can't be blank", "Start date can't be blank"],
710 issue.errors.full_messages.sort
710 issue.errors.full_messages.sort
711
711
712 issue.start_date = Date.today
712 issue.start_date = Date.today
713 issue.custom_field_values = {cf.id.to_s => 'bar'}
713 issue.custom_field_values = {cf.id.to_s => 'bar'}
714 assert issue.save
714 assert issue.save
715 end
715 end
716
716
717 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
717 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
718 WorkflowPermission.delete_all
718 WorkflowPermission.delete_all
719 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
719 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
720 :role_id => 1, :field_name => 'due_date',
720 :role_id => 1, :field_name => 'due_date',
721 :rule => 'required')
721 :rule => 'required')
722 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
722 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
723 :role_id => 1, :field_name => 'start_date',
723 :role_id => 1, :field_name => 'start_date',
724 :rule => 'required')
724 :rule => 'required')
725 user = User.find(2)
725 user = User.find(2)
726 member = Member.find(1)
726 member = Member.find(1)
727 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
727 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
728
728
729 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
729 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
730
730
731 member.role_ids = [1, 2]
731 member.role_ids = [1, 2]
732 member.save!
732 member.save!
733 assert_equal [], issue.required_attribute_names(user.reload)
733 assert_equal [], issue.required_attribute_names(user.reload)
734
734
735 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
735 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
736 :role_id => 2, :field_name => 'due_date',
736 :role_id => 2, :field_name => 'due_date',
737 :rule => 'required')
737 :rule => 'required')
738 assert_equal %w(due_date), issue.required_attribute_names(user)
738 assert_equal %w(due_date), issue.required_attribute_names(user)
739
739
740 member.role_ids = [1, 2, 3]
740 member.role_ids = [1, 2, 3]
741 member.save!
741 member.save!
742 assert_equal [], issue.required_attribute_names(user.reload)
742 assert_equal [], issue.required_attribute_names(user.reload)
743
743
744 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
744 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
745 :role_id => 2, :field_name => 'due_date',
745 :role_id => 2, :field_name => 'due_date',
746 :rule => 'readonly')
746 :rule => 'readonly')
747 # required + readonly => required
747 # required + readonly => required
748 assert_equal %w(due_date), issue.required_attribute_names(user)
748 assert_equal %w(due_date), issue.required_attribute_names(user)
749 end
749 end
750
750
751 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
751 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
752 WorkflowPermission.delete_all
752 WorkflowPermission.delete_all
753 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
753 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
754 :role_id => 1, :field_name => 'due_date',
754 :role_id => 1, :field_name => 'due_date',
755 :rule => 'readonly')
755 :rule => 'readonly')
756 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
756 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
757 :role_id => 1, :field_name => 'start_date',
757 :role_id => 1, :field_name => 'start_date',
758 :rule => 'readonly')
758 :rule => 'readonly')
759 user = User.find(2)
759 user = User.find(2)
760 member = Member.find(1)
760 member = Member.find(1)
761 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
761 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
762
762
763 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
763 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
764
764
765 member.role_ids = [1, 2]
765 member.role_ids = [1, 2]
766 member.save!
766 member.save!
767 assert_equal [], issue.read_only_attribute_names(user.reload)
767 assert_equal [], issue.read_only_attribute_names(user.reload)
768
768
769 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
769 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
770 :role_id => 2, :field_name => 'due_date',
770 :role_id => 2, :field_name => 'due_date',
771 :rule => 'readonly')
771 :rule => 'readonly')
772 assert_equal %w(due_date), issue.read_only_attribute_names(user)
772 assert_equal %w(due_date), issue.read_only_attribute_names(user)
773 end
773 end
774
774
775 def test_copy
775 def test_copy
776 issue = Issue.new.copy_from(1)
776 issue = Issue.new.copy_from(1)
777 assert issue.copy?
777 assert issue.copy?
778 assert issue.save
778 assert issue.save
779 issue.reload
779 issue.reload
780 orig = Issue.find(1)
780 orig = Issue.find(1)
781 assert_equal orig.subject, issue.subject
781 assert_equal orig.subject, issue.subject
782 assert_equal orig.tracker, issue.tracker
782 assert_equal orig.tracker, issue.tracker
783 assert_equal "125", issue.custom_value_for(2).value
783 assert_equal "125", issue.custom_value_for(2).value
784 end
784 end
785
785
786 def test_copy_should_copy_status
786 def test_copy_should_copy_status
787 orig = Issue.find(8)
787 orig = Issue.find(8)
788 assert orig.status != IssueStatus.default
788 assert orig.status != IssueStatus.default
789
789
790 issue = Issue.new.copy_from(orig)
790 issue = Issue.new.copy_from(orig)
791 assert issue.save
791 assert issue.save
792 issue.reload
792 issue.reload
793 assert_equal orig.status, issue.status
793 assert_equal orig.status, issue.status
794 end
794 end
795
795
796 def test_copy_should_add_relation_with_copied_issue
796 def test_copy_should_add_relation_with_copied_issue
797 copied = Issue.find(1)
797 copied = Issue.find(1)
798 issue = Issue.new.copy_from(copied)
798 issue = Issue.new.copy_from(copied)
799 assert issue.save
799 assert issue.save
800 issue.reload
800 issue.reload
801
801
802 assert_equal 1, issue.relations.size
802 assert_equal 1, issue.relations.size
803 relation = issue.relations.first
803 relation = issue.relations.first
804 assert_equal 'copied_to', relation.relation_type
804 assert_equal 'copied_to', relation.relation_type
805 assert_equal copied, relation.issue_from
805 assert_equal copied, relation.issue_from
806 assert_equal issue, relation.issue_to
806 assert_equal issue, relation.issue_to
807 end
807 end
808
808
809 def test_copy_should_copy_subtasks
809 def test_copy_should_copy_subtasks
810 issue = Issue.generate_with_descendants!
810 issue = Issue.generate_with_descendants!
811
811
812 copy = issue.reload.copy
812 copy = issue.reload.copy
813 copy.author = User.find(7)
813 copy.author = User.find(7)
814 assert_difference 'Issue.count', 1+issue.descendants.count do
814 assert_difference 'Issue.count', 1+issue.descendants.count do
815 assert copy.save
815 assert copy.save
816 end
816 end
817 copy.reload
817 copy.reload
818 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
818 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
819 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
819 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
820 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
820 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
821 assert_equal copy.author, child_copy.author
821 assert_equal copy.author, child_copy.author
822 end
822 end
823
823
824 def test_copy_should_copy_subtasks_to_target_project
824 def test_copy_should_copy_subtasks_to_target_project
825 issue = Issue.generate_with_descendants!
825 issue = Issue.generate_with_descendants!
826
826
827 copy = issue.copy(:project_id => 3)
827 copy = issue.copy(:project_id => 3)
828 assert_difference 'Issue.count', 1+issue.descendants.count do
828 assert_difference 'Issue.count', 1+issue.descendants.count do
829 assert copy.save
829 assert copy.save
830 end
830 end
831 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
831 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
832 end
832 end
833
833
834 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
834 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
835 issue = Issue.generate_with_descendants!
835 issue = Issue.generate_with_descendants!
836
836
837 copy = issue.reload.copy
837 copy = issue.reload.copy
838 assert_difference 'Issue.count', 1+issue.descendants.count do
838 assert_difference 'Issue.count', 1+issue.descendants.count do
839 assert copy.save
839 assert copy.save
840 assert copy.save
840 assert copy.save
841 end
841 end
842 end
842 end
843
843
844 def test_should_not_call_after_project_change_on_creation
844 def test_should_not_call_after_project_change_on_creation
845 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
845 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
846 :subject => 'Test', :author_id => 1)
846 :subject => 'Test', :author_id => 1)
847 issue.expects(:after_project_change).never
847 issue.expects(:after_project_change).never
848 issue.save!
848 issue.save!
849 end
849 end
850
850
851 def test_should_not_call_after_project_change_on_update
851 def test_should_not_call_after_project_change_on_update
852 issue = Issue.find(1)
852 issue = Issue.find(1)
853 issue.project = Project.find(1)
853 issue.project = Project.find(1)
854 issue.subject = 'No project change'
854 issue.subject = 'No project change'
855 issue.expects(:after_project_change).never
855 issue.expects(:after_project_change).never
856 issue.save!
856 issue.save!
857 end
857 end
858
858
859 def test_should_call_after_project_change_on_project_change
859 def test_should_call_after_project_change_on_project_change
860 issue = Issue.find(1)
860 issue = Issue.find(1)
861 issue.project = Project.find(2)
861 issue.project = Project.find(2)
862 issue.expects(:after_project_change).once
862 issue.expects(:after_project_change).once
863 issue.save!
863 issue.save!
864 end
864 end
865
865
866 def test_adding_journal_should_update_timestamp
866 def test_adding_journal_should_update_timestamp
867 issue = Issue.find(1)
867 issue = Issue.find(1)
868 updated_on_was = issue.updated_on
868 updated_on_was = issue.updated_on
869
869
870 issue.init_journal(User.first, "Adding notes")
870 issue.init_journal(User.first, "Adding notes")
871 assert_difference 'Journal.count' do
871 assert_difference 'Journal.count' do
872 assert issue.save
872 assert issue.save
873 end
873 end
874 issue.reload
874 issue.reload
875
875
876 assert_not_equal updated_on_was, issue.updated_on
876 assert_not_equal updated_on_was, issue.updated_on
877 end
877 end
878
878
879 def test_should_close_duplicates
879 def test_should_close_duplicates
880 # Create 3 issues
880 # Create 3 issues
881 issue1 = Issue.generate!
881 issue1 = Issue.generate!
882 issue2 = Issue.generate!
882 issue2 = Issue.generate!
883 issue3 = Issue.generate!
883 issue3 = Issue.generate!
884
884
885 # 2 is a dupe of 1
885 # 2 is a dupe of 1
886 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
886 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
887 :relation_type => IssueRelation::TYPE_DUPLICATES)
887 :relation_type => IssueRelation::TYPE_DUPLICATES)
888 # And 3 is a dupe of 2
888 # And 3 is a dupe of 2
889 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
889 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
890 :relation_type => IssueRelation::TYPE_DUPLICATES)
890 :relation_type => IssueRelation::TYPE_DUPLICATES)
891 # And 3 is a dupe of 1 (circular duplicates)
891 # And 3 is a dupe of 1 (circular duplicates)
892 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
892 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
893 :relation_type => IssueRelation::TYPE_DUPLICATES)
893 :relation_type => IssueRelation::TYPE_DUPLICATES)
894
894
895 assert issue1.reload.duplicates.include?(issue2)
895 assert issue1.reload.duplicates.include?(issue2)
896
896
897 # Closing issue 1
897 # Closing issue 1
898 issue1.init_journal(User.first, "Closing issue1")
898 issue1.init_journal(User.first, "Closing issue1")
899 issue1.status = IssueStatus.where(:is_closed => true).first
899 issue1.status = IssueStatus.where(:is_closed => true).first
900 assert issue1.save
900 assert issue1.save
901 # 2 and 3 should be also closed
901 # 2 and 3 should be also closed
902 assert issue2.reload.closed?
902 assert issue2.reload.closed?
903 assert issue3.reload.closed?
903 assert issue3.reload.closed?
904 end
904 end
905
905
906 def test_should_not_close_duplicated_issue
906 def test_should_not_close_duplicated_issue
907 issue1 = Issue.generate!
907 issue1 = Issue.generate!
908 issue2 = Issue.generate!
908 issue2 = Issue.generate!
909
909
910 # 2 is a dupe of 1
910 # 2 is a dupe of 1
911 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
911 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
912 :relation_type => IssueRelation::TYPE_DUPLICATES)
912 :relation_type => IssueRelation::TYPE_DUPLICATES)
913 # 2 is a dup of 1 but 1 is not a duplicate of 2
913 # 2 is a dup of 1 but 1 is not a duplicate of 2
914 assert !issue2.reload.duplicates.include?(issue1)
914 assert !issue2.reload.duplicates.include?(issue1)
915
915
916 # Closing issue 2
916 # Closing issue 2
917 issue2.init_journal(User.first, "Closing issue2")
917 issue2.init_journal(User.first, "Closing issue2")
918 issue2.status = IssueStatus.where(:is_closed => true).first
918 issue2.status = IssueStatus.where(:is_closed => true).first
919 assert issue2.save
919 assert issue2.save
920 # 1 should not be also closed
920 # 1 should not be also closed
921 assert !issue1.reload.closed?
921 assert !issue1.reload.closed?
922 end
922 end
923
923
924 def test_assignable_versions
924 def test_assignable_versions
925 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
925 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
926 :status_id => 1, :fixed_version_id => 1,
926 :status_id => 1, :fixed_version_id => 1,
927 :subject => 'New issue')
927 :subject => 'New issue')
928 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
928 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
929 end
929 end
930
930
931 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
931 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
932 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
932 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
933 :status_id => 1, :fixed_version_id => 1,
933 :status_id => 1, :fixed_version_id => 1,
934 :subject => 'New issue')
934 :subject => 'New issue')
935 assert !issue.save
935 assert !issue.save
936 assert_not_nil issue.errors[:fixed_version_id]
936 assert_not_nil issue.errors[:fixed_version_id]
937 end
937 end
938
938
939 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
939 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
940 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
940 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
941 :status_id => 1, :fixed_version_id => 2,
941 :status_id => 1, :fixed_version_id => 2,
942 :subject => 'New issue')
942 :subject => 'New issue')
943 assert !issue.save
943 assert !issue.save
944 assert_not_nil issue.errors[:fixed_version_id]
944 assert_not_nil issue.errors[:fixed_version_id]
945 end
945 end
946
946
947 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
947 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
948 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
948 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
949 :status_id => 1, :fixed_version_id => 3,
949 :status_id => 1, :fixed_version_id => 3,
950 :subject => 'New issue')
950 :subject => 'New issue')
951 assert issue.save
951 assert issue.save
952 end
952 end
953
953
954 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
954 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
955 issue = Issue.find(11)
955 issue = Issue.find(11)
956 assert_equal 'closed', issue.fixed_version.status
956 assert_equal 'closed', issue.fixed_version.status
957 issue.subject = 'Subject changed'
957 issue.subject = 'Subject changed'
958 assert issue.save
958 assert issue.save
959 end
959 end
960
960
961 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
961 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
962 issue = Issue.find(11)
962 issue = Issue.find(11)
963 issue.status_id = 1
963 issue.status_id = 1
964 assert !issue.save
964 assert !issue.save
965 assert_not_nil issue.errors[:base]
965 assert_not_nil issue.errors[:base]
966 end
966 end
967
967
968 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
968 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
969 issue = Issue.find(11)
969 issue = Issue.find(11)
970 issue.status_id = 1
970 issue.status_id = 1
971 issue.fixed_version_id = 3
971 issue.fixed_version_id = 3
972 assert issue.save
972 assert issue.save
973 end
973 end
974
974
975 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
975 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
976 issue = Issue.find(12)
976 issue = Issue.find(12)
977 assert_equal 'locked', issue.fixed_version.status
977 assert_equal 'locked', issue.fixed_version.status
978 issue.status_id = 1
978 issue.status_id = 1
979 assert issue.save
979 assert issue.save
980 end
980 end
981
981
982 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
982 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
983 issue = Issue.find(2)
983 issue = Issue.find(2)
984 assert_equal 2, issue.fixed_version_id
984 assert_equal 2, issue.fixed_version_id
985 issue.project_id = 3
985 issue.project_id = 3
986 assert_nil issue.fixed_version_id
986 assert_nil issue.fixed_version_id
987 issue.fixed_version_id = 2
987 issue.fixed_version_id = 2
988 assert !issue.save
988 assert !issue.save
989 assert_include 'Target version is not included in the list', issue.errors.full_messages
989 assert_include 'Target version is not included in the list', issue.errors.full_messages
990 end
990 end
991
991
992 def test_should_keep_shared_version_when_changing_project
992 def test_should_keep_shared_version_when_changing_project
993 Version.find(2).update_attribute :sharing, 'tree'
993 Version.find(2).update_attribute :sharing, 'tree'
994
994
995 issue = Issue.find(2)
995 issue = Issue.find(2)
996 assert_equal 2, issue.fixed_version_id
996 assert_equal 2, issue.fixed_version_id
997 issue.project_id = 3
997 issue.project_id = 3
998 assert_equal 2, issue.fixed_version_id
998 assert_equal 2, issue.fixed_version_id
999 assert issue.save
999 assert issue.save
1000 end
1000 end
1001
1001
1002 def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
1002 def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
1003 assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
1003 assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
1004 end
1004 end
1005
1005
1006 def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
1006 def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
1007 Project.find(2).disable_module! :issue_tracking
1007 Project.find(2).disable_module! :issue_tracking
1008 assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
1008 assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
1009 end
1009 end
1010
1010
1011 def test_move_to_another_project_with_same_category
1011 def test_move_to_another_project_with_same_category
1012 issue = Issue.find(1)
1012 issue = Issue.find(1)
1013 issue.project = Project.find(2)
1013 issue.project = Project.find(2)
1014 assert issue.save
1014 assert issue.save
1015 issue.reload
1015 issue.reload
1016 assert_equal 2, issue.project_id
1016 assert_equal 2, issue.project_id
1017 # Category changes
1017 # Category changes
1018 assert_equal 4, issue.category_id
1018 assert_equal 4, issue.category_id
1019 # Make sure time entries were move to the target project
1019 # Make sure time entries were move to the target project
1020 assert_equal 2, issue.time_entries.first.project_id
1020 assert_equal 2, issue.time_entries.first.project_id
1021 end
1021 end
1022
1022
1023 def test_move_to_another_project_without_same_category
1023 def test_move_to_another_project_without_same_category
1024 issue = Issue.find(2)
1024 issue = Issue.find(2)
1025 issue.project = Project.find(2)
1025 issue.project = Project.find(2)
1026 assert issue.save
1026 assert issue.save
1027 issue.reload
1027 issue.reload
1028 assert_equal 2, issue.project_id
1028 assert_equal 2, issue.project_id
1029 # Category cleared
1029 # Category cleared
1030 assert_nil issue.category_id
1030 assert_nil issue.category_id
1031 end
1031 end
1032
1032
1033 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1033 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1034 issue = Issue.find(1)
1034 issue = Issue.find(1)
1035 issue.update_attribute(:fixed_version_id, 1)
1035 issue.update_attribute(:fixed_version_id, 1)
1036 issue.project = Project.find(2)
1036 issue.project = Project.find(2)
1037 assert issue.save
1037 assert issue.save
1038 issue.reload
1038 issue.reload
1039 assert_equal 2, issue.project_id
1039 assert_equal 2, issue.project_id
1040 # Cleared fixed_version
1040 # Cleared fixed_version
1041 assert_equal nil, issue.fixed_version
1041 assert_equal nil, issue.fixed_version
1042 end
1042 end
1043
1043
1044 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1044 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1045 issue = Issue.find(1)
1045 issue = Issue.find(1)
1046 issue.update_attribute(:fixed_version_id, 4)
1046 issue.update_attribute(:fixed_version_id, 4)
1047 issue.project = Project.find(5)
1047 issue.project = Project.find(5)
1048 assert issue.save
1048 assert issue.save
1049 issue.reload
1049 issue.reload
1050 assert_equal 5, issue.project_id
1050 assert_equal 5, issue.project_id
1051 # Keep fixed_version
1051 # Keep fixed_version
1052 assert_equal 4, issue.fixed_version_id
1052 assert_equal 4, issue.fixed_version_id
1053 end
1053 end
1054
1054
1055 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1055 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1056 issue = Issue.find(1)
1056 issue = Issue.find(1)
1057 issue.update_attribute(:fixed_version_id, 1)
1057 issue.update_attribute(:fixed_version_id, 1)
1058 issue.project = Project.find(5)
1058 issue.project = Project.find(5)
1059 assert issue.save
1059 assert issue.save
1060 issue.reload
1060 issue.reload
1061 assert_equal 5, issue.project_id
1061 assert_equal 5, issue.project_id
1062 # Cleared fixed_version
1062 # Cleared fixed_version
1063 assert_equal nil, issue.fixed_version
1063 assert_equal nil, issue.fixed_version
1064 end
1064 end
1065
1065
1066 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1066 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1067 issue = Issue.find(1)
1067 issue = Issue.find(1)
1068 issue.update_attribute(:fixed_version_id, 7)
1068 issue.update_attribute(:fixed_version_id, 7)
1069 issue.project = Project.find(2)
1069 issue.project = Project.find(2)
1070 assert issue.save
1070 assert issue.save
1071 issue.reload
1071 issue.reload
1072 assert_equal 2, issue.project_id
1072 assert_equal 2, issue.project_id
1073 # Keep fixed_version
1073 # Keep fixed_version
1074 assert_equal 7, issue.fixed_version_id
1074 assert_equal 7, issue.fixed_version_id
1075 end
1075 end
1076
1076
1077 def test_move_to_another_project_should_keep_parent_if_valid
1077 def test_move_to_another_project_should_keep_parent_if_valid
1078 issue = Issue.find(1)
1078 issue = Issue.find(1)
1079 issue.update_attribute(:parent_issue_id, 2)
1079 issue.update_attribute(:parent_issue_id, 2)
1080 issue.project = Project.find(3)
1080 issue.project = Project.find(3)
1081 assert issue.save
1081 assert issue.save
1082 issue.reload
1082 issue.reload
1083 assert_equal 2, issue.parent_id
1083 assert_equal 2, issue.parent_id
1084 end
1084 end
1085
1085
1086 def test_move_to_another_project_should_clear_parent_if_not_valid
1086 def test_move_to_another_project_should_clear_parent_if_not_valid
1087 issue = Issue.find(1)
1087 issue = Issue.find(1)
1088 issue.update_attribute(:parent_issue_id, 2)
1088 issue.update_attribute(:parent_issue_id, 2)
1089 issue.project = Project.find(2)
1089 issue.project = Project.find(2)
1090 assert issue.save
1090 assert issue.save
1091 issue.reload
1091 issue.reload
1092 assert_nil issue.parent_id
1092 assert_nil issue.parent_id
1093 end
1093 end
1094
1094
1095 def test_move_to_another_project_with_disabled_tracker
1095 def test_move_to_another_project_with_disabled_tracker
1096 issue = Issue.find(1)
1096 issue = Issue.find(1)
1097 target = Project.find(2)
1097 target = Project.find(2)
1098 target.tracker_ids = [3]
1098 target.tracker_ids = [3]
1099 target.save
1099 target.save
1100 issue.project = target
1100 issue.project = target
1101 assert issue.save
1101 assert issue.save
1102 issue.reload
1102 issue.reload
1103 assert_equal 2, issue.project_id
1103 assert_equal 2, issue.project_id
1104 assert_equal 3, issue.tracker_id
1104 assert_equal 3, issue.tracker_id
1105 end
1105 end
1106
1106
1107 def test_copy_to_the_same_project
1107 def test_copy_to_the_same_project
1108 issue = Issue.find(1)
1108 issue = Issue.find(1)
1109 copy = issue.copy
1109 copy = issue.copy
1110 assert_difference 'Issue.count' do
1110 assert_difference 'Issue.count' do
1111 copy.save!
1111 copy.save!
1112 end
1112 end
1113 assert_kind_of Issue, copy
1113 assert_kind_of Issue, copy
1114 assert_equal issue.project, copy.project
1114 assert_equal issue.project, copy.project
1115 assert_equal "125", copy.custom_value_for(2).value
1115 assert_equal "125", copy.custom_value_for(2).value
1116 end
1116 end
1117
1117
1118 def test_copy_to_another_project_and_tracker
1118 def test_copy_to_another_project_and_tracker
1119 issue = Issue.find(1)
1119 issue = Issue.find(1)
1120 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1120 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1121 assert_difference 'Issue.count' do
1121 assert_difference 'Issue.count' do
1122 copy.save!
1122 copy.save!
1123 end
1123 end
1124 copy.reload
1124 copy.reload
1125 assert_kind_of Issue, copy
1125 assert_kind_of Issue, copy
1126 assert_equal Project.find(3), copy.project
1126 assert_equal Project.find(3), copy.project
1127 assert_equal Tracker.find(2), copy.tracker
1127 assert_equal Tracker.find(2), copy.tracker
1128 # Custom field #2 is not associated with target tracker
1128 # Custom field #2 is not associated with target tracker
1129 assert_nil copy.custom_value_for(2)
1129 assert_nil copy.custom_value_for(2)
1130 end
1130 end
1131
1131
1132 context "#copy" do
1132 test "#copy should not create a journal" do
1133 setup do
1133 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1134 @issue = Issue.find(1)
1134 copy.save!
1135 end
1135 assert_equal 0, copy.reload.journals.size
1136
1136 end
1137 should "not create a journal" do
1138 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1139 copy.save!
1140 assert_equal 0, copy.reload.journals.size
1141 end
1142
1137
1143 should "allow assigned_to changes" do
1138 test "#copy should allow assigned_to changes" do
1144 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1139 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1145 assert_equal 3, copy.assigned_to_id
1140 assert_equal 3, copy.assigned_to_id
1146 end
1141 end
1147
1142
1148 should "allow status changes" do
1143 test "#copy should allow status changes" do
1149 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1144 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1150 assert_equal 2, copy.status_id
1145 assert_equal 2, copy.status_id
1151 end
1146 end
1152
1147
1153 should "allow start date changes" do
1148 test "#copy should allow start date changes" do
1154 date = Date.today
1149 date = Date.today
1155 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1150 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1156 assert_equal date, copy.start_date
1151 assert_equal date, copy.start_date
1157 end
1152 end
1158
1153
1159 should "allow due date changes" do
1154 test "#copy should allow due date changes" do
1160 date = Date.today
1155 date = Date.today
1161 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1156 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1162 assert_equal date, copy.due_date
1157 assert_equal date, copy.due_date
1163 end
1158 end
1164
1159
1165 should "set current user as author" do
1160 test "#copy should set current user as author" do
1166 User.current = User.find(9)
1161 User.current = User.find(9)
1167 copy = @issue.copy(:project_id => 3, :tracker_id => 2)
1162 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1168 assert_equal User.current, copy.author
1163 assert_equal User.current, copy.author
1169 end
1164 end
1170
1165
1171 should "create a journal with notes" do
1166 test "#copy should create a journal with notes" do
1172 date = Date.today
1167 date = Date.today
1173 notes = "Notes added when copying"
1168 notes = "Notes added when copying"
1174 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1169 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1175 copy.init_journal(User.current, notes)
1170 copy.init_journal(User.current, notes)
1176 copy.save!
1171 copy.save!
1177
1172
1178 assert_equal 1, copy.journals.size
1173 assert_equal 1, copy.journals.size
1179 journal = copy.journals.first
1174 journal = copy.journals.first
1180 assert_equal 0, journal.details.size
1175 assert_equal 0, journal.details.size
1181 assert_equal notes, journal.notes
1176 assert_equal notes, journal.notes
1182 end
1183 end
1177 end
1184
1178
1185 def test_valid_parent_project
1179 def test_valid_parent_project
1186 issue = Issue.find(1)
1180 issue = Issue.find(1)
1187 issue_in_same_project = Issue.find(2)
1181 issue_in_same_project = Issue.find(2)
1188 issue_in_child_project = Issue.find(5)
1182 issue_in_child_project = Issue.find(5)
1189 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1183 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1190 issue_in_other_child_project = Issue.find(6)
1184 issue_in_other_child_project = Issue.find(6)
1191 issue_in_different_tree = Issue.find(4)
1185 issue_in_different_tree = Issue.find(4)
1192
1186
1193 with_settings :cross_project_subtasks => '' do
1187 with_settings :cross_project_subtasks => '' do
1194 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1188 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1195 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1189 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1196 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1190 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1197 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1191 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1198 end
1192 end
1199
1193
1200 with_settings :cross_project_subtasks => 'system' do
1194 with_settings :cross_project_subtasks => 'system' do
1201 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1195 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1202 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1196 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1203 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1197 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1204 end
1198 end
1205
1199
1206 with_settings :cross_project_subtasks => 'tree' do
1200 with_settings :cross_project_subtasks => 'tree' do
1207 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1201 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1208 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1202 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1209 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1203 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1210 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1204 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1211
1205
1212 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1206 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1213 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1207 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1214 end
1208 end
1215
1209
1216 with_settings :cross_project_subtasks => 'descendants' do
1210 with_settings :cross_project_subtasks => 'descendants' do
1217 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1211 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1218 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1212 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1219 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1213 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1220 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1214 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1221
1215
1222 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1216 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1223 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1217 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1224 end
1218 end
1225 end
1219 end
1226
1220
1227 def test_recipients_should_include_previous_assignee
1221 def test_recipients_should_include_previous_assignee
1228 user = User.find(3)
1222 user = User.find(3)
1229 user.members.update_all ["mail_notification = ?", false]
1223 user.members.update_all ["mail_notification = ?", false]
1230 user.update_attribute :mail_notification, 'only_assigned'
1224 user.update_attribute :mail_notification, 'only_assigned'
1231
1225
1232 issue = Issue.find(2)
1226 issue = Issue.find(2)
1233 issue.assigned_to = nil
1227 issue.assigned_to = nil
1234 assert_include user.mail, issue.recipients
1228 assert_include user.mail, issue.recipients
1235 issue.save!
1229 issue.save!
1236 assert !issue.recipients.include?(user.mail)
1230 assert !issue.recipients.include?(user.mail)
1237 end
1231 end
1238
1232
1239 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1233 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1240 issue = Issue.find(12)
1234 issue = Issue.find(12)
1241 assert issue.recipients.include?(issue.author.mail)
1235 assert issue.recipients.include?(issue.author.mail)
1242 # copy the issue to a private project
1236 # copy the issue to a private project
1243 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1237 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1244 # author is not a member of project anymore
1238 # author is not a member of project anymore
1245 assert !copy.recipients.include?(copy.author.mail)
1239 assert !copy.recipients.include?(copy.author.mail)
1246 end
1240 end
1247
1241
1248 def test_recipients_should_include_the_assigned_group_members
1242 def test_recipients_should_include_the_assigned_group_members
1249 group_member = User.generate!
1243 group_member = User.generate!
1250 group = Group.generate!
1244 group = Group.generate!
1251 group.users << group_member
1245 group.users << group_member
1252
1246
1253 issue = Issue.find(12)
1247 issue = Issue.find(12)
1254 issue.assigned_to = group
1248 issue.assigned_to = group
1255 assert issue.recipients.include?(group_member.mail)
1249 assert issue.recipients.include?(group_member.mail)
1256 end
1250 end
1257
1251
1258 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1252 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1259 user = User.find(3)
1253 user = User.find(3)
1260 issue = Issue.find(9)
1254 issue = Issue.find(9)
1261 Watcher.create!(:user => user, :watchable => issue)
1255 Watcher.create!(:user => user, :watchable => issue)
1262 assert issue.watched_by?(user)
1256 assert issue.watched_by?(user)
1263 assert !issue.watcher_recipients.include?(user.mail)
1257 assert !issue.watcher_recipients.include?(user.mail)
1264 end
1258 end
1265
1259
1266 def test_issue_destroy
1260 def test_issue_destroy
1267 Issue.find(1).destroy
1261 Issue.find(1).destroy
1268 assert_nil Issue.find_by_id(1)
1262 assert_nil Issue.find_by_id(1)
1269 assert_nil TimeEntry.find_by_issue_id(1)
1263 assert_nil TimeEntry.find_by_issue_id(1)
1270 end
1264 end
1271
1265
1272 def test_destroying_a_deleted_issue_should_not_raise_an_error
1266 def test_destroying_a_deleted_issue_should_not_raise_an_error
1273 issue = Issue.find(1)
1267 issue = Issue.find(1)
1274 Issue.find(1).destroy
1268 Issue.find(1).destroy
1275
1269
1276 assert_nothing_raised do
1270 assert_nothing_raised do
1277 assert_no_difference 'Issue.count' do
1271 assert_no_difference 'Issue.count' do
1278 issue.destroy
1272 issue.destroy
1279 end
1273 end
1280 assert issue.destroyed?
1274 assert issue.destroyed?
1281 end
1275 end
1282 end
1276 end
1283
1277
1284 def test_destroying_a_stale_issue_should_not_raise_an_error
1278 def test_destroying_a_stale_issue_should_not_raise_an_error
1285 issue = Issue.find(1)
1279 issue = Issue.find(1)
1286 Issue.find(1).update_attribute :subject, "Updated"
1280 Issue.find(1).update_attribute :subject, "Updated"
1287
1281
1288 assert_nothing_raised do
1282 assert_nothing_raised do
1289 assert_difference 'Issue.count', -1 do
1283 assert_difference 'Issue.count', -1 do
1290 issue.destroy
1284 issue.destroy
1291 end
1285 end
1292 assert issue.destroyed?
1286 assert issue.destroyed?
1293 end
1287 end
1294 end
1288 end
1295
1289
1296 def test_blocked
1290 def test_blocked
1297 blocked_issue = Issue.find(9)
1291 blocked_issue = Issue.find(9)
1298 blocking_issue = Issue.find(10)
1292 blocking_issue = Issue.find(10)
1299
1293
1300 assert blocked_issue.blocked?
1294 assert blocked_issue.blocked?
1301 assert !blocking_issue.blocked?
1295 assert !blocking_issue.blocked?
1302 end
1296 end
1303
1297
1304 def test_blocked_issues_dont_allow_closed_statuses
1298 def test_blocked_issues_dont_allow_closed_statuses
1305 blocked_issue = Issue.find(9)
1299 blocked_issue = Issue.find(9)
1306
1300
1307 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1301 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1308 assert !allowed_statuses.empty?
1302 assert !allowed_statuses.empty?
1309 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1303 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1310 assert closed_statuses.empty?
1304 assert closed_statuses.empty?
1311 end
1305 end
1312
1306
1313 def test_unblocked_issues_allow_closed_statuses
1307 def test_unblocked_issues_allow_closed_statuses
1314 blocking_issue = Issue.find(10)
1308 blocking_issue = Issue.find(10)
1315
1309
1316 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1310 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1317 assert !allowed_statuses.empty?
1311 assert !allowed_statuses.empty?
1318 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1312 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1319 assert !closed_statuses.empty?
1313 assert !closed_statuses.empty?
1320 end
1314 end
1321
1315
1322 def test_reschedule_an_issue_without_dates
1316 def test_reschedule_an_issue_without_dates
1323 with_settings :non_working_week_days => [] do
1317 with_settings :non_working_week_days => [] do
1324 issue = Issue.new(:start_date => nil, :due_date => nil)
1318 issue = Issue.new(:start_date => nil, :due_date => nil)
1325 issue.reschedule_on '2012-10-09'.to_date
1319 issue.reschedule_on '2012-10-09'.to_date
1326 assert_equal '2012-10-09'.to_date, issue.start_date
1320 assert_equal '2012-10-09'.to_date, issue.start_date
1327 assert_equal '2012-10-09'.to_date, issue.due_date
1321 assert_equal '2012-10-09'.to_date, issue.due_date
1328 end
1322 end
1329
1323
1330 with_settings :non_working_week_days => %w(6 7) do
1324 with_settings :non_working_week_days => %w(6 7) do
1331 issue = Issue.new(:start_date => nil, :due_date => nil)
1325 issue = Issue.new(:start_date => nil, :due_date => nil)
1332 issue.reschedule_on '2012-10-09'.to_date
1326 issue.reschedule_on '2012-10-09'.to_date
1333 assert_equal '2012-10-09'.to_date, issue.start_date
1327 assert_equal '2012-10-09'.to_date, issue.start_date
1334 assert_equal '2012-10-09'.to_date, issue.due_date
1328 assert_equal '2012-10-09'.to_date, issue.due_date
1335
1329
1336 issue = Issue.new(:start_date => nil, :due_date => nil)
1330 issue = Issue.new(:start_date => nil, :due_date => nil)
1337 issue.reschedule_on '2012-10-13'.to_date
1331 issue.reschedule_on '2012-10-13'.to_date
1338 assert_equal '2012-10-15'.to_date, issue.start_date
1332 assert_equal '2012-10-15'.to_date, issue.start_date
1339 assert_equal '2012-10-15'.to_date, issue.due_date
1333 assert_equal '2012-10-15'.to_date, issue.due_date
1340 end
1334 end
1341 end
1335 end
1342
1336
1343 def test_reschedule_an_issue_with_start_date
1337 def test_reschedule_an_issue_with_start_date
1344 with_settings :non_working_week_days => [] do
1338 with_settings :non_working_week_days => [] do
1345 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1339 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1346 issue.reschedule_on '2012-10-13'.to_date
1340 issue.reschedule_on '2012-10-13'.to_date
1347 assert_equal '2012-10-13'.to_date, issue.start_date
1341 assert_equal '2012-10-13'.to_date, issue.start_date
1348 assert_equal '2012-10-13'.to_date, issue.due_date
1342 assert_equal '2012-10-13'.to_date, issue.due_date
1349 end
1343 end
1350
1344
1351 with_settings :non_working_week_days => %w(6 7) do
1345 with_settings :non_working_week_days => %w(6 7) do
1352 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1346 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1353 issue.reschedule_on '2012-10-11'.to_date
1347 issue.reschedule_on '2012-10-11'.to_date
1354 assert_equal '2012-10-11'.to_date, issue.start_date
1348 assert_equal '2012-10-11'.to_date, issue.start_date
1355 assert_equal '2012-10-11'.to_date, issue.due_date
1349 assert_equal '2012-10-11'.to_date, issue.due_date
1356
1350
1357 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1351 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1358 issue.reschedule_on '2012-10-13'.to_date
1352 issue.reschedule_on '2012-10-13'.to_date
1359 assert_equal '2012-10-15'.to_date, issue.start_date
1353 assert_equal '2012-10-15'.to_date, issue.start_date
1360 assert_equal '2012-10-15'.to_date, issue.due_date
1354 assert_equal '2012-10-15'.to_date, issue.due_date
1361 end
1355 end
1362 end
1356 end
1363
1357
1364 def test_reschedule_an_issue_with_start_and_due_dates
1358 def test_reschedule_an_issue_with_start_and_due_dates
1365 with_settings :non_working_week_days => [] do
1359 with_settings :non_working_week_days => [] do
1366 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1360 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1367 issue.reschedule_on '2012-10-13'.to_date
1361 issue.reschedule_on '2012-10-13'.to_date
1368 assert_equal '2012-10-13'.to_date, issue.start_date
1362 assert_equal '2012-10-13'.to_date, issue.start_date
1369 assert_equal '2012-10-19'.to_date, issue.due_date
1363 assert_equal '2012-10-19'.to_date, issue.due_date
1370 end
1364 end
1371
1365
1372 with_settings :non_working_week_days => %w(6 7) do
1366 with_settings :non_working_week_days => %w(6 7) do
1373 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1367 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1374 issue.reschedule_on '2012-10-11'.to_date
1368 issue.reschedule_on '2012-10-11'.to_date
1375 assert_equal '2012-10-11'.to_date, issue.start_date
1369 assert_equal '2012-10-11'.to_date, issue.start_date
1376 assert_equal '2012-10-23'.to_date, issue.due_date
1370 assert_equal '2012-10-23'.to_date, issue.due_date
1377
1371
1378 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1372 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1379 issue.reschedule_on '2012-10-13'.to_date
1373 issue.reschedule_on '2012-10-13'.to_date
1380 assert_equal '2012-10-15'.to_date, issue.start_date
1374 assert_equal '2012-10-15'.to_date, issue.start_date
1381 assert_equal '2012-10-25'.to_date, issue.due_date
1375 assert_equal '2012-10-25'.to_date, issue.due_date
1382 end
1376 end
1383 end
1377 end
1384
1378
1385 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1379 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1386 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1380 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1387 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1381 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1388 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1382 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1389 :relation_type => IssueRelation::TYPE_PRECEDES)
1383 :relation_type => IssueRelation::TYPE_PRECEDES)
1390 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1384 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1391
1385
1392 issue1.due_date = '2012-10-23'
1386 issue1.due_date = '2012-10-23'
1393 issue1.save!
1387 issue1.save!
1394 issue2.reload
1388 issue2.reload
1395 assert_equal Date.parse('2012-10-24'), issue2.start_date
1389 assert_equal Date.parse('2012-10-24'), issue2.start_date
1396 assert_equal Date.parse('2012-10-26'), issue2.due_date
1390 assert_equal Date.parse('2012-10-26'), issue2.due_date
1397 end
1391 end
1398
1392
1399 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1393 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1400 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1394 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1401 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1395 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1402 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1396 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1403 :relation_type => IssueRelation::TYPE_PRECEDES)
1397 :relation_type => IssueRelation::TYPE_PRECEDES)
1404 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1398 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1405
1399
1406 issue1.start_date = '2012-09-17'
1400 issue1.start_date = '2012-09-17'
1407 issue1.due_date = '2012-09-18'
1401 issue1.due_date = '2012-09-18'
1408 issue1.save!
1402 issue1.save!
1409 issue2.reload
1403 issue2.reload
1410 assert_equal Date.parse('2012-09-19'), issue2.start_date
1404 assert_equal Date.parse('2012-09-19'), issue2.start_date
1411 assert_equal Date.parse('2012-09-21'), issue2.due_date
1405 assert_equal Date.parse('2012-09-21'), issue2.due_date
1412 end
1406 end
1413
1407
1414 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1408 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1415 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1409 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1416 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1410 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1417 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1411 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1418 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1412 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1419 :relation_type => IssueRelation::TYPE_PRECEDES)
1413 :relation_type => IssueRelation::TYPE_PRECEDES)
1420 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1414 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1421 :relation_type => IssueRelation::TYPE_PRECEDES)
1415 :relation_type => IssueRelation::TYPE_PRECEDES)
1422 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1416 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1423
1417
1424 issue1.start_date = '2012-09-17'
1418 issue1.start_date = '2012-09-17'
1425 issue1.due_date = '2012-09-18'
1419 issue1.due_date = '2012-09-18'
1426 issue1.save!
1420 issue1.save!
1427 issue2.reload
1421 issue2.reload
1428 # Issue 2 must start after Issue 3
1422 # Issue 2 must start after Issue 3
1429 assert_equal Date.parse('2012-10-03'), issue2.start_date
1423 assert_equal Date.parse('2012-10-03'), issue2.start_date
1430 assert_equal Date.parse('2012-10-05'), issue2.due_date
1424 assert_equal Date.parse('2012-10-05'), issue2.due_date
1431 end
1425 end
1432
1426
1433 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1427 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1434 with_settings :non_working_week_days => [] do
1428 with_settings :non_working_week_days => [] do
1435 stale = Issue.find(1)
1429 stale = Issue.find(1)
1436 issue = Issue.find(1)
1430 issue = Issue.find(1)
1437 issue.subject = "Updated"
1431 issue.subject = "Updated"
1438 issue.save!
1432 issue.save!
1439 date = 10.days.from_now.to_date
1433 date = 10.days.from_now.to_date
1440 assert_nothing_raised do
1434 assert_nothing_raised do
1441 stale.reschedule_on!(date)
1435 stale.reschedule_on!(date)
1442 end
1436 end
1443 assert_equal date, stale.reload.start_date
1437 assert_equal date, stale.reload.start_date
1444 end
1438 end
1445 end
1439 end
1446
1440
1447 def test_overdue
1441 def test_overdue
1448 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1442 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1449 assert !Issue.new(:due_date => Date.today).overdue?
1443 assert !Issue.new(:due_date => Date.today).overdue?
1450 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1444 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1451 assert !Issue.new(:due_date => nil).overdue?
1445 assert !Issue.new(:due_date => nil).overdue?
1452 assert !Issue.new(:due_date => 1.day.ago.to_date,
1446 assert !Issue.new(:due_date => 1.day.ago.to_date,
1453 :status => IssueStatus.where(:is_closed => true).first
1447 :status => IssueStatus.where(:is_closed => true).first
1454 ).overdue?
1448 ).overdue?
1455 end
1449 end
1456
1450
1457 context "#behind_schedule?" do
1451 test "#behind_schedule? should be false if the issue has no start_date" do
1458 should "be false if the issue has no start_date" do
1452 assert !Issue.new(:start_date => nil,
1459 assert !Issue.new(:start_date => nil,
1453 :due_date => 1.day.from_now.to_date,
1460 :due_date => 1.day.from_now.to_date,
1454 :done_ratio => 0).behind_schedule?
1461 :done_ratio => 0).behind_schedule?
1455 end
1462 end
1463
1456
1464 should "be false if the issue has no end_date" do
1457 test "#behind_schedule? should be false if the issue has no end_date" do
1465 assert !Issue.new(:start_date => 1.day.from_now.to_date,
1458 assert !Issue.new(:start_date => 1.day.from_now.to_date,
1466 :due_date => nil,
1459 :due_date => nil,
1467 :done_ratio => 0).behind_schedule?
1460 :done_ratio => 0).behind_schedule?
1468 end
1461 end
1469
1462
1470 should "be false if the issue has more done than it's calendar time" do
1463 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
1471 assert !Issue.new(:start_date => 50.days.ago.to_date,
1464 assert !Issue.new(:start_date => 50.days.ago.to_date,
1472 :due_date => 50.days.from_now.to_date,
1465 :due_date => 50.days.from_now.to_date,
1473 :done_ratio => 90).behind_schedule?
1466 :done_ratio => 90).behind_schedule?
1474 end
1467 end
1475
1468
1476 should "be true if the issue hasn't been started at all" do
1469 test "#behind_schedule? should be true if the issue hasn't been started at all" do
1477 assert Issue.new(:start_date => 1.day.ago.to_date,
1470 assert Issue.new(:start_date => 1.day.ago.to_date,
1478 :due_date => 1.day.from_now.to_date,
1471 :due_date => 1.day.from_now.to_date,
1479 :done_ratio => 0).behind_schedule?
1472 :done_ratio => 0).behind_schedule?
1480 end
1473 end
1481
1474
1482 should "be true if the issue has used more calendar time than it's done ratio" do
1475 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
1483 assert Issue.new(:start_date => 100.days.ago.to_date,
1476 assert Issue.new(:start_date => 100.days.ago.to_date,
1484 :due_date => Date.today,
1477 :due_date => Date.today,
1485 :done_ratio => 90).behind_schedule?
1478 :done_ratio => 90).behind_schedule?
1486 end
1487 end
1479 end
1488
1480
1489 context "#assignable_users" do
1481 test "#assignable_users should be Users" do
1490 should "be Users" do
1482 assert_kind_of User, Issue.find(1).assignable_users.first
1491 assert_kind_of User, Issue.find(1).assignable_users.first
1483 end
1492 end
1493
1484
1494 should "include the issue author" do
1485 test "#assignable_users should include the issue author" do
1495 non_project_member = User.generate!
1486 non_project_member = User.generate!
1496 issue = Issue.generate!(:author => non_project_member)
1487 issue = Issue.generate!(:author => non_project_member)
1497
1488
1498 assert issue.assignable_users.include?(non_project_member)
1489 assert issue.assignable_users.include?(non_project_member)
1499 end
1490 end
1500
1491
1501 should "include the current assignee" do
1492 test "#assignable_users should include the current assignee" do
1502 user = User.generate!
1493 user = User.generate!
1503 issue = Issue.generate!(:assigned_to => user)
1494 issue = Issue.generate!(:assigned_to => user)
1504 user.lock!
1495 user.lock!
1505
1496
1506 assert Issue.find(issue.id).assignable_users.include?(user)
1497 assert Issue.find(issue.id).assignable_users.include?(user)
1507 end
1498 end
1508
1499
1509 should "not show the issue author twice" do
1500 test "#assignable_users should not show the issue author twice" do
1510 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1501 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1511 assert_equal 2, assignable_user_ids.length
1502 assert_equal 2, assignable_user_ids.length
1512
1503
1513 assignable_user_ids.each do |user_id|
1504 assignable_user_ids.each do |user_id|
1514 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
1505 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
1515 "User #{user_id} appears more or less than once"
1506 "User #{user_id} appears more or less than once"
1516 end
1517 end
1507 end
1508 end
1518
1509
1519 context "with issue_group_assignment" do
1510 test "#assignable_users with issue_group_assignment should include groups" do
1520 should "include groups" do
1511 issue = Issue.new(:project => Project.find(2))
1521 issue = Issue.new(:project => Project.find(2))
1522
1512
1523 with_settings :issue_group_assignment => '1' do
1513 with_settings :issue_group_assignment => '1' do
1524 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1514 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1525 assert issue.assignable_users.include?(Group.find(11))
1515 assert issue.assignable_users.include?(Group.find(11))
1526 end
1527 end
1528 end
1516 end
1517 end
1529
1518
1530 context "without issue_group_assignment" do
1519 test "#assignable_users without issue_group_assignment should not include groups" do
1531 should "not include groups" do
1520 issue = Issue.new(:project => Project.find(2))
1532 issue = Issue.new(:project => Project.find(2))
1533
1521
1534 with_settings :issue_group_assignment => '0' do
1522 with_settings :issue_group_assignment => '0' do
1535 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1523 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1536 assert !issue.assignable_users.include?(Group.find(11))
1524 assert !issue.assignable_users.include?(Group.find(11))
1537 end
1538 end
1539 end
1525 end
1540 end
1526 end
1541
1527
1542 def test_create_should_send_email_notification
1528 def test_create_should_send_email_notification
1543 ActionMailer::Base.deliveries.clear
1529 ActionMailer::Base.deliveries.clear
1544 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1530 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1545 :author_id => 3, :status_id => 1,
1531 :author_id => 3, :status_id => 1,
1546 :priority => IssuePriority.all.first,
1532 :priority => IssuePriority.all.first,
1547 :subject => 'test_create', :estimated_hours => '1:30')
1533 :subject => 'test_create', :estimated_hours => '1:30')
1548
1534
1549 assert issue.save
1535 assert issue.save
1550 assert_equal 1, ActionMailer::Base.deliveries.size
1536 assert_equal 1, ActionMailer::Base.deliveries.size
1551 end
1537 end
1552
1538
1553 def test_stale_issue_should_not_send_email_notification
1539 def test_stale_issue_should_not_send_email_notification
1554 ActionMailer::Base.deliveries.clear
1540 ActionMailer::Base.deliveries.clear
1555 issue = Issue.find(1)
1541 issue = Issue.find(1)
1556 stale = Issue.find(1)
1542 stale = Issue.find(1)
1557
1543
1558 issue.init_journal(User.find(1))
1544 issue.init_journal(User.find(1))
1559 issue.subject = 'Subjet update'
1545 issue.subject = 'Subjet update'
1560 assert issue.save
1546 assert issue.save
1561 assert_equal 1, ActionMailer::Base.deliveries.size
1547 assert_equal 1, ActionMailer::Base.deliveries.size
1562 ActionMailer::Base.deliveries.clear
1548 ActionMailer::Base.deliveries.clear
1563
1549
1564 stale.init_journal(User.find(1))
1550 stale.init_journal(User.find(1))
1565 stale.subject = 'Another subjet update'
1551 stale.subject = 'Another subjet update'
1566 assert_raise ActiveRecord::StaleObjectError do
1552 assert_raise ActiveRecord::StaleObjectError do
1567 stale.save
1553 stale.save
1568 end
1554 end
1569 assert ActionMailer::Base.deliveries.empty?
1555 assert ActionMailer::Base.deliveries.empty?
1570 end
1556 end
1571
1557
1572 def test_journalized_description
1558 def test_journalized_description
1573 IssueCustomField.delete_all
1559 IssueCustomField.delete_all
1574
1560
1575 i = Issue.first
1561 i = Issue.first
1576 old_description = i.description
1562 old_description = i.description
1577 new_description = "This is the new description"
1563 new_description = "This is the new description"
1578
1564
1579 i.init_journal(User.find(2))
1565 i.init_journal(User.find(2))
1580 i.description = new_description
1566 i.description = new_description
1581 assert_difference 'Journal.count', 1 do
1567 assert_difference 'Journal.count', 1 do
1582 assert_difference 'JournalDetail.count', 1 do
1568 assert_difference 'JournalDetail.count', 1 do
1583 i.save!
1569 i.save!
1584 end
1570 end
1585 end
1571 end
1586
1572
1587 detail = JournalDetail.first(:order => 'id DESC')
1573 detail = JournalDetail.first(:order => 'id DESC')
1588 assert_equal i, detail.journal.journalized
1574 assert_equal i, detail.journal.journalized
1589 assert_equal 'attr', detail.property
1575 assert_equal 'attr', detail.property
1590 assert_equal 'description', detail.prop_key
1576 assert_equal 'description', detail.prop_key
1591 assert_equal old_description, detail.old_value
1577 assert_equal old_description, detail.old_value
1592 assert_equal new_description, detail.value
1578 assert_equal new_description, detail.value
1593 end
1579 end
1594
1580
1595 def test_blank_descriptions_should_not_be_journalized
1581 def test_blank_descriptions_should_not_be_journalized
1596 IssueCustomField.delete_all
1582 IssueCustomField.delete_all
1597 Issue.update_all("description = NULL", "id=1")
1583 Issue.update_all("description = NULL", "id=1")
1598
1584
1599 i = Issue.find(1)
1585 i = Issue.find(1)
1600 i.init_journal(User.find(2))
1586 i.init_journal(User.find(2))
1601 i.subject = "blank description"
1587 i.subject = "blank description"
1602 i.description = "\r\n"
1588 i.description = "\r\n"
1603
1589
1604 assert_difference 'Journal.count', 1 do
1590 assert_difference 'Journal.count', 1 do
1605 assert_difference 'JournalDetail.count', 1 do
1591 assert_difference 'JournalDetail.count', 1 do
1606 i.save!
1592 i.save!
1607 end
1593 end
1608 end
1594 end
1609 end
1595 end
1610
1596
1611 def test_journalized_multi_custom_field
1597 def test_journalized_multi_custom_field
1612 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
1598 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
1613 :is_filter => true, :is_for_all => true,
1599 :is_filter => true, :is_for_all => true,
1614 :tracker_ids => [1],
1600 :tracker_ids => [1],
1615 :possible_values => ['value1', 'value2', 'value3'],
1601 :possible_values => ['value1', 'value2', 'value3'],
1616 :multiple => true)
1602 :multiple => true)
1617
1603
1618 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
1604 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
1619 :subject => 'Test', :author_id => 1)
1605 :subject => 'Test', :author_id => 1)
1620
1606
1621 assert_difference 'Journal.count' do
1607 assert_difference 'Journal.count' do
1622 assert_difference 'JournalDetail.count' do
1608 assert_difference 'JournalDetail.count' do
1623 issue.init_journal(User.first)
1609 issue.init_journal(User.first)
1624 issue.custom_field_values = {field.id => ['value1']}
1610 issue.custom_field_values = {field.id => ['value1']}
1625 issue.save!
1611 issue.save!
1626 end
1612 end
1627 assert_difference 'JournalDetail.count' do
1613 assert_difference 'JournalDetail.count' do
1628 issue.init_journal(User.first)
1614 issue.init_journal(User.first)
1629 issue.custom_field_values = {field.id => ['value1', 'value2']}
1615 issue.custom_field_values = {field.id => ['value1', 'value2']}
1630 issue.save!
1616 issue.save!
1631 end
1617 end
1632 assert_difference 'JournalDetail.count', 2 do
1618 assert_difference 'JournalDetail.count', 2 do
1633 issue.init_journal(User.first)
1619 issue.init_journal(User.first)
1634 issue.custom_field_values = {field.id => ['value3', 'value2']}
1620 issue.custom_field_values = {field.id => ['value3', 'value2']}
1635 issue.save!
1621 issue.save!
1636 end
1622 end
1637 assert_difference 'JournalDetail.count', 2 do
1623 assert_difference 'JournalDetail.count', 2 do
1638 issue.init_journal(User.first)
1624 issue.init_journal(User.first)
1639 issue.custom_field_values = {field.id => nil}
1625 issue.custom_field_values = {field.id => nil}
1640 issue.save!
1626 issue.save!
1641 end
1627 end
1642 end
1628 end
1643 end
1629 end
1644
1630
1645 def test_description_eol_should_be_normalized
1631 def test_description_eol_should_be_normalized
1646 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1632 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1647 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1633 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1648 end
1634 end
1649
1635
1650 def test_saving_twice_should_not_duplicate_journal_details
1636 def test_saving_twice_should_not_duplicate_journal_details
1651 i = Issue.first
1637 i = Issue.first
1652 i.init_journal(User.find(2), 'Some notes')
1638 i.init_journal(User.find(2), 'Some notes')
1653 # initial changes
1639 # initial changes
1654 i.subject = 'New subject'
1640 i.subject = 'New subject'
1655 i.done_ratio = i.done_ratio + 10
1641 i.done_ratio = i.done_ratio + 10
1656 assert_difference 'Journal.count' do
1642 assert_difference 'Journal.count' do
1657 assert i.save
1643 assert i.save
1658 end
1644 end
1659 # 1 more change
1645 # 1 more change
1660 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
1646 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
1661 assert_no_difference 'Journal.count' do
1647 assert_no_difference 'Journal.count' do
1662 assert_difference 'JournalDetail.count', 1 do
1648 assert_difference 'JournalDetail.count', 1 do
1663 i.save
1649 i.save
1664 end
1650 end
1665 end
1651 end
1666 # no more change
1652 # no more change
1667 assert_no_difference 'Journal.count' do
1653 assert_no_difference 'Journal.count' do
1668 assert_no_difference 'JournalDetail.count' do
1654 assert_no_difference 'JournalDetail.count' do
1669 i.save
1655 i.save
1670 end
1656 end
1671 end
1657 end
1672 end
1658 end
1673
1659
1674 def test_all_dependent_issues
1660 def test_all_dependent_issues
1675 IssueRelation.delete_all
1661 IssueRelation.delete_all
1676 assert IssueRelation.create!(:issue_from => Issue.find(1),
1662 assert IssueRelation.create!(:issue_from => Issue.find(1),
1677 :issue_to => Issue.find(2),
1663 :issue_to => Issue.find(2),
1678 :relation_type => IssueRelation::TYPE_PRECEDES)
1664 :relation_type => IssueRelation::TYPE_PRECEDES)
1679 assert IssueRelation.create!(:issue_from => Issue.find(2),
1665 assert IssueRelation.create!(:issue_from => Issue.find(2),
1680 :issue_to => Issue.find(3),
1666 :issue_to => Issue.find(3),
1681 :relation_type => IssueRelation::TYPE_PRECEDES)
1667 :relation_type => IssueRelation::TYPE_PRECEDES)
1682 assert IssueRelation.create!(:issue_from => Issue.find(3),
1668 assert IssueRelation.create!(:issue_from => Issue.find(3),
1683 :issue_to => Issue.find(8),
1669 :issue_to => Issue.find(8),
1684 :relation_type => IssueRelation::TYPE_PRECEDES)
1670 :relation_type => IssueRelation::TYPE_PRECEDES)
1685
1671
1686 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1672 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1687 end
1673 end
1688
1674
1689 def test_all_dependent_issues_with_persistent_circular_dependency
1675 def test_all_dependent_issues_with_persistent_circular_dependency
1690 IssueRelation.delete_all
1676 IssueRelation.delete_all
1691 assert IssueRelation.create!(:issue_from => Issue.find(1),
1677 assert IssueRelation.create!(:issue_from => Issue.find(1),
1692 :issue_to => Issue.find(2),
1678 :issue_to => Issue.find(2),
1693 :relation_type => IssueRelation::TYPE_PRECEDES)
1679 :relation_type => IssueRelation::TYPE_PRECEDES)
1694 assert IssueRelation.create!(:issue_from => Issue.find(2),
1680 assert IssueRelation.create!(:issue_from => Issue.find(2),
1695 :issue_to => Issue.find(3),
1681 :issue_to => Issue.find(3),
1696 :relation_type => IssueRelation::TYPE_PRECEDES)
1682 :relation_type => IssueRelation::TYPE_PRECEDES)
1697
1683
1698 r = IssueRelation.create!(:issue_from => Issue.find(3),
1684 r = IssueRelation.create!(:issue_from => Issue.find(3),
1699 :issue_to => Issue.find(7),
1685 :issue_to => Issue.find(7),
1700 :relation_type => IssueRelation::TYPE_PRECEDES)
1686 :relation_type => IssueRelation::TYPE_PRECEDES)
1701 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1687 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1702
1688
1703 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1689 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1704 end
1690 end
1705
1691
1706 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1692 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1707 IssueRelation.delete_all
1693 IssueRelation.delete_all
1708 assert IssueRelation.create!(:issue_from => Issue.find(1),
1694 assert IssueRelation.create!(:issue_from => Issue.find(1),
1709 :issue_to => Issue.find(2),
1695 :issue_to => Issue.find(2),
1710 :relation_type => IssueRelation::TYPE_RELATES)
1696 :relation_type => IssueRelation::TYPE_RELATES)
1711 assert IssueRelation.create!(:issue_from => Issue.find(2),
1697 assert IssueRelation.create!(:issue_from => Issue.find(2),
1712 :issue_to => Issue.find(3),
1698 :issue_to => Issue.find(3),
1713 :relation_type => IssueRelation::TYPE_RELATES)
1699 :relation_type => IssueRelation::TYPE_RELATES)
1714 assert IssueRelation.create!(:issue_from => Issue.find(3),
1700 assert IssueRelation.create!(:issue_from => Issue.find(3),
1715 :issue_to => Issue.find(8),
1701 :issue_to => Issue.find(8),
1716 :relation_type => IssueRelation::TYPE_RELATES)
1702 :relation_type => IssueRelation::TYPE_RELATES)
1717
1703
1718 r = IssueRelation.create!(:issue_from => Issue.find(8),
1704 r = IssueRelation.create!(:issue_from => Issue.find(8),
1719 :issue_to => Issue.find(7),
1705 :issue_to => Issue.find(7),
1720 :relation_type => IssueRelation::TYPE_RELATES)
1706 :relation_type => IssueRelation::TYPE_RELATES)
1721 IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
1707 IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
1722
1708
1723 r = IssueRelation.create!(:issue_from => Issue.find(3),
1709 r = IssueRelation.create!(:issue_from => Issue.find(3),
1724 :issue_to => Issue.find(7),
1710 :issue_to => Issue.find(7),
1725 :relation_type => IssueRelation::TYPE_RELATES)
1711 :relation_type => IssueRelation::TYPE_RELATES)
1726 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1712 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1727
1713
1728 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1714 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1729 end
1715 end
1730
1716
1731 context "#done_ratio" do
1717 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
1732 setup do
1718 @issue = Issue.find(1)
1733 @issue = Issue.find(1)
1719 @issue_status = IssueStatus.find(1)
1734 @issue_status = IssueStatus.find(1)
1720 @issue_status.update_attribute(:default_done_ratio, 50)
1735 @issue_status.update_attribute(:default_done_ratio, 50)
1721 @issue2 = Issue.find(2)
1736 @issue2 = Issue.find(2)
1722 @issue_status2 = IssueStatus.find(2)
1737 @issue_status2 = IssueStatus.find(2)
1723 @issue_status2.update_attribute(:default_done_ratio, 0)
1738 @issue_status2.update_attribute(:default_done_ratio, 0)
1739 end
1740
1724
1741 teardown do
1725 with_settings :issue_done_ratio => 'issue_field' do
1742 Setting.issue_done_ratio = 'issue_field'
1726 assert_equal 0, @issue.done_ratio
1727 assert_equal 30, @issue2.done_ratio
1743 end
1728 end
1744
1729
1745 context "with Setting.issue_done_ratio using the issue_field" do
1730 with_settings :issue_done_ratio => 'issue_status' do
1746 setup do
1731 assert_equal 50, @issue.done_ratio
1747 Setting.issue_done_ratio = 'issue_field'
1732 assert_equal 0, @issue2.done_ratio
1748 end
1749
1750 should "read the issue's field" do
1751 assert_equal 0, @issue.done_ratio
1752 assert_equal 30, @issue2.done_ratio
1753 end
1754 end
1755
1756 context "with Setting.issue_done_ratio using the issue_status" do
1757 setup do
1758 Setting.issue_done_ratio = 'issue_status'
1759 end
1760
1761 should "read the Issue Status's default done ratio" do
1762 assert_equal 50, @issue.done_ratio
1763 assert_equal 0, @issue2.done_ratio
1764 end
1765 end
1733 end
1766 end
1734 end
1767
1735
1768 context "#update_done_ratio_from_issue_status" do
1736 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
1769 setup do
1737 @issue = Issue.find(1)
1770 @issue = Issue.find(1)
1738 @issue_status = IssueStatus.find(1)
1771 @issue_status = IssueStatus.find(1)
1739 @issue_status.update_attribute(:default_done_ratio, 50)
1772 @issue_status.update_attribute(:default_done_ratio, 50)
1740 @issue2 = Issue.find(2)
1773 @issue2 = Issue.find(2)
1741 @issue_status2 = IssueStatus.find(2)
1774 @issue_status2 = IssueStatus.find(2)
1742 @issue_status2.update_attribute(:default_done_ratio, 0)
1775 @issue_status2.update_attribute(:default_done_ratio, 0)
1776 end
1777
1778 context "with Setting.issue_done_ratio using the issue_field" do
1779 setup do
1780 Setting.issue_done_ratio = 'issue_field'
1781 end
1782
1743
1783 should "not change the issue" do
1744 with_settings :issue_done_ratio => 'issue_field' do
1784 @issue.update_done_ratio_from_issue_status
1745 @issue.update_done_ratio_from_issue_status
1785 @issue2.update_done_ratio_from_issue_status
1746 @issue2.update_done_ratio_from_issue_status
1786
1747
1787 assert_equal 0, @issue.read_attribute(:done_ratio)
1748 assert_equal 0, @issue.read_attribute(:done_ratio)
1788 assert_equal 30, @issue2.read_attribute(:done_ratio)
1749 assert_equal 30, @issue2.read_attribute(:done_ratio)
1789 end
1790 end
1750 end
1791
1751
1792 context "with Setting.issue_done_ratio using the issue_status" do
1752 with_settings :issue_done_ratio => 'issue_status' do
1793 setup do
1753 @issue.update_done_ratio_from_issue_status
1794 Setting.issue_done_ratio = 'issue_status'
1754 @issue2.update_done_ratio_from_issue_status
1795 end
1796
1797 should "change the issue's done ratio" do
1798 @issue.update_done_ratio_from_issue_status
1799 @issue2.update_done_ratio_from_issue_status
1800
1755
1801 assert_equal 50, @issue.read_attribute(:done_ratio)
1756 assert_equal 50, @issue.read_attribute(:done_ratio)
1802 assert_equal 0, @issue2.read_attribute(:done_ratio)
1757 assert_equal 0, @issue2.read_attribute(:done_ratio)
1803 end
1804 end
1758 end
1805 end
1759 end
1806
1760
1807 test "#by_tracker" do
1761 test "#by_tracker" do
1808 User.current = User.anonymous
1762 User.current = User.anonymous
1809 groups = Issue.by_tracker(Project.find(1))
1763 groups = Issue.by_tracker(Project.find(1))
1810 assert_equal 3, groups.size
1764 assert_equal 3, groups.size
1811 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1765 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1812 end
1766 end
1813
1767
1814 test "#by_version" do
1768 test "#by_version" do
1815 User.current = User.anonymous
1769 User.current = User.anonymous
1816 groups = Issue.by_version(Project.find(1))
1770 groups = Issue.by_version(Project.find(1))
1817 assert_equal 3, groups.size
1771 assert_equal 3, groups.size
1818 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1772 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1819 end
1773 end
1820
1774
1821 test "#by_priority" do
1775 test "#by_priority" do
1822 User.current = User.anonymous
1776 User.current = User.anonymous
1823 groups = Issue.by_priority(Project.find(1))
1777 groups = Issue.by_priority(Project.find(1))
1824 assert_equal 4, groups.size
1778 assert_equal 4, groups.size
1825 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1779 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1826 end
1780 end
1827
1781
1828 test "#by_category" do
1782 test "#by_category" do
1829 User.current = User.anonymous
1783 User.current = User.anonymous
1830 groups = Issue.by_category(Project.find(1))
1784 groups = Issue.by_category(Project.find(1))
1831 assert_equal 2, groups.size
1785 assert_equal 2, groups.size
1832 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1786 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1833 end
1787 end
1834
1788
1835 test "#by_assigned_to" do
1789 test "#by_assigned_to" do
1836 User.current = User.anonymous
1790 User.current = User.anonymous
1837 groups = Issue.by_assigned_to(Project.find(1))
1791 groups = Issue.by_assigned_to(Project.find(1))
1838 assert_equal 2, groups.size
1792 assert_equal 2, groups.size
1839 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1793 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1840 end
1794 end
1841
1795
1842 test "#by_author" do
1796 test "#by_author" do
1843 User.current = User.anonymous
1797 User.current = User.anonymous
1844 groups = Issue.by_author(Project.find(1))
1798 groups = Issue.by_author(Project.find(1))
1845 assert_equal 4, groups.size
1799 assert_equal 4, groups.size
1846 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1800 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1847 end
1801 end
1848
1802
1849 test "#by_subproject" do
1803 test "#by_subproject" do
1850 User.current = User.anonymous
1804 User.current = User.anonymous
1851 groups = Issue.by_subproject(Project.find(1))
1805 groups = Issue.by_subproject(Project.find(1))
1852 # Private descendant not visible
1806 # Private descendant not visible
1853 assert_equal 1, groups.size
1807 assert_equal 1, groups.size
1854 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1808 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1855 end
1809 end
1856
1810
1857 def test_recently_updated_scope
1811 def test_recently_updated_scope
1858 #should return the last updated issue
1812 #should return the last updated issue
1859 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
1813 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
1860 end
1814 end
1861
1815
1862 def test_on_active_projects_scope
1816 def test_on_active_projects_scope
1863 assert Project.find(2).archive
1817 assert Project.find(2).archive
1864
1818
1865 before = Issue.on_active_project.length
1819 before = Issue.on_active_project.length
1866 # test inclusion to results
1820 # test inclusion to results
1867 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
1821 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
1868 assert_equal before + 1, Issue.on_active_project.length
1822 assert_equal before + 1, Issue.on_active_project.length
1869
1823
1870 # Move to an archived project
1824 # Move to an archived project
1871 issue.project = Project.find(2)
1825 issue.project = Project.find(2)
1872 assert issue.save
1826 assert issue.save
1873 assert_equal before, Issue.on_active_project.length
1827 assert_equal before, Issue.on_active_project.length
1874 end
1828 end
1875
1829
1876 context "Issue#recipients" do
1830 test "Issue#recipients should include project recipients" do
1877 setup do
1831 issue = Issue.generate!
1878 @project = Project.find(1)
1832 assert issue.project.recipients.present?
1879 @author = User.generate!
1833 issue.project.recipients.each do |project_recipient|
1880 @assignee = User.generate!
1834 assert issue.recipients.include?(project_recipient)
1881 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1882 end
1883
1884 should "include project recipients" do
1885 assert @project.recipients.present?
1886 @project.recipients.each do |project_recipient|
1887 assert @issue.recipients.include?(project_recipient)
1888 end
1889 end
1890
1891 should "include the author if the author is active" do
1892 assert @issue.author, "No author set for Issue"
1893 assert @issue.recipients.include?(@issue.author.mail)
1894 end
1895
1896 should "include the assigned to user if the assigned to user is active" do
1897 assert @issue.assigned_to, "No assigned_to set for Issue"
1898 assert @issue.recipients.include?(@issue.assigned_to.mail)
1899 end
1835 end
1836 end
1900
1837
1901 should "not include users who opt out of all email" do
1838 test "Issue#recipients should include the author if the author is active" do
1902 @author.update_attribute(:mail_notification, :none)
1839 issue = Issue.generate!(:author => User.generate!)
1903
1840 assert issue.author, "No author set for Issue"
1904 assert !@issue.recipients.include?(@issue.author.mail)
1841 assert issue.recipients.include?(issue.author.mail)
1905 end
1842 end
1906
1843
1907 should "not include the issue author if they are only notified of assigned issues" do
1844 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
1908 @author.update_attribute(:mail_notification, :only_assigned)
1845 issue = Issue.generate!(:assigned_to => User.generate!)
1846 assert issue.assigned_to, "No assigned_to set for Issue"
1847 assert issue.recipients.include?(issue.assigned_to.mail)
1848 end
1909
1849
1910 assert !@issue.recipients.include?(@issue.author.mail)
1850 test "Issue#recipients should not include users who opt out of all email" do
1911 end
1851 issue = Issue.generate!(:author => User.generate!)
1852 issue.author.update_attribute(:mail_notification, :none)
1853 assert !issue.recipients.include?(issue.author.mail)
1854 end
1912
1855
1913 should "not include the assigned user if they are only notified of owned issues" do
1856 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
1914 @assignee.update_attribute(:mail_notification, :only_owner)
1857 issue = Issue.generate!(:author => User.generate!)
1858 issue.author.update_attribute(:mail_notification, :only_assigned)
1859 assert !issue.recipients.include?(issue.author.mail)
1860 end
1915
1861
1916 assert !@issue.recipients.include?(@issue.assigned_to.mail)
1862 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
1917 end
1863 issue = Issue.generate!(:assigned_to => User.generate!)
1864 issue.assigned_to.update_attribute(:mail_notification, :only_owner)
1865 assert !issue.recipients.include?(issue.assigned_to.mail)
1918 end
1866 end
1919
1867
1920 def test_last_journal_id_with_journals_should_return_the_journal_id
1868 def test_last_journal_id_with_journals_should_return_the_journal_id
1921 assert_equal 2, Issue.find(1).last_journal_id
1869 assert_equal 2, Issue.find(1).last_journal_id
1922 end
1870 end
1923
1871
1924 def test_last_journal_id_without_journals_should_return_nil
1872 def test_last_journal_id_without_journals_should_return_nil
1925 assert_nil Issue.find(3).last_journal_id
1873 assert_nil Issue.find(3).last_journal_id
1926 end
1874 end
1927
1875
1928 def test_journals_after_should_return_journals_with_greater_id
1876 def test_journals_after_should_return_journals_with_greater_id
1929 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
1877 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
1930 assert_equal [], Issue.find(1).journals_after('2')
1878 assert_equal [], Issue.find(1).journals_after('2')
1931 end
1879 end
1932
1880
1933 def test_journals_after_with_blank_arg_should_return_all_journals
1881 def test_journals_after_with_blank_arg_should_return_all_journals
1934 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
1882 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
1935 end
1883 end
1936
1884
1937 def test_css_classes_should_include_priority
1885 def test_css_classes_should_include_priority
1938 issue = Issue.new(:priority => IssuePriority.find(8))
1886 issue = Issue.new(:priority => IssuePriority.find(8))
1939 classes = issue.css_classes.split(' ')
1887 classes = issue.css_classes.split(' ')
1940 assert_include 'priority-8', classes
1888 assert_include 'priority-8', classes
1941 assert_include 'priority-highest', classes
1889 assert_include 'priority-highest', classes
1942 end
1890 end
1943
1891
1944 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
1892 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
1945 set_tmp_attachments_directory
1893 set_tmp_attachments_directory
1946 issue = Issue.generate!
1894 issue = Issue.generate!
1947 issue.save_attachments({
1895 issue.save_attachments({
1948 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
1896 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
1949 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
1897 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
1950 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
1898 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
1951 })
1899 })
1952 issue.attach_saved_attachments
1900 issue.attach_saved_attachments
1953
1901
1954 assert_equal 3, issue.reload.attachments.count
1902 assert_equal 3, issue.reload.attachments.count
1955 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
1903 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
1956 end
1904 end
1957 end
1905 end
@@ -1,794 +1,776
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class MailHandlerTest < ActiveSupport::TestCase
22 class MailHandlerTest < ActiveSupport::TestCase
23 fixtures :users, :projects, :enabled_modules, :roles,
23 fixtures :users, :projects, :enabled_modules, :roles,
24 :members, :member_roles, :users,
24 :members, :member_roles, :users,
25 :issues, :issue_statuses,
25 :issues, :issue_statuses,
26 :workflows, :trackers, :projects_trackers,
26 :workflows, :trackers, :projects_trackers,
27 :versions, :enumerations, :issue_categories,
27 :versions, :enumerations, :issue_categories,
28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
29 :boards, :messages
29 :boards, :messages
30
30
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
32
32
33 def setup
33 def setup
34 ActionMailer::Base.deliveries.clear
34 ActionMailer::Base.deliveries.clear
35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
36 end
36 end
37
37
38 def teardown
38 def teardown
39 Setting.clear_cache
39 Setting.clear_cache
40 end
40 end
41
41
42 def test_add_issue
42 def test_add_issue
43 ActionMailer::Base.deliveries.clear
43 ActionMailer::Base.deliveries.clear
44 # This email contains: 'Project: onlinestore'
44 # This email contains: 'Project: onlinestore'
45 issue = submit_email('ticket_on_given_project.eml')
45 issue = submit_email('ticket_on_given_project.eml')
46 assert issue.is_a?(Issue)
46 assert issue.is_a?(Issue)
47 assert !issue.new_record?
47 assert !issue.new_record?
48 issue.reload
48 issue.reload
49 assert_equal Project.find(2), issue.project
49 assert_equal Project.find(2), issue.project
50 assert_equal issue.project.trackers.first, issue.tracker
50 assert_equal issue.project.trackers.first, issue.tracker
51 assert_equal 'New ticket on a given project', issue.subject
51 assert_equal 'New ticket on a given project', issue.subject
52 assert_equal User.find_by_login('jsmith'), issue.author
52 assert_equal User.find_by_login('jsmith'), issue.author
53 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
53 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
54 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
54 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
55 assert_equal '2010-01-01', issue.start_date.to_s
55 assert_equal '2010-01-01', issue.start_date.to_s
56 assert_equal '2010-12-31', issue.due_date.to_s
56 assert_equal '2010-12-31', issue.due_date.to_s
57 assert_equal User.find_by_login('jsmith'), issue.assigned_to
57 assert_equal User.find_by_login('jsmith'), issue.assigned_to
58 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
58 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
59 assert_equal 2.5, issue.estimated_hours
59 assert_equal 2.5, issue.estimated_hours
60 assert_equal 30, issue.done_ratio
60 assert_equal 30, issue.done_ratio
61 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
61 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
62 # keywords should be removed from the email body
62 # keywords should be removed from the email body
63 assert !issue.description.match(/^Project:/i)
63 assert !issue.description.match(/^Project:/i)
64 assert !issue.description.match(/^Status:/i)
64 assert !issue.description.match(/^Status:/i)
65 assert !issue.description.match(/^Start Date:/i)
65 assert !issue.description.match(/^Start Date:/i)
66 # Email notification should be sent
66 # Email notification should be sent
67 mail = ActionMailer::Base.deliveries.last
67 mail = ActionMailer::Base.deliveries.last
68 assert_not_nil mail
68 assert_not_nil mail
69 assert mail.subject.include?('New ticket on a given project')
69 assert mail.subject.include?('New ticket on a given project')
70 end
70 end
71
71
72 def test_add_issue_with_default_tracker
72 def test_add_issue_with_default_tracker
73 # This email contains: 'Project: onlinestore'
73 # This email contains: 'Project: onlinestore'
74 issue = submit_email(
74 issue = submit_email(
75 'ticket_on_given_project.eml',
75 'ticket_on_given_project.eml',
76 :issue => {:tracker => 'Support request'}
76 :issue => {:tracker => 'Support request'}
77 )
77 )
78 assert issue.is_a?(Issue)
78 assert issue.is_a?(Issue)
79 assert !issue.new_record?
79 assert !issue.new_record?
80 issue.reload
80 issue.reload
81 assert_equal 'Support request', issue.tracker.name
81 assert_equal 'Support request', issue.tracker.name
82 end
82 end
83
83
84 def test_add_issue_with_status
84 def test_add_issue_with_status
85 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
85 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
86 issue = submit_email('ticket_on_given_project.eml')
86 issue = submit_email('ticket_on_given_project.eml')
87 assert issue.is_a?(Issue)
87 assert issue.is_a?(Issue)
88 assert !issue.new_record?
88 assert !issue.new_record?
89 issue.reload
89 issue.reload
90 assert_equal Project.find(2), issue.project
90 assert_equal Project.find(2), issue.project
91 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
91 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
92 end
92 end
93
93
94 def test_add_issue_with_attributes_override
94 def test_add_issue_with_attributes_override
95 issue = submit_email(
95 issue = submit_email(
96 'ticket_with_attributes.eml',
96 'ticket_with_attributes.eml',
97 :allow_override => 'tracker,category,priority'
97 :allow_override => 'tracker,category,priority'
98 )
98 )
99 assert issue.is_a?(Issue)
99 assert issue.is_a?(Issue)
100 assert !issue.new_record?
100 assert !issue.new_record?
101 issue.reload
101 issue.reload
102 assert_equal 'New ticket on a given project', issue.subject
102 assert_equal 'New ticket on a given project', issue.subject
103 assert_equal User.find_by_login('jsmith'), issue.author
103 assert_equal User.find_by_login('jsmith'), issue.author
104 assert_equal Project.find(2), issue.project
104 assert_equal Project.find(2), issue.project
105 assert_equal 'Feature request', issue.tracker.to_s
105 assert_equal 'Feature request', issue.tracker.to_s
106 assert_equal 'Stock management', issue.category.to_s
106 assert_equal 'Stock management', issue.category.to_s
107 assert_equal 'Urgent', issue.priority.to_s
107 assert_equal 'Urgent', issue.priority.to_s
108 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
108 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
109 end
109 end
110
110
111 def test_add_issue_with_group_assignment
111 def test_add_issue_with_group_assignment
112 with_settings :issue_group_assignment => '1' do
112 with_settings :issue_group_assignment => '1' do
113 issue = submit_email('ticket_on_given_project.eml') do |email|
113 issue = submit_email('ticket_on_given_project.eml') do |email|
114 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
114 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
115 end
115 end
116 assert issue.is_a?(Issue)
116 assert issue.is_a?(Issue)
117 assert !issue.new_record?
117 assert !issue.new_record?
118 issue.reload
118 issue.reload
119 assert_equal Group.find(11), issue.assigned_to
119 assert_equal Group.find(11), issue.assigned_to
120 end
120 end
121 end
121 end
122
122
123 def test_add_issue_with_partial_attributes_override
123 def test_add_issue_with_partial_attributes_override
124 issue = submit_email(
124 issue = submit_email(
125 'ticket_with_attributes.eml',
125 'ticket_with_attributes.eml',
126 :issue => {:priority => 'High'},
126 :issue => {:priority => 'High'},
127 :allow_override => ['tracker']
127 :allow_override => ['tracker']
128 )
128 )
129 assert issue.is_a?(Issue)
129 assert issue.is_a?(Issue)
130 assert !issue.new_record?
130 assert !issue.new_record?
131 issue.reload
131 issue.reload
132 assert_equal 'New ticket on a given project', issue.subject
132 assert_equal 'New ticket on a given project', issue.subject
133 assert_equal User.find_by_login('jsmith'), issue.author
133 assert_equal User.find_by_login('jsmith'), issue.author
134 assert_equal Project.find(2), issue.project
134 assert_equal Project.find(2), issue.project
135 assert_equal 'Feature request', issue.tracker.to_s
135 assert_equal 'Feature request', issue.tracker.to_s
136 assert_nil issue.category
136 assert_nil issue.category
137 assert_equal 'High', issue.priority.to_s
137 assert_equal 'High', issue.priority.to_s
138 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
138 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
139 end
139 end
140
140
141 def test_add_issue_with_spaces_between_attribute_and_separator
141 def test_add_issue_with_spaces_between_attribute_and_separator
142 issue = submit_email(
142 issue = submit_email(
143 'ticket_with_spaces_between_attribute_and_separator.eml',
143 'ticket_with_spaces_between_attribute_and_separator.eml',
144 :allow_override => 'tracker,category,priority'
144 :allow_override => 'tracker,category,priority'
145 )
145 )
146 assert issue.is_a?(Issue)
146 assert issue.is_a?(Issue)
147 assert !issue.new_record?
147 assert !issue.new_record?
148 issue.reload
148 issue.reload
149 assert_equal 'New ticket on a given project', issue.subject
149 assert_equal 'New ticket on a given project', issue.subject
150 assert_equal User.find_by_login('jsmith'), issue.author
150 assert_equal User.find_by_login('jsmith'), issue.author
151 assert_equal Project.find(2), issue.project
151 assert_equal Project.find(2), issue.project
152 assert_equal 'Feature request', issue.tracker.to_s
152 assert_equal 'Feature request', issue.tracker.to_s
153 assert_equal 'Stock management', issue.category.to_s
153 assert_equal 'Stock management', issue.category.to_s
154 assert_equal 'Urgent', issue.priority.to_s
154 assert_equal 'Urgent', issue.priority.to_s
155 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
155 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
156 end
156 end
157
157
158 def test_add_issue_with_attachment_to_specific_project
158 def test_add_issue_with_attachment_to_specific_project
159 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
159 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
160 assert issue.is_a?(Issue)
160 assert issue.is_a?(Issue)
161 assert !issue.new_record?
161 assert !issue.new_record?
162 issue.reload
162 issue.reload
163 assert_equal 'Ticket created by email with attachment', issue.subject
163 assert_equal 'Ticket created by email with attachment', issue.subject
164 assert_equal User.find_by_login('jsmith'), issue.author
164 assert_equal User.find_by_login('jsmith'), issue.author
165 assert_equal Project.find(2), issue.project
165 assert_equal Project.find(2), issue.project
166 assert_equal 'This is a new ticket with attachments', issue.description
166 assert_equal 'This is a new ticket with attachments', issue.description
167 # Attachment properties
167 # Attachment properties
168 assert_equal 1, issue.attachments.size
168 assert_equal 1, issue.attachments.size
169 assert_equal 'Paella.jpg', issue.attachments.first.filename
169 assert_equal 'Paella.jpg', issue.attachments.first.filename
170 assert_equal 'image/jpeg', issue.attachments.first.content_type
170 assert_equal 'image/jpeg', issue.attachments.first.content_type
171 assert_equal 10790, issue.attachments.first.filesize
171 assert_equal 10790, issue.attachments.first.filesize
172 end
172 end
173
173
174 def test_add_issue_with_custom_fields
174 def test_add_issue_with_custom_fields
175 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
175 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
176 assert issue.is_a?(Issue)
176 assert issue.is_a?(Issue)
177 assert !issue.new_record?
177 assert !issue.new_record?
178 issue.reload
178 issue.reload
179 assert_equal 'New ticket with custom field values', issue.subject
179 assert_equal 'New ticket with custom field values', issue.subject
180 assert_equal 'PostgreSQL', issue.custom_field_value(1)
180 assert_equal 'PostgreSQL', issue.custom_field_value(1)
181 assert_equal 'Value for a custom field', issue.custom_field_value(2)
181 assert_equal 'Value for a custom field', issue.custom_field_value(2)
182 assert !issue.description.match(/^searchable field:/i)
182 assert !issue.description.match(/^searchable field:/i)
183 end
183 end
184
184
185 def test_add_issue_with_version_custom_fields
185 def test_add_issue_with_version_custom_fields
186 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
186 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
187
187
188 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
188 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
189 email << "Affected version: 1.0\n"
189 email << "Affected version: 1.0\n"
190 end
190 end
191 assert issue.is_a?(Issue)
191 assert issue.is_a?(Issue)
192 assert !issue.new_record?
192 assert !issue.new_record?
193 issue.reload
193 issue.reload
194 assert_equal '2', issue.custom_field_value(field)
194 assert_equal '2', issue.custom_field_value(field)
195 end
195 end
196
196
197 def test_add_issue_should_match_assignee_on_display_name
197 def test_add_issue_should_match_assignee_on_display_name
198 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
198 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
199 User.add_to_project(user, Project.find(2))
199 User.add_to_project(user, Project.find(2))
200 issue = submit_email('ticket_on_given_project.eml') do |email|
200 issue = submit_email('ticket_on_given_project.eml') do |email|
201 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
201 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
202 end
202 end
203 assert issue.is_a?(Issue)
203 assert issue.is_a?(Issue)
204 assert_equal user, issue.assigned_to
204 assert_equal user, issue.assigned_to
205 end
205 end
206
206
207 def test_add_issue_with_cc
207 def test_add_issue_with_cc
208 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
208 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
209 assert issue.is_a?(Issue)
209 assert issue.is_a?(Issue)
210 assert !issue.new_record?
210 assert !issue.new_record?
211 issue.reload
211 issue.reload
212 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
212 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
213 assert_equal 1, issue.watcher_user_ids.size
213 assert_equal 1, issue.watcher_user_ids.size
214 end
214 end
215
215
216 def test_add_issue_by_unknown_user
216 def test_add_issue_by_unknown_user
217 assert_no_difference 'User.count' do
217 assert_no_difference 'User.count' do
218 assert_equal false,
218 assert_equal false,
219 submit_email(
219 submit_email(
220 'ticket_by_unknown_user.eml',
220 'ticket_by_unknown_user.eml',
221 :issue => {:project => 'ecookbook'}
221 :issue => {:project => 'ecookbook'}
222 )
222 )
223 end
223 end
224 end
224 end
225
225
226 def test_add_issue_by_anonymous_user
226 def test_add_issue_by_anonymous_user
227 Role.anonymous.add_permission!(:add_issues)
227 Role.anonymous.add_permission!(:add_issues)
228 assert_no_difference 'User.count' do
228 assert_no_difference 'User.count' do
229 issue = submit_email(
229 issue = submit_email(
230 'ticket_by_unknown_user.eml',
230 'ticket_by_unknown_user.eml',
231 :issue => {:project => 'ecookbook'},
231 :issue => {:project => 'ecookbook'},
232 :unknown_user => 'accept'
232 :unknown_user => 'accept'
233 )
233 )
234 assert issue.is_a?(Issue)
234 assert issue.is_a?(Issue)
235 assert issue.author.anonymous?
235 assert issue.author.anonymous?
236 end
236 end
237 end
237 end
238
238
239 def test_add_issue_by_anonymous_user_with_no_from_address
239 def test_add_issue_by_anonymous_user_with_no_from_address
240 Role.anonymous.add_permission!(:add_issues)
240 Role.anonymous.add_permission!(:add_issues)
241 assert_no_difference 'User.count' do
241 assert_no_difference 'User.count' do
242 issue = submit_email(
242 issue = submit_email(
243 'ticket_by_empty_user.eml',
243 'ticket_by_empty_user.eml',
244 :issue => {:project => 'ecookbook'},
244 :issue => {:project => 'ecookbook'},
245 :unknown_user => 'accept'
245 :unknown_user => 'accept'
246 )
246 )
247 assert issue.is_a?(Issue)
247 assert issue.is_a?(Issue)
248 assert issue.author.anonymous?
248 assert issue.author.anonymous?
249 end
249 end
250 end
250 end
251
251
252 def test_add_issue_by_anonymous_user_on_private_project
252 def test_add_issue_by_anonymous_user_on_private_project
253 Role.anonymous.add_permission!(:add_issues)
253 Role.anonymous.add_permission!(:add_issues)
254 assert_no_difference 'User.count' do
254 assert_no_difference 'User.count' do
255 assert_no_difference 'Issue.count' do
255 assert_no_difference 'Issue.count' do
256 assert_equal false,
256 assert_equal false,
257 submit_email(
257 submit_email(
258 'ticket_by_unknown_user.eml',
258 'ticket_by_unknown_user.eml',
259 :issue => {:project => 'onlinestore'},
259 :issue => {:project => 'onlinestore'},
260 :unknown_user => 'accept'
260 :unknown_user => 'accept'
261 )
261 )
262 end
262 end
263 end
263 end
264 end
264 end
265
265
266 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
266 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
267 assert_no_difference 'User.count' do
267 assert_no_difference 'User.count' do
268 assert_difference 'Issue.count' do
268 assert_difference 'Issue.count' do
269 issue = submit_email(
269 issue = submit_email(
270 'ticket_by_unknown_user.eml',
270 'ticket_by_unknown_user.eml',
271 :issue => {:project => 'onlinestore'},
271 :issue => {:project => 'onlinestore'},
272 :no_permission_check => '1',
272 :no_permission_check => '1',
273 :unknown_user => 'accept'
273 :unknown_user => 'accept'
274 )
274 )
275 assert issue.is_a?(Issue)
275 assert issue.is_a?(Issue)
276 assert issue.author.anonymous?
276 assert issue.author.anonymous?
277 assert !issue.project.is_public?
277 assert !issue.project.is_public?
278 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
278 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
279 end
279 end
280 end
280 end
281 end
281 end
282
282
283 def test_add_issue_by_created_user
283 def test_add_issue_by_created_user
284 Setting.default_language = 'en'
284 Setting.default_language = 'en'
285 assert_difference 'User.count' do
285 assert_difference 'User.count' do
286 issue = submit_email(
286 issue = submit_email(
287 'ticket_by_unknown_user.eml',
287 'ticket_by_unknown_user.eml',
288 :issue => {:project => 'ecookbook'},
288 :issue => {:project => 'ecookbook'},
289 :unknown_user => 'create'
289 :unknown_user => 'create'
290 )
290 )
291 assert issue.is_a?(Issue)
291 assert issue.is_a?(Issue)
292 assert issue.author.active?
292 assert issue.author.active?
293 assert_equal 'john.doe@somenet.foo', issue.author.mail
293 assert_equal 'john.doe@somenet.foo', issue.author.mail
294 assert_equal 'John', issue.author.firstname
294 assert_equal 'John', issue.author.firstname
295 assert_equal 'Doe', issue.author.lastname
295 assert_equal 'Doe', issue.author.lastname
296
296
297 # account information
297 # account information
298 email = ActionMailer::Base.deliveries.first
298 email = ActionMailer::Base.deliveries.first
299 assert_not_nil email
299 assert_not_nil email
300 assert email.subject.include?('account activation')
300 assert email.subject.include?('account activation')
301 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
301 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
302 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
302 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
303 assert_equal issue.author, User.try_to_login(login, password)
303 assert_equal issue.author, User.try_to_login(login, password)
304 end
304 end
305 end
305 end
306
306
307 def test_add_issue_without_from_header
307 def test_add_issue_without_from_header
308 Role.anonymous.add_permission!(:add_issues)
308 Role.anonymous.add_permission!(:add_issues)
309 assert_equal false, submit_email('ticket_without_from_header.eml')
309 assert_equal false, submit_email('ticket_without_from_header.eml')
310 end
310 end
311
311
312 def test_add_issue_with_invalid_attributes
312 def test_add_issue_with_invalid_attributes
313 issue = submit_email(
313 issue = submit_email(
314 'ticket_with_invalid_attributes.eml',
314 'ticket_with_invalid_attributes.eml',
315 :allow_override => 'tracker,category,priority'
315 :allow_override => 'tracker,category,priority'
316 )
316 )
317 assert issue.is_a?(Issue)
317 assert issue.is_a?(Issue)
318 assert !issue.new_record?
318 assert !issue.new_record?
319 issue.reload
319 issue.reload
320 assert_nil issue.assigned_to
320 assert_nil issue.assigned_to
321 assert_nil issue.start_date
321 assert_nil issue.start_date
322 assert_nil issue.due_date
322 assert_nil issue.due_date
323 assert_equal 0, issue.done_ratio
323 assert_equal 0, issue.done_ratio
324 assert_equal 'Normal', issue.priority.to_s
324 assert_equal 'Normal', issue.priority.to_s
325 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
325 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
326 end
326 end
327
327
328 def test_add_issue_with_localized_attributes
328 def test_add_issue_with_localized_attributes
329 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
329 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
330 issue = submit_email(
330 issue = submit_email(
331 'ticket_with_localized_attributes.eml',
331 'ticket_with_localized_attributes.eml',
332 :allow_override => 'tracker,category,priority'
332 :allow_override => 'tracker,category,priority'
333 )
333 )
334 assert issue.is_a?(Issue)
334 assert issue.is_a?(Issue)
335 assert !issue.new_record?
335 assert !issue.new_record?
336 issue.reload
336 issue.reload
337 assert_equal 'New ticket on a given project', issue.subject
337 assert_equal 'New ticket on a given project', issue.subject
338 assert_equal User.find_by_login('jsmith'), issue.author
338 assert_equal User.find_by_login('jsmith'), issue.author
339 assert_equal Project.find(2), issue.project
339 assert_equal Project.find(2), issue.project
340 assert_equal 'Feature request', issue.tracker.to_s
340 assert_equal 'Feature request', issue.tracker.to_s
341 assert_equal 'Stock management', issue.category.to_s
341 assert_equal 'Stock management', issue.category.to_s
342 assert_equal 'Urgent', issue.priority.to_s
342 assert_equal 'Urgent', issue.priority.to_s
343 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
343 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
344 end
344 end
345
345
346 def test_add_issue_with_japanese_keywords
346 def test_add_issue_with_japanese_keywords
347 ja_dev = "\xe9\x96\x8b\xe7\x99\xba"
347 ja_dev = "\xe9\x96\x8b\xe7\x99\xba"
348 ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding)
348 ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding)
349 tracker = Tracker.create!(:name => ja_dev)
349 tracker = Tracker.create!(:name => ja_dev)
350 Project.find(1).trackers << tracker
350 Project.find(1).trackers << tracker
351 issue = submit_email(
351 issue = submit_email(
352 'japanese_keywords_iso_2022_jp.eml',
352 'japanese_keywords_iso_2022_jp.eml',
353 :issue => {:project => 'ecookbook'},
353 :issue => {:project => 'ecookbook'},
354 :allow_override => 'tracker'
354 :allow_override => 'tracker'
355 )
355 )
356 assert_kind_of Issue, issue
356 assert_kind_of Issue, issue
357 assert_equal tracker, issue.tracker
357 assert_equal tracker, issue.tracker
358 end
358 end
359
359
360 def test_add_issue_from_apple_mail
360 def test_add_issue_from_apple_mail
361 issue = submit_email(
361 issue = submit_email(
362 'apple_mail_with_attachment.eml',
362 'apple_mail_with_attachment.eml',
363 :issue => {:project => 'ecookbook'}
363 :issue => {:project => 'ecookbook'}
364 )
364 )
365 assert_kind_of Issue, issue
365 assert_kind_of Issue, issue
366 assert_equal 1, issue.attachments.size
366 assert_equal 1, issue.attachments.size
367
367
368 attachment = issue.attachments.first
368 attachment = issue.attachments.first
369 assert_equal 'paella.jpg', attachment.filename
369 assert_equal 'paella.jpg', attachment.filename
370 assert_equal 10790, attachment.filesize
370 assert_equal 10790, attachment.filesize
371 assert File.exist?(attachment.diskfile)
371 assert File.exist?(attachment.diskfile)
372 assert_equal 10790, File.size(attachment.diskfile)
372 assert_equal 10790, File.size(attachment.diskfile)
373 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
373 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
374 end
374 end
375
375
376 def test_thunderbird_with_attachment_ja
376 def test_thunderbird_with_attachment_ja
377 issue = submit_email(
377 issue = submit_email(
378 'thunderbird_with_attachment_ja.eml',
378 'thunderbird_with_attachment_ja.eml',
379 :issue => {:project => 'ecookbook'}
379 :issue => {:project => 'ecookbook'}
380 )
380 )
381 assert_kind_of Issue, issue
381 assert_kind_of Issue, issue
382 assert_equal 1, issue.attachments.size
382 assert_equal 1, issue.attachments.size
383 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
383 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
384 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
384 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
385 attachment = issue.attachments.first
385 attachment = issue.attachments.first
386 assert_equal ja, attachment.filename
386 assert_equal ja, attachment.filename
387 assert_equal 5, attachment.filesize
387 assert_equal 5, attachment.filesize
388 assert File.exist?(attachment.diskfile)
388 assert File.exist?(attachment.diskfile)
389 assert_equal 5, File.size(attachment.diskfile)
389 assert_equal 5, File.size(attachment.diskfile)
390 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
390 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
391 end
391 end
392
392
393 def test_gmail_with_attachment_ja
393 def test_gmail_with_attachment_ja
394 issue = submit_email(
394 issue = submit_email(
395 'gmail_with_attachment_ja.eml',
395 'gmail_with_attachment_ja.eml',
396 :issue => {:project => 'ecookbook'}
396 :issue => {:project => 'ecookbook'}
397 )
397 )
398 assert_kind_of Issue, issue
398 assert_kind_of Issue, issue
399 assert_equal 1, issue.attachments.size
399 assert_equal 1, issue.attachments.size
400 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
400 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
401 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
401 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
402 attachment = issue.attachments.first
402 attachment = issue.attachments.first
403 assert_equal ja, attachment.filename
403 assert_equal ja, attachment.filename
404 assert_equal 5, attachment.filesize
404 assert_equal 5, attachment.filesize
405 assert File.exist?(attachment.diskfile)
405 assert File.exist?(attachment.diskfile)
406 assert_equal 5, File.size(attachment.diskfile)
406 assert_equal 5, File.size(attachment.diskfile)
407 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
407 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
408 end
408 end
409
409
410 def test_thunderbird_with_attachment_latin1
410 def test_thunderbird_with_attachment_latin1
411 issue = submit_email(
411 issue = submit_email(
412 'thunderbird_with_attachment_iso-8859-1.eml',
412 'thunderbird_with_attachment_iso-8859-1.eml',
413 :issue => {:project => 'ecookbook'}
413 :issue => {:project => 'ecookbook'}
414 )
414 )
415 assert_kind_of Issue, issue
415 assert_kind_of Issue, issue
416 assert_equal 1, issue.attachments.size
416 assert_equal 1, issue.attachments.size
417 u = ""
417 u = ""
418 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
418 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
419 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
419 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
420 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
420 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
421 11.times { u << u1 }
421 11.times { u << u1 }
422 attachment = issue.attachments.first
422 attachment = issue.attachments.first
423 assert_equal "#{u}.png", attachment.filename
423 assert_equal "#{u}.png", attachment.filename
424 assert_equal 130, attachment.filesize
424 assert_equal 130, attachment.filesize
425 assert File.exist?(attachment.diskfile)
425 assert File.exist?(attachment.diskfile)
426 assert_equal 130, File.size(attachment.diskfile)
426 assert_equal 130, File.size(attachment.diskfile)
427 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
427 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
428 end
428 end
429
429
430 def test_gmail_with_attachment_latin1
430 def test_gmail_with_attachment_latin1
431 issue = submit_email(
431 issue = submit_email(
432 'gmail_with_attachment_iso-8859-1.eml',
432 'gmail_with_attachment_iso-8859-1.eml',
433 :issue => {:project => 'ecookbook'}
433 :issue => {:project => 'ecookbook'}
434 )
434 )
435 assert_kind_of Issue, issue
435 assert_kind_of Issue, issue
436 assert_equal 1, issue.attachments.size
436 assert_equal 1, issue.attachments.size
437 u = ""
437 u = ""
438 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
438 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
439 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
439 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
440 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
440 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
441 11.times { u << u1 }
441 11.times { u << u1 }
442 attachment = issue.attachments.first
442 attachment = issue.attachments.first
443 assert_equal "#{u}.txt", attachment.filename
443 assert_equal "#{u}.txt", attachment.filename
444 assert_equal 5, attachment.filesize
444 assert_equal 5, attachment.filesize
445 assert File.exist?(attachment.diskfile)
445 assert File.exist?(attachment.diskfile)
446 assert_equal 5, File.size(attachment.diskfile)
446 assert_equal 5, File.size(attachment.diskfile)
447 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
447 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
448 end
448 end
449
449
450 def test_add_issue_with_iso_8859_1_subject
450 def test_add_issue_with_iso_8859_1_subject
451 issue = submit_email(
451 issue = submit_email(
452 'subject_as_iso-8859-1.eml',
452 'subject_as_iso-8859-1.eml',
453 :issue => {:project => 'ecookbook'}
453 :issue => {:project => 'ecookbook'}
454 )
454 )
455 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc..."
455 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc..."
456 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
456 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
457 assert_kind_of Issue, issue
457 assert_kind_of Issue, issue
458 assert_equal str, issue.subject
458 assert_equal str, issue.subject
459 end
459 end
460
460
461 def test_add_issue_with_japanese_subject
461 def test_add_issue_with_japanese_subject
462 issue = submit_email(
462 issue = submit_email(
463 'subject_japanese_1.eml',
463 'subject_japanese_1.eml',
464 :issue => {:project => 'ecookbook'}
464 :issue => {:project => 'ecookbook'}
465 )
465 )
466 assert_kind_of Issue, issue
466 assert_kind_of Issue, issue
467 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
467 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
468 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
468 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
469 assert_equal ja, issue.subject
469 assert_equal ja, issue.subject
470 end
470 end
471
471
472 def test_add_issue_with_no_subject_header
472 def test_add_issue_with_no_subject_header
473 issue = submit_email(
473 issue = submit_email(
474 'no_subject_header.eml',
474 'no_subject_header.eml',
475 :issue => {:project => 'ecookbook'}
475 :issue => {:project => 'ecookbook'}
476 )
476 )
477 assert_kind_of Issue, issue
477 assert_kind_of Issue, issue
478 assert_equal '(no subject)', issue.subject
478 assert_equal '(no subject)', issue.subject
479 end
479 end
480
480
481 def test_add_issue_with_mixed_japanese_subject
481 def test_add_issue_with_mixed_japanese_subject
482 issue = submit_email(
482 issue = submit_email(
483 'subject_japanese_2.eml',
483 'subject_japanese_2.eml',
484 :issue => {:project => 'ecookbook'}
484 :issue => {:project => 'ecookbook'}
485 )
485 )
486 assert_kind_of Issue, issue
486 assert_kind_of Issue, issue
487 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
487 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
488 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
488 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
489 assert_equal ja, issue.subject
489 assert_equal ja, issue.subject
490 end
490 end
491
491
492 def test_should_ignore_emails_from_locked_users
492 def test_should_ignore_emails_from_locked_users
493 User.find(2).lock!
493 User.find(2).lock!
494
494
495 MailHandler.any_instance.expects(:dispatch).never
495 MailHandler.any_instance.expects(:dispatch).never
496 assert_no_difference 'Issue.count' do
496 assert_no_difference 'Issue.count' do
497 assert_equal false, submit_email('ticket_on_given_project.eml')
497 assert_equal false, submit_email('ticket_on_given_project.eml')
498 end
498 end
499 end
499 end
500
500
501 def test_should_ignore_emails_from_emission_address
501 def test_should_ignore_emails_from_emission_address
502 Role.anonymous.add_permission!(:add_issues)
502 Role.anonymous.add_permission!(:add_issues)
503 assert_no_difference 'User.count' do
503 assert_no_difference 'User.count' do
504 assert_equal false,
504 assert_equal false,
505 submit_email(
505 submit_email(
506 'ticket_from_emission_address.eml',
506 'ticket_from_emission_address.eml',
507 :issue => {:project => 'ecookbook'},
507 :issue => {:project => 'ecookbook'},
508 :unknown_user => 'create'
508 :unknown_user => 'create'
509 )
509 )
510 end
510 end
511 end
511 end
512
512
513 def test_should_ignore_auto_replied_emails
513 def test_should_ignore_auto_replied_emails
514 MailHandler.any_instance.expects(:dispatch).never
514 MailHandler.any_instance.expects(:dispatch).never
515 [
515 [
516 "X-Auto-Response-Suppress: OOF",
516 "X-Auto-Response-Suppress: OOF",
517 "Auto-Submitted: auto-replied",
517 "Auto-Submitted: auto-replied",
518 "Auto-Submitted: Auto-Replied",
518 "Auto-Submitted: Auto-Replied",
519 "Auto-Submitted: auto-generated"
519 "Auto-Submitted: auto-generated"
520 ].each do |header|
520 ].each do |header|
521 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
521 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
522 raw = header + "\n" + raw
522 raw = header + "\n" + raw
523
523
524 assert_no_difference 'Issue.count' do
524 assert_no_difference 'Issue.count' do
525 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
525 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
526 end
526 end
527 end
527 end
528 end
528 end
529
529
530 def test_add_issue_should_send_email_notification
530 def test_add_issue_should_send_email_notification
531 Setting.notified_events = ['issue_added']
531 Setting.notified_events = ['issue_added']
532 ActionMailer::Base.deliveries.clear
532 ActionMailer::Base.deliveries.clear
533 # This email contains: 'Project: onlinestore'
533 # This email contains: 'Project: onlinestore'
534 issue = submit_email('ticket_on_given_project.eml')
534 issue = submit_email('ticket_on_given_project.eml')
535 assert issue.is_a?(Issue)
535 assert issue.is_a?(Issue)
536 assert_equal 1, ActionMailer::Base.deliveries.size
536 assert_equal 1, ActionMailer::Base.deliveries.size
537 end
537 end
538
538
539 def test_update_issue
539 def test_update_issue
540 journal = submit_email('ticket_reply.eml')
540 journal = submit_email('ticket_reply.eml')
541 assert journal.is_a?(Journal)
541 assert journal.is_a?(Journal)
542 assert_equal User.find_by_login('jsmith'), journal.user
542 assert_equal User.find_by_login('jsmith'), journal.user
543 assert_equal Issue.find(2), journal.journalized
543 assert_equal Issue.find(2), journal.journalized
544 assert_match /This is reply/, journal.notes
544 assert_match /This is reply/, journal.notes
545 assert_equal false, journal.private_notes
545 assert_equal false, journal.private_notes
546 assert_equal 'Feature request', journal.issue.tracker.name
546 assert_equal 'Feature request', journal.issue.tracker.name
547 end
547 end
548
548
549 def test_update_issue_with_attribute_changes
549 def test_update_issue_with_attribute_changes
550 # This email contains: 'Status: Resolved'
550 # This email contains: 'Status: Resolved'
551 journal = submit_email('ticket_reply_with_status.eml')
551 journal = submit_email('ticket_reply_with_status.eml')
552 assert journal.is_a?(Journal)
552 assert journal.is_a?(Journal)
553 issue = Issue.find(journal.issue.id)
553 issue = Issue.find(journal.issue.id)
554 assert_equal User.find_by_login('jsmith'), journal.user
554 assert_equal User.find_by_login('jsmith'), journal.user
555 assert_equal Issue.find(2), journal.journalized
555 assert_equal Issue.find(2), journal.journalized
556 assert_match /This is reply/, journal.notes
556 assert_match /This is reply/, journal.notes
557 assert_equal 'Feature request', journal.issue.tracker.name
557 assert_equal 'Feature request', journal.issue.tracker.name
558 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
558 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
559 assert_equal '2010-01-01', issue.start_date.to_s
559 assert_equal '2010-01-01', issue.start_date.to_s
560 assert_equal '2010-12-31', issue.due_date.to_s
560 assert_equal '2010-12-31', issue.due_date.to_s
561 assert_equal User.find_by_login('jsmith'), issue.assigned_to
561 assert_equal User.find_by_login('jsmith'), issue.assigned_to
562 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
562 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
563 # keywords should be removed from the email body
563 # keywords should be removed from the email body
564 assert !journal.notes.match(/^Status:/i)
564 assert !journal.notes.match(/^Status:/i)
565 assert !journal.notes.match(/^Start Date:/i)
565 assert !journal.notes.match(/^Start Date:/i)
566 end
566 end
567
567
568 def test_update_issue_with_attachment
568 def test_update_issue_with_attachment
569 assert_difference 'Journal.count' do
569 assert_difference 'Journal.count' do
570 assert_difference 'JournalDetail.count' do
570 assert_difference 'JournalDetail.count' do
571 assert_difference 'Attachment.count' do
571 assert_difference 'Attachment.count' do
572 assert_no_difference 'Issue.count' do
572 assert_no_difference 'Issue.count' do
573 journal = submit_email('ticket_with_attachment.eml') do |raw|
573 journal = submit_email('ticket_with_attachment.eml') do |raw|
574 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
574 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
575 end
575 end
576 end
576 end
577 end
577 end
578 end
578 end
579 end
579 end
580 journal = Journal.first(:order => 'id DESC')
580 journal = Journal.first(:order => 'id DESC')
581 assert_equal Issue.find(2), journal.journalized
581 assert_equal Issue.find(2), journal.journalized
582 assert_equal 1, journal.details.size
582 assert_equal 1, journal.details.size
583
583
584 detail = journal.details.first
584 detail = journal.details.first
585 assert_equal 'attachment', detail.property
585 assert_equal 'attachment', detail.property
586 assert_equal 'Paella.jpg', detail.value
586 assert_equal 'Paella.jpg', detail.value
587 end
587 end
588
588
589 def test_update_issue_should_send_email_notification
589 def test_update_issue_should_send_email_notification
590 ActionMailer::Base.deliveries.clear
590 ActionMailer::Base.deliveries.clear
591 journal = submit_email('ticket_reply.eml')
591 journal = submit_email('ticket_reply.eml')
592 assert journal.is_a?(Journal)
592 assert journal.is_a?(Journal)
593 assert_equal 1, ActionMailer::Base.deliveries.size
593 assert_equal 1, ActionMailer::Base.deliveries.size
594 end
594 end
595
595
596 def test_update_issue_should_not_set_defaults
596 def test_update_issue_should_not_set_defaults
597 journal = submit_email(
597 journal = submit_email(
598 'ticket_reply.eml',
598 'ticket_reply.eml',
599 :issue => {:tracker => 'Support request', :priority => 'High'}
599 :issue => {:tracker => 'Support request', :priority => 'High'}
600 )
600 )
601 assert journal.is_a?(Journal)
601 assert journal.is_a?(Journal)
602 assert_match /This is reply/, journal.notes
602 assert_match /This is reply/, journal.notes
603 assert_equal 'Feature request', journal.issue.tracker.name
603 assert_equal 'Feature request', journal.issue.tracker.name
604 assert_equal 'Normal', journal.issue.priority.name
604 assert_equal 'Normal', journal.issue.priority.name
605 end
605 end
606
606
607 def test_replying_to_a_private_note_should_add_reply_as_private
607 def test_replying_to_a_private_note_should_add_reply_as_private
608 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
608 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
609
609
610 assert_difference 'Journal.count' do
610 assert_difference 'Journal.count' do
611 journal = submit_email('ticket_reply.eml') do |email|
611 journal = submit_email('ticket_reply.eml') do |email|
612 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
612 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
613 end
613 end
614
614
615 assert_kind_of Journal, journal
615 assert_kind_of Journal, journal
616 assert_match /This is reply/, journal.notes
616 assert_match /This is reply/, journal.notes
617 assert_equal true, journal.private_notes
617 assert_equal true, journal.private_notes
618 end
618 end
619 end
619 end
620
620
621 def test_reply_to_a_message
621 def test_reply_to_a_message
622 m = submit_email('message_reply.eml')
622 m = submit_email('message_reply.eml')
623 assert m.is_a?(Message)
623 assert m.is_a?(Message)
624 assert !m.new_record?
624 assert !m.new_record?
625 m.reload
625 m.reload
626 assert_equal 'Reply via email', m.subject
626 assert_equal 'Reply via email', m.subject
627 # The email replies to message #2 which is part of the thread of message #1
627 # The email replies to message #2 which is part of the thread of message #1
628 assert_equal Message.find(1), m.parent
628 assert_equal Message.find(1), m.parent
629 end
629 end
630
630
631 def test_reply_to_a_message_by_subject
631 def test_reply_to_a_message_by_subject
632 m = submit_email('message_reply_by_subject.eml')
632 m = submit_email('message_reply_by_subject.eml')
633 assert m.is_a?(Message)
633 assert m.is_a?(Message)
634 assert !m.new_record?
634 assert !m.new_record?
635 m.reload
635 m.reload
636 assert_equal 'Reply to the first post', m.subject
636 assert_equal 'Reply to the first post', m.subject
637 assert_equal Message.find(1), m.parent
637 assert_equal Message.find(1), m.parent
638 end
638 end
639
639
640 def test_should_strip_tags_of_html_only_emails
640 def test_should_strip_tags_of_html_only_emails
641 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
641 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
642 assert issue.is_a?(Issue)
642 assert issue.is_a?(Issue)
643 assert !issue.new_record?
643 assert !issue.new_record?
644 issue.reload
644 issue.reload
645 assert_equal 'HTML email', issue.subject
645 assert_equal 'HTML email', issue.subject
646 assert_equal 'This is a html-only email.', issue.description
646 assert_equal 'This is a html-only email.', issue.description
647 end
647 end
648
648
649 context "truncate emails based on the Setting" do
649 test "truncate emails with no setting should add the entire email into the issue" do
650 context "with no setting" do
650 with_settings :mail_handler_body_delimiters => '' do
651 setup do
651 issue = submit_email('ticket_on_given_project.eml')
652 Setting.mail_handler_body_delimiters = ''
652 assert_issue_created(issue)
653 end
653 assert issue.description.include?('---')
654
654 assert issue.description.include?('This paragraph is after the delimiter')
655 should "add the entire email into the issue" do
656 issue = submit_email('ticket_on_given_project.eml')
657 assert_issue_created(issue)
658 assert issue.description.include?('---')
659 assert issue.description.include?('This paragraph is after the delimiter')
660 end
661 end
655 end
656 end
662
657
663 context "with a single string" do
658 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
664 setup do
659 with_settings :mail_handler_body_delimiters => '---' do
665 Setting.mail_handler_body_delimiters = '---'
660 issue = submit_email('ticket_on_given_project.eml')
666 end
661 assert_issue_created(issue)
667 should "truncate the email at the delimiter for the issue" do
662 assert issue.description.include?('This paragraph is before delimiters')
668 issue = submit_email('ticket_on_given_project.eml')
663 assert issue.description.include?('--- This line starts with a delimiter')
669 assert_issue_created(issue)
664 assert !issue.description.match(/^---$/)
670 assert issue.description.include?('This paragraph is before delimiters')
665 assert !issue.description.include?('This paragraph is after the delimiter')
671 assert issue.description.include?('--- This line starts with a delimiter')
672 assert !issue.description.match(/^---$/)
673 assert !issue.description.include?('This paragraph is after the delimiter')
674 end
675 end
666 end
667 end
676
668
677 context "with a single quoted reply (e.g. reply to a Redmine email notification)" do
669 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
678 setup do
670 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
679 Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---'
671 journal = submit_email('issue_update_with_quoted_reply_above.eml')
680 end
672 assert journal.is_a?(Journal)
681 should "truncate the email at the delimiter with the quoted reply symbols (>)" do
673 assert journal.notes.include?('An update to the issue by the sender.')
682 journal = submit_email('issue_update_with_quoted_reply_above.eml')
674 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
683 assert journal.is_a?(Journal)
675 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
684 assert journal.notes.include?('An update to the issue by the sender.')
685 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
686 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
687 end
688 end
676 end
677 end
689
678
690 context "with multiple quoted replies (e.g. reply to a reply of a Redmine email notification)" do
679 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
691 setup do
680 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
692 Setting.mail_handler_body_delimiters = '--- Reply above. Do not remove this line. ---'
681 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
693 end
682 assert journal.is_a?(Journal)
694 should "truncate the email at the delimiter with the quoted reply symbols (>)" do
683 assert journal.notes.include?('An update to the issue by the sender.')
695 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
684 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
696 assert journal.is_a?(Journal)
685 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
697 assert journal.notes.include?('An update to the issue by the sender.')
698 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
699 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
700 end
701 end
686 end
687 end
702
688
703 context "with multiple strings" do
689 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
704 setup do
690 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
705 Setting.mail_handler_body_delimiters = "---\nBREAK"
691 issue = submit_email('ticket_on_given_project.eml')
706 end
692 assert_issue_created(issue)
707 should "truncate the email at the first delimiter found (BREAK)" do
693 assert issue.description.include?('This paragraph is before delimiters')
708 issue = submit_email('ticket_on_given_project.eml')
694 assert !issue.description.include?('BREAK')
709 assert_issue_created(issue)
695 assert !issue.description.include?('This paragraph is between delimiters')
710 assert issue.description.include?('This paragraph is before delimiters')
696 assert !issue.description.match(/^---$/)
711 assert !issue.description.include?('BREAK')
697 assert !issue.description.include?('This paragraph is after the delimiter')
712 assert !issue.description.include?('This paragraph is between delimiters')
713 assert !issue.description.match(/^---$/)
714 assert !issue.description.include?('This paragraph is after the delimiter')
715 end
716 end
698 end
717 end
699 end
718
700
719 def test_email_with_long_subject_line
701 def test_email_with_long_subject_line
720 issue = submit_email('ticket_with_long_subject.eml')
702 issue = submit_email('ticket_with_long_subject.eml')
721 assert issue.is_a?(Issue)
703 assert issue.is_a?(Issue)
722 assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255]
704 assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255]
723 end
705 end
724
706
725 def test_new_user_from_attributes_should_return_valid_user
707 def test_new_user_from_attributes_should_return_valid_user
726 to_test = {
708 to_test = {
727 # [address, name] => [login, firstname, lastname]
709 # [address, name] => [login, firstname, lastname]
728 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
710 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
729 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
711 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
730 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
712 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
731 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
713 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
732 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
714 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
733 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
715 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
734 }
716 }
735
717
736 to_test.each do |attrs, expected|
718 to_test.each do |attrs, expected|
737 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
719 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
738
720
739 assert user.valid?, user.errors.full_messages.to_s
721 assert user.valid?, user.errors.full_messages.to_s
740 assert_equal attrs.first, user.mail
722 assert_equal attrs.first, user.mail
741 assert_equal expected[0], user.login
723 assert_equal expected[0], user.login
742 assert_equal expected[1], user.firstname
724 assert_equal expected[1], user.firstname
743 assert_equal expected[2], user.lastname
725 assert_equal expected[2], user.lastname
744 end
726 end
745 end
727 end
746
728
747 def test_new_user_from_attributes_should_respect_minimum_password_length
729 def test_new_user_from_attributes_should_respect_minimum_password_length
748 with_settings :password_min_length => 15 do
730 with_settings :password_min_length => 15 do
749 user = MailHandler.new_user_from_attributes('jsmith@example.net')
731 user = MailHandler.new_user_from_attributes('jsmith@example.net')
750 assert user.valid?
732 assert user.valid?
751 assert user.password.length >= 15
733 assert user.password.length >= 15
752 end
734 end
753 end
735 end
754
736
755 def test_new_user_from_attributes_should_use_default_login_if_invalid
737 def test_new_user_from_attributes_should_use_default_login_if_invalid
756 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
738 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
757 assert user.valid?
739 assert user.valid?
758 assert user.login =~ /^user[a-f0-9]+$/
740 assert user.login =~ /^user[a-f0-9]+$/
759 assert_equal 'foo+bar@example.net', user.mail
741 assert_equal 'foo+bar@example.net', user.mail
760 end
742 end
761
743
762 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
744 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
763 assert_difference 'User.count' do
745 assert_difference 'User.count' do
764 issue = submit_email(
746 issue = submit_email(
765 'fullname_of_sender_as_utf8_encoded.eml',
747 'fullname_of_sender_as_utf8_encoded.eml',
766 :issue => {:project => 'ecookbook'},
748 :issue => {:project => 'ecookbook'},
767 :unknown_user => 'create'
749 :unknown_user => 'create'
768 )
750 )
769 end
751 end
770
752
771 user = User.first(:order => 'id DESC')
753 user = User.first(:order => 'id DESC')
772 assert_equal "foo@example.org", user.mail
754 assert_equal "foo@example.org", user.mail
773 str1 = "\xc3\x84\xc3\xa4"
755 str1 = "\xc3\x84\xc3\xa4"
774 str2 = "\xc3\x96\xc3\xb6"
756 str2 = "\xc3\x96\xc3\xb6"
775 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
757 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
776 str2.force_encoding('UTF-8') if str2.respond_to?(:force_encoding)
758 str2.force_encoding('UTF-8') if str2.respond_to?(:force_encoding)
777 assert_equal str1, user.firstname
759 assert_equal str1, user.firstname
778 assert_equal str2, user.lastname
760 assert_equal str2, user.lastname
779 end
761 end
780
762
781 private
763 private
782
764
783 def submit_email(filename, options={})
765 def submit_email(filename, options={})
784 raw = IO.read(File.join(FIXTURES_PATH, filename))
766 raw = IO.read(File.join(FIXTURES_PATH, filename))
785 yield raw if block_given?
767 yield raw if block_given?
786 MailHandler.receive(raw, options)
768 MailHandler.receive(raw, options)
787 end
769 end
788
770
789 def assert_issue_created(issue)
771 def assert_issue_created(issue)
790 assert issue.is_a?(Issue)
772 assert issue.is_a?(Issue)
791 assert !issue.new_record?
773 assert !issue.new_record?
792 issue.reload
774 issue.reload
793 end
775 end
794 end
776 end
@@ -1,618 +1,614
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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 MailerTest < ActiveSupport::TestCase
20 class MailerTest < ActiveSupport::TestCase
21 include Redmine::I18n
21 include Redmine::I18n
22 include ActionDispatch::Assertions::SelectorAssertions
22 include ActionDispatch::Assertions::SelectorAssertions
23 fixtures :projects, :enabled_modules, :issues, :users, :members,
23 fixtures :projects, :enabled_modules, :issues, :users, :members,
24 :member_roles, :roles, :documents, :attachments, :news,
24 :member_roles, :roles, :documents, :attachments, :news,
25 :tokens, :journals, :journal_details, :changesets,
25 :tokens, :journals, :journal_details, :changesets,
26 :trackers, :projects_trackers,
26 :trackers, :projects_trackers,
27 :issue_statuses, :enumerations, :messages, :boards, :repositories,
27 :issue_statuses, :enumerations, :messages, :boards, :repositories,
28 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
28 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
29 :versions,
29 :versions,
30 :comments
30 :comments
31
31
32 def setup
32 def setup
33 ActionMailer::Base.deliveries.clear
33 ActionMailer::Base.deliveries.clear
34 Setting.host_name = 'mydomain.foo'
34 Setting.host_name = 'mydomain.foo'
35 Setting.protocol = 'http'
35 Setting.protocol = 'http'
36 Setting.plain_text_mail = '0'
36 Setting.plain_text_mail = '0'
37 end
37 end
38
38
39 def test_generated_links_in_emails
39 def test_generated_links_in_emails
40 Setting.default_language = 'en'
40 Setting.default_language = 'en'
41 Setting.host_name = 'mydomain.foo'
41 Setting.host_name = 'mydomain.foo'
42 Setting.protocol = 'https'
42 Setting.protocol = 'https'
43
43
44 journal = Journal.find(3)
44 journal = Journal.find(3)
45 assert Mailer.issue_edit(journal).deliver
45 assert Mailer.issue_edit(journal).deliver
46
46
47 mail = last_email
47 mail = last_email
48 assert_not_nil mail
48 assert_not_nil mail
49
49
50 assert_select_email do
50 assert_select_email do
51 # link to the main ticket
51 # link to the main ticket
52 assert_select 'a[href=?]',
52 assert_select 'a[href=?]',
53 'https://mydomain.foo/issues/2#change-3',
53 'https://mydomain.foo/issues/2#change-3',
54 :text => 'Feature request #2: Add ingredients categories'
54 :text => 'Feature request #2: Add ingredients categories'
55 # link to a referenced ticket
55 # link to a referenced ticket
56 assert_select 'a[href=?][title=?]',
56 assert_select 'a[href=?][title=?]',
57 'https://mydomain.foo/issues/1',
57 'https://mydomain.foo/issues/1',
58 'Can&#x27;t print recipes (New)',
58 'Can&#x27;t print recipes (New)',
59 :text => '#1'
59 :text => '#1'
60 # link to a changeset
60 # link to a changeset
61 assert_select 'a[href=?][title=?]',
61 assert_select 'a[href=?][title=?]',
62 'https://mydomain.foo/projects/ecookbook/repository/revisions/2',
62 'https://mydomain.foo/projects/ecookbook/repository/revisions/2',
63 'This commit fixes #1, #2 and references #1 &amp; #3',
63 'This commit fixes #1, #2 and references #1 &amp; #3',
64 :text => 'r2'
64 :text => 'r2'
65 # link to a description diff
65 # link to a description diff
66 assert_select 'a[href=?][title=?]',
66 assert_select 'a[href=?][title=?]',
67 'https://mydomain.foo/journals/diff/3?detail_id=4',
67 'https://mydomain.foo/journals/diff/3?detail_id=4',
68 'View differences',
68 'View differences',
69 :text => 'diff'
69 :text => 'diff'
70 # link to an attachment
70 # link to an attachment
71 assert_select 'a[href=?]',
71 assert_select 'a[href=?]',
72 'https://mydomain.foo/attachments/download/4/source.rb',
72 'https://mydomain.foo/attachments/download/4/source.rb',
73 :text => 'source.rb'
73 :text => 'source.rb'
74 end
74 end
75 end
75 end
76
76
77 def test_generated_links_with_prefix
77 def test_generated_links_with_prefix
78 Setting.default_language = 'en'
78 Setting.default_language = 'en'
79 relative_url_root = Redmine::Utils.relative_url_root
79 relative_url_root = Redmine::Utils.relative_url_root
80 Setting.host_name = 'mydomain.foo/rdm'
80 Setting.host_name = 'mydomain.foo/rdm'
81 Setting.protocol = 'http'
81 Setting.protocol = 'http'
82
82
83 journal = Journal.find(3)
83 journal = Journal.find(3)
84 assert Mailer.issue_edit(journal).deliver
84 assert Mailer.issue_edit(journal).deliver
85
85
86 mail = last_email
86 mail = last_email
87 assert_not_nil mail
87 assert_not_nil mail
88
88
89 assert_select_email do
89 assert_select_email do
90 # link to the main ticket
90 # link to the main ticket
91 assert_select 'a[href=?]',
91 assert_select 'a[href=?]',
92 'http://mydomain.foo/rdm/issues/2#change-3',
92 'http://mydomain.foo/rdm/issues/2#change-3',
93 :text => 'Feature request #2: Add ingredients categories'
93 :text => 'Feature request #2: Add ingredients categories'
94 # link to a referenced ticket
94 # link to a referenced ticket
95 assert_select 'a[href=?][title=?]',
95 assert_select 'a[href=?][title=?]',
96 'http://mydomain.foo/rdm/issues/1',
96 'http://mydomain.foo/rdm/issues/1',
97 'Can&#x27;t print recipes (New)',
97 'Can&#x27;t print recipes (New)',
98 :text => '#1'
98 :text => '#1'
99 # link to a changeset
99 # link to a changeset
100 assert_select 'a[href=?][title=?]',
100 assert_select 'a[href=?][title=?]',
101 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
101 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
102 'This commit fixes #1, #2 and references #1 &amp; #3',
102 'This commit fixes #1, #2 and references #1 &amp; #3',
103 :text => 'r2'
103 :text => 'r2'
104 # link to a description diff
104 # link to a description diff
105 assert_select 'a[href=?][title=?]',
105 assert_select 'a[href=?][title=?]',
106 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
106 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
107 'View differences',
107 'View differences',
108 :text => 'diff'
108 :text => 'diff'
109 # link to an attachment
109 # link to an attachment
110 assert_select 'a[href=?]',
110 assert_select 'a[href=?]',
111 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
111 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
112 :text => 'source.rb'
112 :text => 'source.rb'
113 end
113 end
114 end
114 end
115
115
116 def test_generated_links_with_prefix_and_no_relative_url_root
116 def test_generated_links_with_prefix_and_no_relative_url_root
117 Setting.default_language = 'en'
117 Setting.default_language = 'en'
118 relative_url_root = Redmine::Utils.relative_url_root
118 relative_url_root = Redmine::Utils.relative_url_root
119 Setting.host_name = 'mydomain.foo/rdm'
119 Setting.host_name = 'mydomain.foo/rdm'
120 Setting.protocol = 'http'
120 Setting.protocol = 'http'
121 Redmine::Utils.relative_url_root = nil
121 Redmine::Utils.relative_url_root = nil
122
122
123 journal = Journal.find(3)
123 journal = Journal.find(3)
124 assert Mailer.issue_edit(journal).deliver
124 assert Mailer.issue_edit(journal).deliver
125
125
126 mail = last_email
126 mail = last_email
127 assert_not_nil mail
127 assert_not_nil mail
128
128
129 assert_select_email do
129 assert_select_email do
130 # link to the main ticket
130 # link to the main ticket
131 assert_select 'a[href=?]',
131 assert_select 'a[href=?]',
132 'http://mydomain.foo/rdm/issues/2#change-3',
132 'http://mydomain.foo/rdm/issues/2#change-3',
133 :text => 'Feature request #2: Add ingredients categories'
133 :text => 'Feature request #2: Add ingredients categories'
134 # link to a referenced ticket
134 # link to a referenced ticket
135 assert_select 'a[href=?][title=?]',
135 assert_select 'a[href=?][title=?]',
136 'http://mydomain.foo/rdm/issues/1',
136 'http://mydomain.foo/rdm/issues/1',
137 'Can&#x27;t print recipes (New)',
137 'Can&#x27;t print recipes (New)',
138 :text => '#1'
138 :text => '#1'
139 # link to a changeset
139 # link to a changeset
140 assert_select 'a[href=?][title=?]',
140 assert_select 'a[href=?][title=?]',
141 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
141 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2',
142 'This commit fixes #1, #2 and references #1 &amp; #3',
142 'This commit fixes #1, #2 and references #1 &amp; #3',
143 :text => 'r2'
143 :text => 'r2'
144 # link to a description diff
144 # link to a description diff
145 assert_select 'a[href=?][title=?]',
145 assert_select 'a[href=?][title=?]',
146 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
146 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4',
147 'View differences',
147 'View differences',
148 :text => 'diff'
148 :text => 'diff'
149 # link to an attachment
149 # link to an attachment
150 assert_select 'a[href=?]',
150 assert_select 'a[href=?]',
151 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
151 'http://mydomain.foo/rdm/attachments/download/4/source.rb',
152 :text => 'source.rb'
152 :text => 'source.rb'
153 end
153 end
154 ensure
154 ensure
155 # restore it
155 # restore it
156 Redmine::Utils.relative_url_root = relative_url_root
156 Redmine::Utils.relative_url_root = relative_url_root
157 end
157 end
158
158
159 def test_email_headers
159 def test_email_headers
160 issue = Issue.find(1)
160 issue = Issue.find(1)
161 Mailer.issue_add(issue).deliver
161 Mailer.issue_add(issue).deliver
162 mail = last_email
162 mail = last_email
163 assert_not_nil mail
163 assert_not_nil mail
164 assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s
164 assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s
165 assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s
165 assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s
166 assert_equal '<redmine.example.net>', mail.header['List-Id'].to_s
166 assert_equal '<redmine.example.net>', mail.header['List-Id'].to_s
167 end
167 end
168
168
169 def test_email_headers_should_include_sender
169 def test_email_headers_should_include_sender
170 issue = Issue.find(1)
170 issue = Issue.find(1)
171 Mailer.issue_add(issue).deliver
171 Mailer.issue_add(issue).deliver
172 mail = last_email
172 mail = last_email
173 assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s
173 assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s
174 end
174 end
175
175
176 def test_plain_text_mail
176 def test_plain_text_mail
177 Setting.plain_text_mail = 1
177 Setting.plain_text_mail = 1
178 journal = Journal.find(2)
178 journal = Journal.find(2)
179 Mailer.issue_edit(journal).deliver
179 Mailer.issue_edit(journal).deliver
180 mail = last_email
180 mail = last_email
181 assert_equal "text/plain; charset=UTF-8", mail.content_type
181 assert_equal "text/plain; charset=UTF-8", mail.content_type
182 assert_equal 0, mail.parts.size
182 assert_equal 0, mail.parts.size
183 assert !mail.encoded.include?('href')
183 assert !mail.encoded.include?('href')
184 end
184 end
185
185
186 def test_html_mail
186 def test_html_mail
187 Setting.plain_text_mail = 0
187 Setting.plain_text_mail = 0
188 journal = Journal.find(2)
188 journal = Journal.find(2)
189 Mailer.issue_edit(journal).deliver
189 Mailer.issue_edit(journal).deliver
190 mail = last_email
190 mail = last_email
191 assert_equal 2, mail.parts.size
191 assert_equal 2, mail.parts.size
192 assert mail.encoded.include?('href')
192 assert mail.encoded.include?('href')
193 end
193 end
194
194
195 def test_from_header
195 def test_from_header
196 with_settings :mail_from => 'redmine@example.net' do
196 with_settings :mail_from => 'redmine@example.net' do
197 Mailer.test_email(User.find(1)).deliver
197 Mailer.test_email(User.find(1)).deliver
198 end
198 end
199 mail = last_email
199 mail = last_email
200 assert_equal 'redmine@example.net', mail.from_addrs.first
200 assert_equal 'redmine@example.net', mail.from_addrs.first
201 end
201 end
202
202
203 def test_from_header_with_phrase
203 def test_from_header_with_phrase
204 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
204 with_settings :mail_from => 'Redmine app <redmine@example.net>' do
205 Mailer.test_email(User.find(1)).deliver
205 Mailer.test_email(User.find(1)).deliver
206 end
206 end
207 mail = last_email
207 mail = last_email
208 assert_equal 'redmine@example.net', mail.from_addrs.first
208 assert_equal 'redmine@example.net', mail.from_addrs.first
209 assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s
209 assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s
210 end
210 end
211
211
212 def test_should_not_send_email_without_recipient
212 def test_should_not_send_email_without_recipient
213 news = News.first
213 news = News.first
214 user = news.author
214 user = news.author
215 # Remove members except news author
215 # Remove members except news author
216 news.project.memberships.each {|m| m.destroy unless m.user == user}
216 news.project.memberships.each {|m| m.destroy unless m.user == user}
217
217
218 user.pref[:no_self_notified] = false
218 user.pref[:no_self_notified] = false
219 user.pref.save
219 user.pref.save
220 User.current = user
220 User.current = user
221 Mailer.news_added(news.reload).deliver
221 Mailer.news_added(news.reload).deliver
222 assert_equal 1, last_email.bcc.size
222 assert_equal 1, last_email.bcc.size
223
223
224 # nobody to notify
224 # nobody to notify
225 user.pref[:no_self_notified] = true
225 user.pref[:no_self_notified] = true
226 user.pref.save
226 user.pref.save
227 User.current = user
227 User.current = user
228 ActionMailer::Base.deliveries.clear
228 ActionMailer::Base.deliveries.clear
229 Mailer.news_added(news.reload).deliver
229 Mailer.news_added(news.reload).deliver
230 assert ActionMailer::Base.deliveries.empty?
230 assert ActionMailer::Base.deliveries.empty?
231 end
231 end
232
232
233 def test_issue_add_message_id
233 def test_issue_add_message_id
234 issue = Issue.find(1)
234 issue = Issue.find(1)
235 Mailer.issue_add(issue).deliver
235 Mailer.issue_add(issue).deliver
236 mail = last_email
236 mail = last_email
237 assert_equal Mailer.message_id_for(issue), mail.message_id
237 assert_equal Mailer.message_id_for(issue), mail.message_id
238 assert_nil mail.references
238 assert_nil mail.references
239 end
239 end
240
240
241 def test_issue_edit_message_id
241 def test_issue_edit_message_id
242 journal = Journal.find(1)
242 journal = Journal.find(1)
243 Mailer.issue_edit(journal).deliver
243 Mailer.issue_edit(journal).deliver
244 mail = last_email
244 mail = last_email
245 assert_equal Mailer.message_id_for(journal), mail.message_id
245 assert_equal Mailer.message_id_for(journal), mail.message_id
246 assert_include Mailer.message_id_for(journal.issue), mail.references
246 assert_include Mailer.message_id_for(journal.issue), mail.references
247 assert_select_email do
247 assert_select_email do
248 # link to the update
248 # link to the update
249 assert_select "a[href=?]",
249 assert_select "a[href=?]",
250 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
250 "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}"
251 end
251 end
252 end
252 end
253
253
254 def test_message_posted_message_id
254 def test_message_posted_message_id
255 message = Message.find(1)
255 message = Message.find(1)
256 Mailer.message_posted(message).deliver
256 Mailer.message_posted(message).deliver
257 mail = last_email
257 mail = last_email
258 assert_equal Mailer.message_id_for(message), mail.message_id
258 assert_equal Mailer.message_id_for(message), mail.message_id
259 assert_nil mail.references
259 assert_nil mail.references
260 assert_select_email do
260 assert_select_email do
261 # link to the message
261 # link to the message
262 assert_select "a[href=?]",
262 assert_select "a[href=?]",
263 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
263 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}",
264 :text => message.subject
264 :text => message.subject
265 end
265 end
266 end
266 end
267
267
268 def test_reply_posted_message_id
268 def test_reply_posted_message_id
269 message = Message.find(3)
269 message = Message.find(3)
270 Mailer.message_posted(message).deliver
270 Mailer.message_posted(message).deliver
271 mail = last_email
271 mail = last_email
272 assert_equal Mailer.message_id_for(message), mail.message_id
272 assert_equal Mailer.message_id_for(message), mail.message_id
273 assert_include Mailer.message_id_for(message.parent), mail.references
273 assert_include Mailer.message_id_for(message.parent), mail.references
274 assert_select_email do
274 assert_select_email do
275 # link to the reply
275 # link to the reply
276 assert_select "a[href=?]",
276 assert_select "a[href=?]",
277 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
277 "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}",
278 :text => message.subject
278 :text => message.subject
279 end
279 end
280 end
280 end
281
281
282 context("#issue_add") do
282 test "#issue_add should notify project members" do
283 setup do
283 issue = Issue.find(1)
284 ActionMailer::Base.deliveries.clear
284 assert Mailer.issue_add(issue).deliver
285 Setting.bcc_recipients = '1'
285 assert last_email.bcc.include?('dlopper@somenet.foo')
286 @issue = Issue.find(1)
286 end
287 end
288
287
289 should "notify project members" do
288 test "#issue_add should not notify project members that are not allow to view the issue" do
290 assert Mailer.issue_add(@issue).deliver
289 issue = Issue.find(1)
291 assert last_email.bcc.include?('dlopper@somenet.foo')
290 Role.find(2).remove_permission!(:view_issues)
292 end
291 assert Mailer.issue_add(issue).deliver
292 assert !last_email.bcc.include?('dlopper@somenet.foo')
293 end
293
294
294 should "not notify project members that are not allow to view the issue" do
295 test "#issue_add should notify issue watchers" do
295 Role.find(2).remove_permission!(:view_issues)
296 issue = Issue.find(1)
296 assert Mailer.issue_add(@issue).deliver
297 user = User.find(9)
297 assert !last_email.bcc.include?('dlopper@somenet.foo')
298 # minimal email notification options
298 end
299 user.pref[:no_self_notified] = '1'
300 user.pref.save
301 user.mail_notification = false
302 user.save
299
303
300 should "notify issue watchers" do
304 Watcher.create!(:watchable => issue, :user => user)
301 user = User.find(9)
305 assert Mailer.issue_add(issue).deliver
302 # minimal email notification options
306 assert last_email.bcc.include?(user.mail)
303 user.pref[:no_self_notified] = '1'
307 end
304 user.pref.save
305 user.mail_notification = false
306 user.save
307
308 Watcher.create!(:watchable => @issue, :user => user)
309 assert Mailer.issue_add(@issue).deliver
310 assert last_email.bcc.include?(user.mail)
311 end
312
308
313 should "not notify watchers not allowed to view the issue" do
309 test "#issue_add should not notify watchers not allowed to view the issue" do
314 user = User.find(9)
310 issue = Issue.find(1)
315 Watcher.create!(:watchable => @issue, :user => user)
311 user = User.find(9)
316 Role.non_member.remove_permission!(:view_issues)
312 Watcher.create!(:watchable => issue, :user => user)
317 assert Mailer.issue_add(@issue).deliver
313 Role.non_member.remove_permission!(:view_issues)
318 assert !last_email.bcc.include?(user.mail)
314 assert Mailer.issue_add(issue).deliver
319 end
315 assert !last_email.bcc.include?(user.mail)
320 end
316 end
321
317
322 # test mailer methods for each language
318 # test mailer methods for each language
323 def test_issue_add
319 def test_issue_add
324 issue = Issue.find(1)
320 issue = Issue.find(1)
325 valid_languages.each do |lang|
321 valid_languages.each do |lang|
326 Setting.default_language = lang.to_s
322 Setting.default_language = lang.to_s
327 assert Mailer.issue_add(issue).deliver
323 assert Mailer.issue_add(issue).deliver
328 end
324 end
329 end
325 end
330
326
331 def test_issue_edit
327 def test_issue_edit
332 journal = Journal.find(1)
328 journal = Journal.find(1)
333 valid_languages.each do |lang|
329 valid_languages.each do |lang|
334 Setting.default_language = lang.to_s
330 Setting.default_language = lang.to_s
335 assert Mailer.issue_edit(journal).deliver
331 assert Mailer.issue_edit(journal).deliver
336 end
332 end
337 end
333 end
338
334
339 def test_issue_edit_should_send_private_notes_to_users_with_permission_only
335 def test_issue_edit_should_send_private_notes_to_users_with_permission_only
340 journal = Journal.find(1)
336 journal = Journal.find(1)
341 journal.private_notes = true
337 journal.private_notes = true
342 journal.save!
338 journal.save!
343
339
344 Role.find(2).add_permission! :view_private_notes
340 Role.find(2).add_permission! :view_private_notes
345 Mailer.issue_edit(journal).deliver
341 Mailer.issue_edit(journal).deliver
346 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
342 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
347
343
348 Role.find(2).remove_permission! :view_private_notes
344 Role.find(2).remove_permission! :view_private_notes
349 Mailer.issue_edit(journal).deliver
345 Mailer.issue_edit(journal).deliver
350 assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
346 assert_equal %w(jsmith@somenet.foo), ActionMailer::Base.deliveries.last.bcc.sort
351 end
347 end
352
348
353 def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
349 def test_issue_edit_should_send_private_notes_to_watchers_with_permission_only
354 Issue.find(1).set_watcher(User.find_by_login('someone'))
350 Issue.find(1).set_watcher(User.find_by_login('someone'))
355 journal = Journal.find(1)
351 journal = Journal.find(1)
356 journal.private_notes = true
352 journal.private_notes = true
357 journal.save!
353 journal.save!
358
354
359 Role.non_member.add_permission! :view_private_notes
355 Role.non_member.add_permission! :view_private_notes
360 Mailer.issue_edit(journal).deliver
356 Mailer.issue_edit(journal).deliver
361 assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
357 assert_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
362
358
363 Role.non_member.remove_permission! :view_private_notes
359 Role.non_member.remove_permission! :view_private_notes
364 Mailer.issue_edit(journal).deliver
360 Mailer.issue_edit(journal).deliver
365 assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
361 assert_not_include 'someone@foo.bar', ActionMailer::Base.deliveries.last.bcc.sort
366 end
362 end
367
363
368 def test_document_added
364 def test_document_added
369 document = Document.find(1)
365 document = Document.find(1)
370 valid_languages.each do |lang|
366 valid_languages.each do |lang|
371 Setting.default_language = lang.to_s
367 Setting.default_language = lang.to_s
372 assert Mailer.document_added(document).deliver
368 assert Mailer.document_added(document).deliver
373 end
369 end
374 end
370 end
375
371
376 def test_attachments_added
372 def test_attachments_added
377 attachements = [ Attachment.find_by_container_type('Document') ]
373 attachements = [ Attachment.find_by_container_type('Document') ]
378 valid_languages.each do |lang|
374 valid_languages.each do |lang|
379 Setting.default_language = lang.to_s
375 Setting.default_language = lang.to_s
380 assert Mailer.attachments_added(attachements).deliver
376 assert Mailer.attachments_added(attachements).deliver
381 end
377 end
382 end
378 end
383
379
384 def test_version_file_added
380 def test_version_file_added
385 attachements = [ Attachment.find_by_container_type('Version') ]
381 attachements = [ Attachment.find_by_container_type('Version') ]
386 assert Mailer.attachments_added(attachements).deliver
382 assert Mailer.attachments_added(attachements).deliver
387 assert_not_nil last_email.bcc
383 assert_not_nil last_email.bcc
388 assert last_email.bcc.any?
384 assert last_email.bcc.any?
389 assert_select_email do
385 assert_select_email do
390 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
386 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
391 end
387 end
392 end
388 end
393
389
394 def test_project_file_added
390 def test_project_file_added
395 attachements = [ Attachment.find_by_container_type('Project') ]
391 attachements = [ Attachment.find_by_container_type('Project') ]
396 assert Mailer.attachments_added(attachements).deliver
392 assert Mailer.attachments_added(attachements).deliver
397 assert_not_nil last_email.bcc
393 assert_not_nil last_email.bcc
398 assert last_email.bcc.any?
394 assert last_email.bcc.any?
399 assert_select_email do
395 assert_select_email do
400 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
396 assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files"
401 end
397 end
402 end
398 end
403
399
404 def test_news_added
400 def test_news_added
405 news = News.first
401 news = News.first
406 valid_languages.each do |lang|
402 valid_languages.each do |lang|
407 Setting.default_language = lang.to_s
403 Setting.default_language = lang.to_s
408 assert Mailer.news_added(news).deliver
404 assert Mailer.news_added(news).deliver
409 end
405 end
410 end
406 end
411
407
412 def test_news_comment_added
408 def test_news_comment_added
413 comment = Comment.find(2)
409 comment = Comment.find(2)
414 valid_languages.each do |lang|
410 valid_languages.each do |lang|
415 Setting.default_language = lang.to_s
411 Setting.default_language = lang.to_s
416 assert Mailer.news_comment_added(comment).deliver
412 assert Mailer.news_comment_added(comment).deliver
417 end
413 end
418 end
414 end
419
415
420 def test_message_posted
416 def test_message_posted
421 message = Message.first
417 message = Message.first
422 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
418 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
423 recipients = recipients.compact.uniq
419 recipients = recipients.compact.uniq
424 valid_languages.each do |lang|
420 valid_languages.each do |lang|
425 Setting.default_language = lang.to_s
421 Setting.default_language = lang.to_s
426 assert Mailer.message_posted(message).deliver
422 assert Mailer.message_posted(message).deliver
427 end
423 end
428 end
424 end
429
425
430 def test_wiki_content_added
426 def test_wiki_content_added
431 content = WikiContent.find(1)
427 content = WikiContent.find(1)
432 valid_languages.each do |lang|
428 valid_languages.each do |lang|
433 Setting.default_language = lang.to_s
429 Setting.default_language = lang.to_s
434 assert_difference 'ActionMailer::Base.deliveries.size' do
430 assert_difference 'ActionMailer::Base.deliveries.size' do
435 assert Mailer.wiki_content_added(content).deliver
431 assert Mailer.wiki_content_added(content).deliver
436 assert_select_email do
432 assert_select_email do
437 assert_select 'a[href=?]',
433 assert_select 'a[href=?]',
438 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
434 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
439 :text => 'CookBook documentation'
435 :text => 'CookBook documentation'
440 end
436 end
441 end
437 end
442 end
438 end
443 end
439 end
444
440
445 def test_wiki_content_updated
441 def test_wiki_content_updated
446 content = WikiContent.find(1)
442 content = WikiContent.find(1)
447 valid_languages.each do |lang|
443 valid_languages.each do |lang|
448 Setting.default_language = lang.to_s
444 Setting.default_language = lang.to_s
449 assert_difference 'ActionMailer::Base.deliveries.size' do
445 assert_difference 'ActionMailer::Base.deliveries.size' do
450 assert Mailer.wiki_content_updated(content).deliver
446 assert Mailer.wiki_content_updated(content).deliver
451 assert_select_email do
447 assert_select_email do
452 assert_select 'a[href=?]',
448 assert_select 'a[href=?]',
453 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
449 'http://mydomain.foo/projects/ecookbook/wiki/CookBook_documentation',
454 :text => 'CookBook documentation'
450 :text => 'CookBook documentation'
455 end
451 end
456 end
452 end
457 end
453 end
458 end
454 end
459
455
460 def test_account_information
456 def test_account_information
461 user = User.find(2)
457 user = User.find(2)
462 valid_languages.each do |lang|
458 valid_languages.each do |lang|
463 user.update_attribute :language, lang.to_s
459 user.update_attribute :language, lang.to_s
464 user.reload
460 user.reload
465 assert Mailer.account_information(user, 'pAsswORd').deliver
461 assert Mailer.account_information(user, 'pAsswORd').deliver
466 end
462 end
467 end
463 end
468
464
469 def test_lost_password
465 def test_lost_password
470 token = Token.find(2)
466 token = Token.find(2)
471 valid_languages.each do |lang|
467 valid_languages.each do |lang|
472 token.user.update_attribute :language, lang.to_s
468 token.user.update_attribute :language, lang.to_s
473 token.reload
469 token.reload
474 assert Mailer.lost_password(token).deliver
470 assert Mailer.lost_password(token).deliver
475 end
471 end
476 end
472 end
477
473
478 def test_register
474 def test_register
479 token = Token.find(1)
475 token = Token.find(1)
480 Setting.host_name = 'redmine.foo'
476 Setting.host_name = 'redmine.foo'
481 Setting.protocol = 'https'
477 Setting.protocol = 'https'
482
478
483 valid_languages.each do |lang|
479 valid_languages.each do |lang|
484 token.user.update_attribute :language, lang.to_s
480 token.user.update_attribute :language, lang.to_s
485 token.reload
481 token.reload
486 ActionMailer::Base.deliveries.clear
482 ActionMailer::Base.deliveries.clear
487 assert Mailer.register(token).deliver
483 assert Mailer.register(token).deliver
488 mail = last_email
484 mail = last_email
489 assert_select_email do
485 assert_select_email do
490 assert_select "a[href=?]",
486 assert_select "a[href=?]",
491 "https://redmine.foo/account/activate?token=#{token.value}",
487 "https://redmine.foo/account/activate?token=#{token.value}",
492 :text => "https://redmine.foo/account/activate?token=#{token.value}"
488 :text => "https://redmine.foo/account/activate?token=#{token.value}"
493 end
489 end
494 end
490 end
495 end
491 end
496
492
497 def test_test
493 def test_test
498 user = User.find(1)
494 user = User.find(1)
499 valid_languages.each do |lang|
495 valid_languages.each do |lang|
500 user.update_attribute :language, lang.to_s
496 user.update_attribute :language, lang.to_s
501 assert Mailer.test_email(user).deliver
497 assert Mailer.test_email(user).deliver
502 end
498 end
503 end
499 end
504
500
505 def test_reminders
501 def test_reminders
506 Mailer.reminders(:days => 42)
502 Mailer.reminders(:days => 42)
507 assert_equal 1, ActionMailer::Base.deliveries.size
503 assert_equal 1, ActionMailer::Base.deliveries.size
508 mail = last_email
504 mail = last_email
509 assert mail.bcc.include?('dlopper@somenet.foo')
505 assert mail.bcc.include?('dlopper@somenet.foo')
510 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
506 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
511 assert_equal '1 issue(s) due in the next 42 days', mail.subject
507 assert_equal '1 issue(s) due in the next 42 days', mail.subject
512 end
508 end
513
509
514 def test_reminders_should_not_include_closed_issues
510 def test_reminders_should_not_include_closed_issues
515 with_settings :default_language => 'en' do
511 with_settings :default_language => 'en' do
516 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5,
512 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5,
517 :subject => 'Closed issue', :assigned_to_id => 3,
513 :subject => 'Closed issue', :assigned_to_id => 3,
518 :due_date => 5.days.from_now,
514 :due_date => 5.days.from_now,
519 :author_id => 2)
515 :author_id => 2)
520 ActionMailer::Base.deliveries.clear
516 ActionMailer::Base.deliveries.clear
521
517
522 Mailer.reminders(:days => 42)
518 Mailer.reminders(:days => 42)
523 assert_equal 1, ActionMailer::Base.deliveries.size
519 assert_equal 1, ActionMailer::Base.deliveries.size
524 mail = last_email
520 mail = last_email
525 assert mail.bcc.include?('dlopper@somenet.foo')
521 assert mail.bcc.include?('dlopper@somenet.foo')
526 assert_mail_body_no_match 'Closed issue', mail
522 assert_mail_body_no_match 'Closed issue', mail
527 end
523 end
528 end
524 end
529
525
530 def test_reminders_for_users
526 def test_reminders_for_users
531 Mailer.reminders(:days => 42, :users => ['5'])
527 Mailer.reminders(:days => 42, :users => ['5'])
532 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
528 assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper
533 Mailer.reminders(:days => 42, :users => ['3'])
529 Mailer.reminders(:days => 42, :users => ['3'])
534 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
530 assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper
535 mail = last_email
531 mail = last_email
536 assert mail.bcc.include?('dlopper@somenet.foo')
532 assert mail.bcc.include?('dlopper@somenet.foo')
537 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
533 assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail
538 end
534 end
539
535
540 def test_reminder_should_include_issues_assigned_to_groups
536 def test_reminder_should_include_issues_assigned_to_groups
541 with_settings :default_language => 'en' do
537 with_settings :default_language => 'en' do
542 group = Group.generate!
538 group = Group.generate!
543 group.users << User.find(2)
539 group.users << User.find(2)
544 group.users << User.find(3)
540 group.users << User.find(3)
545
541
546 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
542 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
547 :subject => 'Assigned to group', :assigned_to => group,
543 :subject => 'Assigned to group', :assigned_to => group,
548 :due_date => 5.days.from_now,
544 :due_date => 5.days.from_now,
549 :author_id => 2)
545 :author_id => 2)
550 ActionMailer::Base.deliveries.clear
546 ActionMailer::Base.deliveries.clear
551
547
552 Mailer.reminders(:days => 7)
548 Mailer.reminders(:days => 7)
553 assert_equal 2, ActionMailer::Base.deliveries.size
549 assert_equal 2, ActionMailer::Base.deliveries.size
554 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
550 assert_equal %w(dlopper@somenet.foo jsmith@somenet.foo), ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
555 ActionMailer::Base.deliveries.each do |mail|
551 ActionMailer::Base.deliveries.each do |mail|
556 assert_mail_body_match 'Assigned to group', mail
552 assert_mail_body_match 'Assigned to group', mail
557 end
553 end
558 end
554 end
559 end
555 end
560
556
561 def test_mailer_should_not_change_locale
557 def test_mailer_should_not_change_locale
562 Setting.default_language = 'en'
558 Setting.default_language = 'en'
563 # Set current language to italian
559 # Set current language to italian
564 set_language_if_valid 'it'
560 set_language_if_valid 'it'
565 # Send an email to a french user
561 # Send an email to a french user
566 user = User.find(1)
562 user = User.find(1)
567 user.language = 'fr'
563 user.language = 'fr'
568 Mailer.account_activated(user).deliver
564 Mailer.account_activated(user).deliver
569 mail = last_email
565 mail = last_email
570 assert_mail_body_match 'Votre compte', mail
566 assert_mail_body_match 'Votre compte', mail
571
567
572 assert_equal :it, current_language
568 assert_equal :it, current_language
573 end
569 end
574
570
575 def test_with_deliveries_off
571 def test_with_deliveries_off
576 Mailer.with_deliveries false do
572 Mailer.with_deliveries false do
577 Mailer.test_email(User.find(1)).deliver
573 Mailer.test_email(User.find(1)).deliver
578 end
574 end
579 assert ActionMailer::Base.deliveries.empty?
575 assert ActionMailer::Base.deliveries.empty?
580 # should restore perform_deliveries
576 # should restore perform_deliveries
581 assert ActionMailer::Base.perform_deliveries
577 assert ActionMailer::Base.perform_deliveries
582 end
578 end
583
579
584 def test_layout_should_include_the_emails_header
580 def test_layout_should_include_the_emails_header
585 with_settings :emails_header => "*Header content*" do
581 with_settings :emails_header => "*Header content*" do
586 assert Mailer.test_email(User.find(1)).deliver
582 assert Mailer.test_email(User.find(1)).deliver
587 assert_select_email do
583 assert_select_email do
588 assert_select ".header" do
584 assert_select ".header" do
589 assert_select "strong", :text => "Header content"
585 assert_select "strong", :text => "Header content"
590 end
586 end
591 end
587 end
592 end
588 end
593 end
589 end
594
590
595 def test_should_escape_html_templates_only
591 def test_should_escape_html_templates_only
596 Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Subject with a <tag>')
592 Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Subject with a <tag>')
597 mail = last_email
593 mail = last_email
598 assert_equal 2, mail.parts.size
594 assert_equal 2, mail.parts.size
599 assert_include '<tag>', text_part.body.encoded
595 assert_include '<tag>', text_part.body.encoded
600 assert_include '&lt;tag&gt;', html_part.body.encoded
596 assert_include '&lt;tag&gt;', html_part.body.encoded
601 end
597 end
602
598
603 private
599 private
604
600
605 def last_email
601 def last_email
606 mail = ActionMailer::Base.deliveries.last
602 mail = ActionMailer::Base.deliveries.last
607 assert_not_nil mail
603 assert_not_nil mail
608 mail
604 mail
609 end
605 end
610
606
611 def text_part
607 def text_part
612 last_email.parts.detect {|part| part.content_type.include?('text/plain')}
608 last_email.parts.detect {|part| part.content_type.include?('text/plain')}
613 end
609 end
614
610
615 def html_part
611 def html_part
616 last_email.parts.detect {|part| part.content_type.include?('text/html')}
612 last_email.parts.detect {|part| part.content_type.include?('text/html')}
617 end
613 end
618 end
614 end
@@ -1,132 +1,114
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class PrincipalTest < ActiveSupport::TestCase
22 class PrincipalTest < ActiveSupport::TestCase
23 fixtures :users, :projects, :members, :member_roles
23 fixtures :users, :projects, :members, :member_roles
24
24
25 def test_active_scope_should_return_groups_and_active_users
25 def test_active_scope_should_return_groups_and_active_users
26 result = Principal.active.all
26 result = Principal.active.all
27 assert_include Group.first, result
27 assert_include Group.first, result
28 assert_not_nil result.detect {|p| p.is_a?(User)}
28 assert_not_nil result.detect {|p| p.is_a?(User)}
29 assert_nil result.detect {|p| p.is_a?(User) && !p.active?}
29 assert_nil result.detect {|p| p.is_a?(User) && !p.active?}
30 assert_nil result.detect {|p| p.is_a?(AnonymousUser)}
30 assert_nil result.detect {|p| p.is_a?(AnonymousUser)}
31 end
31 end
32
32
33 def test_member_of_scope_should_return_the_union_of_all_members
33 def test_member_of_scope_should_return_the_union_of_all_members
34 projects = Project.find_all_by_id(1, 2)
34 projects = Project.find_all_by_id(1, 2)
35 assert_equal projects.map(&:principals).flatten.sort, Principal.member_of(projects).sort
35 assert_equal projects.map(&:principals).flatten.sort, Principal.member_of(projects).sort
36 end
36 end
37
37
38 def test_member_of_scope_should_be_empty_for_no_projects
38 def test_member_of_scope_should_be_empty_for_no_projects
39 assert_equal [], Principal.member_of([]).sort
39 assert_equal [], Principal.member_of([]).sort
40 end
40 end
41
41
42 def test_not_member_of_scope_should_return_users_that_have_no_memberships
42 def test_not_member_of_scope_should_return_users_that_have_no_memberships
43 projects = Project.find_all_by_id(1, 2)
43 projects = Project.find_all_by_id(1, 2)
44 expected = (Principal.all - projects.map(&:memberships).flatten.map(&:principal)).sort
44 expected = (Principal.all - projects.map(&:memberships).flatten.map(&:principal)).sort
45 assert_equal expected, Principal.not_member_of(projects).sort
45 assert_equal expected, Principal.not_member_of(projects).sort
46 end
46 end
47
47
48 def test_not_member_of_scope_should_be_empty_for_no_projects
48 def test_not_member_of_scope_should_be_empty_for_no_projects
49 assert_equal [], Principal.not_member_of([]).sort
49 assert_equal [], Principal.not_member_of([]).sort
50 end
50 end
51
51
52 def test_sorted_scope_should_sort_users_before_groups
52 def test_sorted_scope_should_sort_users_before_groups
53 scope = Principal.where("type <> ?", 'AnonymousUser')
53 scope = Principal.where("type <> ?", 'AnonymousUser')
54 expected_order = scope.all.sort do |a, b|
54 expected_order = scope.all.sort do |a, b|
55 if a.is_a?(User) && b.is_a?(Group)
55 if a.is_a?(User) && b.is_a?(Group)
56 -1
56 -1
57 elsif a.is_a?(Group) && b.is_a?(User)
57 elsif a.is_a?(Group) && b.is_a?(User)
58 1
58 1
59 else
59 else
60 a.name.downcase <=> b.name.downcase
60 a.name.downcase <=> b.name.downcase
61 end
61 end
62 end
62 end
63 assert_equal expected_order.map(&:name).map(&:downcase), scope.sorted.all.map(&:name).map(&:downcase)
63 assert_equal expected_order.map(&:name).map(&:downcase), scope.sorted.all.map(&:name).map(&:downcase)
64 end
64 end
65
65
66 context "#like" do
66 test "like scope should search login" do
67 setup do
67 results = Principal.like('jsmi')
68 Principal.create!(:login => 'login')
69 Principal.create!(:login => 'login2')
70
68
71 Principal.create!(:firstname => 'firstname')
69 assert results.any?
72 Principal.create!(:firstname => 'firstname2')
70 assert results.all? {|u| u.login.match(/jsmi/i) }
73
71 end
74 Principal.create!(:lastname => 'lastname')
75 Principal.create!(:lastname => 'lastname2')
76
77 Principal.create!(:mail => 'mail@example.com')
78 Principal.create!(:mail => 'mail2@example.com')
79
80 @palmer = Principal.create!(:firstname => 'David', :lastname => 'Palmer')
81 end
82
83 should "search login" do
84 results = Principal.like('login')
85
86 assert_equal 2, results.count
87 assert results.all? {|u| u.login.match(/login/) }
88 end
89
72
90 should "search firstname" do
73 test "like scope should search firstname" do
91 results = Principal.like('firstname')
74 results = Principal.like('john')
92
75
93 assert_equal 2, results.count
76 assert results.any?
94 assert results.all? {|u| u.firstname.match(/firstname/) }
77 assert results.all? {|u| u.firstname.match(/john/i) }
95 end
78 end
96
79
97 should "search lastname" do
80 test "like scope should search lastname" do
98 results = Principal.like('lastname')
81 results = Principal.like('smi')
99
82
100 assert_equal 2, results.count
83 assert results.any?
101 assert results.all? {|u| u.lastname.match(/lastname/) }
84 assert results.all? {|u| u.lastname.match(/smi/i) }
102 end
85 end
103
86
104 should "search mail" do
87 test "like scope should search mail" do
105 results = Principal.like('mail')
88 results = Principal.like('somenet')
106
89
107 assert_equal 2, results.count
90 assert results.any?
108 assert results.all? {|u| u.mail.match(/mail/) }
91 assert results.all? {|u| u.mail.match(/somenet/i) }
109 end
92 end
110
93
111 should "search firstname and lastname" do
94 test "like scope should search firstname and lastname" do
112 results = Principal.like('david palm')
95 results = Principal.like('john smi')
113
96
114 assert_equal 1, results.count
97 assert_equal 1, results.count
115 assert_equal @palmer, results.first
98 assert_equal User.find(2), results.first
116 end
99 end
117
100
118 should "search lastname and firstname" do
101 test "like scope should search lastname and firstname" do
119 results = Principal.like('palmer davi')
102 results = Principal.like('smith joh')
120
103
121 assert_equal 1, results.count
104 assert_equal 1, results.count
122 assert_equal @palmer, results.first
105 assert_equal User.find(2), results.first
123 end
124 end
106 end
125
107
126 def test_like_scope_with_cyrillic_name
108 def test_like_scope_with_cyrillic_name
127 user = User.generate!(:firstname => 'Π‘ΠΎΠ±ΠΎΠ»Π΅Π²', :lastname => 'ДСнис')
109 user = User.generate!(:firstname => 'Π‘ΠΎΠ±ΠΎΠ»Π΅Π²', :lastname => 'ДСнис')
128 results = Principal.like('Π‘ΠΎΠ±ΠΎ')
110 results = Principal.like('Π‘ΠΎΠ±ΠΎ')
129 assert_equal 1, results.count
111 assert_equal 1, results.count
130 assert_equal user, results.first
112 assert_equal user, results.first
131 end
113 end
132 end
114 end
@@ -1,1212 +1,1207
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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 :news, :comments,
39 :news, :comments,
40 :documents
40 :documents
41
41
42 def setup
42 def setup
43 @ecookbook = Project.find(1)
43 @ecookbook = Project.find(1)
44 @ecookbook_sub1 = Project.find(3)
44 @ecookbook_sub1 = Project.find(3)
45 set_tmp_attachments_directory
45 set_tmp_attachments_directory
46 User.current = nil
46 User.current = nil
47 end
47 end
48
48
49 def test_truth
49 def test_truth
50 assert_kind_of Project, @ecookbook
50 assert_kind_of Project, @ecookbook
51 assert_equal "eCookbook", @ecookbook.name
51 assert_equal "eCookbook", @ecookbook.name
52 end
52 end
53
53
54 def test_default_attributes
54 def test_default_attributes
55 with_settings :default_projects_public => '1' do
55 with_settings :default_projects_public => '1' do
56 assert_equal true, Project.new.is_public
56 assert_equal true, Project.new.is_public
57 assert_equal false, Project.new(:is_public => false).is_public
57 assert_equal false, Project.new(:is_public => false).is_public
58 end
58 end
59
59
60 with_settings :default_projects_public => '0' do
60 with_settings :default_projects_public => '0' do
61 assert_equal false, Project.new.is_public
61 assert_equal false, Project.new.is_public
62 assert_equal true, Project.new(:is_public => true).is_public
62 assert_equal true, Project.new(:is_public => true).is_public
63 end
63 end
64
64
65 with_settings :sequential_project_identifiers => '1' do
65 with_settings :sequential_project_identifiers => '1' do
66 assert !Project.new.identifier.blank?
66 assert !Project.new.identifier.blank?
67 assert Project.new(:identifier => '').identifier.blank?
67 assert Project.new(:identifier => '').identifier.blank?
68 end
68 end
69
69
70 with_settings :sequential_project_identifiers => '0' do
70 with_settings :sequential_project_identifiers => '0' do
71 assert Project.new.identifier.blank?
71 assert Project.new.identifier.blank?
72 assert !Project.new(:identifier => 'test').blank?
72 assert !Project.new(:identifier => 'test').blank?
73 end
73 end
74
74
75 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
75 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
76 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
76 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
77 end
77 end
78
78
79 assert_equal Tracker.all.sort, Project.new.trackers.sort
79 assert_equal Tracker.all.sort, Project.new.trackers.sort
80 assert_equal Tracker.find(1, 3).sort, Project.new(:tracker_ids => [1, 3]).trackers.sort
80 assert_equal Tracker.find(1, 3).sort, Project.new(:tracker_ids => [1, 3]).trackers.sort
81 end
81 end
82
82
83 def test_update
83 def test_update
84 assert_equal "eCookbook", @ecookbook.name
84 assert_equal "eCookbook", @ecookbook.name
85 @ecookbook.name = "eCook"
85 @ecookbook.name = "eCook"
86 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
86 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
87 @ecookbook.reload
87 @ecookbook.reload
88 assert_equal "eCook", @ecookbook.name
88 assert_equal "eCook", @ecookbook.name
89 end
89 end
90
90
91 def test_validate_identifier
91 def test_validate_identifier
92 to_test = {"abc" => true,
92 to_test = {"abc" => true,
93 "ab12" => true,
93 "ab12" => true,
94 "ab-12" => true,
94 "ab-12" => true,
95 "ab_12" => true,
95 "ab_12" => true,
96 "12" => false,
96 "12" => false,
97 "new" => false}
97 "new" => false}
98
98
99 to_test.each do |identifier, valid|
99 to_test.each do |identifier, valid|
100 p = Project.new
100 p = Project.new
101 p.identifier = identifier
101 p.identifier = identifier
102 p.valid?
102 p.valid?
103 if valid
103 if valid
104 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
104 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
105 else
105 else
106 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
106 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
107 end
107 end
108 end
108 end
109 end
109 end
110
110
111 def test_identifier_should_not_be_frozen_for_a_new_project
111 def test_identifier_should_not_be_frozen_for_a_new_project
112 assert_equal false, Project.new.identifier_frozen?
112 assert_equal false, Project.new.identifier_frozen?
113 end
113 end
114
114
115 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
115 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
116 Project.update_all(["identifier = ''"], "id = 1")
116 Project.update_all(["identifier = ''"], "id = 1")
117
117
118 assert_equal false, Project.find(1).identifier_frozen?
118 assert_equal false, Project.find(1).identifier_frozen?
119 end
119 end
120
120
121 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
121 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
122 assert_equal true, Project.find(1).identifier_frozen?
122 assert_equal true, Project.find(1).identifier_frozen?
123 end
123 end
124
124
125 def test_members_should_be_active_users
125 def test_members_should_be_active_users
126 Project.all.each do |project|
126 Project.all.each do |project|
127 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
127 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
128 end
128 end
129 end
129 end
130
130
131 def test_users_should_be_active_users
131 def test_users_should_be_active_users
132 Project.all.each do |project|
132 Project.all.each do |project|
133 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
133 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
134 end
134 end
135 end
135 end
136
136
137 def test_open_scope_on_issues_association
137 def test_open_scope_on_issues_association
138 assert_kind_of Issue, Project.find(1).issues.open.first
138 assert_kind_of Issue, Project.find(1).issues.open.first
139 end
139 end
140
140
141 def test_archive
141 def test_archive
142 user = @ecookbook.members.first.user
142 user = @ecookbook.members.first.user
143 @ecookbook.archive
143 @ecookbook.archive
144 @ecookbook.reload
144 @ecookbook.reload
145
145
146 assert !@ecookbook.active?
146 assert !@ecookbook.active?
147 assert @ecookbook.archived?
147 assert @ecookbook.archived?
148 assert !user.projects.include?(@ecookbook)
148 assert !user.projects.include?(@ecookbook)
149 # Subproject are also archived
149 # Subproject are also archived
150 assert !@ecookbook.children.empty?
150 assert !@ecookbook.children.empty?
151 assert @ecookbook.descendants.active.empty?
151 assert @ecookbook.descendants.active.empty?
152 end
152 end
153
153
154 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
154 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
155 # Assign an issue of a project to a version of a child project
155 # Assign an issue of a project to a version of a child project
156 Issue.find(4).update_attribute :fixed_version_id, 4
156 Issue.find(4).update_attribute :fixed_version_id, 4
157
157
158 assert_no_difference "Project.count(:all, :conditions => 'status = #{Project::STATUS_ARCHIVED}')" do
158 assert_no_difference "Project.count(:all, :conditions => 'status = #{Project::STATUS_ARCHIVED}')" do
159 assert_equal false, @ecookbook.archive
159 assert_equal false, @ecookbook.archive
160 end
160 end
161 @ecookbook.reload
161 @ecookbook.reload
162 assert @ecookbook.active?
162 assert @ecookbook.active?
163 end
163 end
164
164
165 def test_unarchive
165 def test_unarchive
166 user = @ecookbook.members.first.user
166 user = @ecookbook.members.first.user
167 @ecookbook.archive
167 @ecookbook.archive
168 # A subproject of an archived project can not be unarchived
168 # A subproject of an archived project can not be unarchived
169 assert !@ecookbook_sub1.unarchive
169 assert !@ecookbook_sub1.unarchive
170
170
171 # Unarchive project
171 # Unarchive project
172 assert @ecookbook.unarchive
172 assert @ecookbook.unarchive
173 @ecookbook.reload
173 @ecookbook.reload
174 assert @ecookbook.active?
174 assert @ecookbook.active?
175 assert !@ecookbook.archived?
175 assert !@ecookbook.archived?
176 assert user.projects.include?(@ecookbook)
176 assert user.projects.include?(@ecookbook)
177 # Subproject can now be unarchived
177 # Subproject can now be unarchived
178 @ecookbook_sub1.reload
178 @ecookbook_sub1.reload
179 assert @ecookbook_sub1.unarchive
179 assert @ecookbook_sub1.unarchive
180 end
180 end
181
181
182 def test_destroy
182 def test_destroy
183 # 2 active members
183 # 2 active members
184 assert_equal 2, @ecookbook.members.size
184 assert_equal 2, @ecookbook.members.size
185 # and 1 is locked
185 # and 1 is locked
186 assert_equal 3, Member.where('project_id = ?', @ecookbook.id).all.size
186 assert_equal 3, Member.where('project_id = ?', @ecookbook.id).all.size
187 # some boards
187 # some boards
188 assert @ecookbook.boards.any?
188 assert @ecookbook.boards.any?
189
189
190 @ecookbook.destroy
190 @ecookbook.destroy
191 # make sure that the project non longer exists
191 # make sure that the project non longer exists
192 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
192 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
193 # make sure related data was removed
193 # make sure related data was removed
194 assert_nil Member.first(:conditions => {:project_id => @ecookbook.id})
194 assert_nil Member.first(:conditions => {:project_id => @ecookbook.id})
195 assert_nil Board.first(:conditions => {:project_id => @ecookbook.id})
195 assert_nil Board.first(:conditions => {:project_id => @ecookbook.id})
196 assert_nil Issue.first(:conditions => {:project_id => @ecookbook.id})
196 assert_nil Issue.first(:conditions => {:project_id => @ecookbook.id})
197 end
197 end
198
198
199 def test_destroy_should_destroy_subtasks
199 def test_destroy_should_destroy_subtasks
200 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
200 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
201 issues[0].update_attribute :parent_issue_id, issues[1].id
201 issues[0].update_attribute :parent_issue_id, issues[1].id
202 issues[2].update_attribute :parent_issue_id, issues[1].id
202 issues[2].update_attribute :parent_issue_id, issues[1].id
203 assert_equal 2, issues[1].children.count
203 assert_equal 2, issues[1].children.count
204
204
205 assert_nothing_raised do
205 assert_nothing_raised do
206 Project.find(1).destroy
206 Project.find(1).destroy
207 end
207 end
208 assert Issue.find_all_by_id(issues.map(&:id)).empty?
208 assert Issue.find_all_by_id(issues.map(&:id)).empty?
209 end
209 end
210
210
211 def test_destroying_root_projects_should_clear_data
211 def test_destroying_root_projects_should_clear_data
212 Project.roots.each do |root|
212 Project.roots.each do |root|
213 root.destroy
213 root.destroy
214 end
214 end
215
215
216 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
216 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
217 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
217 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
218 assert_equal 0, MemberRole.count
218 assert_equal 0, MemberRole.count
219 assert_equal 0, Issue.count
219 assert_equal 0, Issue.count
220 assert_equal 0, Journal.count
220 assert_equal 0, Journal.count
221 assert_equal 0, JournalDetail.count
221 assert_equal 0, JournalDetail.count
222 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
222 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
223 assert_equal 0, EnabledModule.count
223 assert_equal 0, EnabledModule.count
224 assert_equal 0, IssueCategory.count
224 assert_equal 0, IssueCategory.count
225 assert_equal 0, IssueRelation.count
225 assert_equal 0, IssueRelation.count
226 assert_equal 0, Board.count
226 assert_equal 0, Board.count
227 assert_equal 0, Message.count
227 assert_equal 0, Message.count
228 assert_equal 0, News.count
228 assert_equal 0, News.count
229 assert_equal 0, Query.count(:conditions => "project_id IS NOT NULL")
229 assert_equal 0, Query.count(:conditions => "project_id IS NOT NULL")
230 assert_equal 0, Repository.count
230 assert_equal 0, Repository.count
231 assert_equal 0, Changeset.count
231 assert_equal 0, Changeset.count
232 assert_equal 0, Change.count
232 assert_equal 0, Change.count
233 assert_equal 0, Comment.count
233 assert_equal 0, Comment.count
234 assert_equal 0, TimeEntry.count
234 assert_equal 0, TimeEntry.count
235 assert_equal 0, Version.count
235 assert_equal 0, Version.count
236 assert_equal 0, Watcher.count
236 assert_equal 0, Watcher.count
237 assert_equal 0, Wiki.count
237 assert_equal 0, Wiki.count
238 assert_equal 0, WikiPage.count
238 assert_equal 0, WikiPage.count
239 assert_equal 0, WikiContent.count
239 assert_equal 0, WikiContent.count
240 assert_equal 0, WikiContent::Version.count
240 assert_equal 0, WikiContent::Version.count
241 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").size
241 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").size
242 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").size
242 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").size
243 assert_equal 0, CustomValue.count(:conditions => {:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']})
243 assert_equal 0, CustomValue.count(:conditions => {:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']})
244 end
244 end
245
245
246 def test_move_an_orphan_project_to_a_root_project
246 def test_move_an_orphan_project_to_a_root_project
247 sub = Project.find(2)
247 sub = Project.find(2)
248 sub.set_parent! @ecookbook
248 sub.set_parent! @ecookbook
249 assert_equal @ecookbook.id, sub.parent.id
249 assert_equal @ecookbook.id, sub.parent.id
250 @ecookbook.reload
250 @ecookbook.reload
251 assert_equal 4, @ecookbook.children.size
251 assert_equal 4, @ecookbook.children.size
252 end
252 end
253
253
254 def test_move_an_orphan_project_to_a_subproject
254 def test_move_an_orphan_project_to_a_subproject
255 sub = Project.find(2)
255 sub = Project.find(2)
256 assert sub.set_parent!(@ecookbook_sub1)
256 assert sub.set_parent!(@ecookbook_sub1)
257 end
257 end
258
258
259 def test_move_a_root_project_to_a_project
259 def test_move_a_root_project_to_a_project
260 sub = @ecookbook
260 sub = @ecookbook
261 assert sub.set_parent!(Project.find(2))
261 assert sub.set_parent!(Project.find(2))
262 end
262 end
263
263
264 def test_should_not_move_a_project_to_its_children
264 def test_should_not_move_a_project_to_its_children
265 sub = @ecookbook
265 sub = @ecookbook
266 assert !(sub.set_parent!(Project.find(3)))
266 assert !(sub.set_parent!(Project.find(3)))
267 end
267 end
268
268
269 def test_set_parent_should_add_roots_in_alphabetical_order
269 def test_set_parent_should_add_roots_in_alphabetical_order
270 ProjectCustomField.delete_all
270 ProjectCustomField.delete_all
271 Project.delete_all
271 Project.delete_all
272 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
272 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
273 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
273 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
274 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
274 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
275 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
275 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
276
276
277 assert_equal 4, Project.count
277 assert_equal 4, Project.count
278 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
278 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
279 end
279 end
280
280
281 def test_set_parent_should_add_children_in_alphabetical_order
281 def test_set_parent_should_add_children_in_alphabetical_order
282 ProjectCustomField.delete_all
282 ProjectCustomField.delete_all
283 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
283 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
284 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
284 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
285 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
285 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
286 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
286 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
287 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
287 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
288
288
289 parent.reload
289 parent.reload
290 assert_equal 4, parent.children.size
290 assert_equal 4, parent.children.size
291 assert_equal parent.children.all.sort_by(&:name), parent.children.all
291 assert_equal parent.children.all.sort_by(&:name), parent.children.all
292 end
292 end
293
293
294 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
294 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
295 # Parent issue with a hierarchy project's fixed version
295 # Parent issue with a hierarchy project's fixed version
296 parent_issue = Issue.find(1)
296 parent_issue = Issue.find(1)
297 parent_issue.update_attribute(:fixed_version_id, 4)
297 parent_issue.update_attribute(:fixed_version_id, 4)
298 parent_issue.reload
298 parent_issue.reload
299 assert_equal 4, parent_issue.fixed_version_id
299 assert_equal 4, parent_issue.fixed_version_id
300
300
301 # Should keep fixed versions for the issues
301 # Should keep fixed versions for the issues
302 issue_with_local_fixed_version = Issue.find(5)
302 issue_with_local_fixed_version = Issue.find(5)
303 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
303 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
304 issue_with_local_fixed_version.reload
304 issue_with_local_fixed_version.reload
305 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
305 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
306
306
307 # Local issue with hierarchy fixed_version
307 # Local issue with hierarchy fixed_version
308 issue_with_hierarchy_fixed_version = Issue.find(13)
308 issue_with_hierarchy_fixed_version = Issue.find(13)
309 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
309 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
310 issue_with_hierarchy_fixed_version.reload
310 issue_with_hierarchy_fixed_version.reload
311 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
311 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
312
312
313 # Move project out of the issue's hierarchy
313 # Move project out of the issue's hierarchy
314 moved_project = Project.find(3)
314 moved_project = Project.find(3)
315 moved_project.set_parent!(Project.find(2))
315 moved_project.set_parent!(Project.find(2))
316 parent_issue.reload
316 parent_issue.reload
317 issue_with_local_fixed_version.reload
317 issue_with_local_fixed_version.reload
318 issue_with_hierarchy_fixed_version.reload
318 issue_with_hierarchy_fixed_version.reload
319
319
320 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
320 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
321 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"
321 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"
322 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
322 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
323 end
323 end
324
324
325 def test_parent
325 def test_parent
326 p = Project.find(6).parent
326 p = Project.find(6).parent
327 assert p.is_a?(Project)
327 assert p.is_a?(Project)
328 assert_equal 5, p.id
328 assert_equal 5, p.id
329 end
329 end
330
330
331 def test_ancestors
331 def test_ancestors
332 a = Project.find(6).ancestors
332 a = Project.find(6).ancestors
333 assert a.first.is_a?(Project)
333 assert a.first.is_a?(Project)
334 assert_equal [1, 5], a.collect(&:id)
334 assert_equal [1, 5], a.collect(&:id)
335 end
335 end
336
336
337 def test_root
337 def test_root
338 r = Project.find(6).root
338 r = Project.find(6).root
339 assert r.is_a?(Project)
339 assert r.is_a?(Project)
340 assert_equal 1, r.id
340 assert_equal 1, r.id
341 end
341 end
342
342
343 def test_children
343 def test_children
344 c = Project.find(1).children
344 c = Project.find(1).children
345 assert c.first.is_a?(Project)
345 assert c.first.is_a?(Project)
346 assert_equal [5, 3, 4], c.collect(&:id)
346 assert_equal [5, 3, 4], c.collect(&:id)
347 end
347 end
348
348
349 def test_descendants
349 def test_descendants
350 d = Project.find(1).descendants
350 d = Project.find(1).descendants
351 assert d.first.is_a?(Project)
351 assert d.first.is_a?(Project)
352 assert_equal [5, 6, 3, 4], d.collect(&:id)
352 assert_equal [5, 6, 3, 4], d.collect(&:id)
353 end
353 end
354
354
355 def test_allowed_parents_should_be_empty_for_non_member_user
355 def test_allowed_parents_should_be_empty_for_non_member_user
356 Role.non_member.add_permission!(:add_project)
356 Role.non_member.add_permission!(:add_project)
357 user = User.find(9)
357 user = User.find(9)
358 assert user.memberships.empty?
358 assert user.memberships.empty?
359 User.current = user
359 User.current = user
360 assert Project.new.allowed_parents.compact.empty?
360 assert Project.new.allowed_parents.compact.empty?
361 end
361 end
362
362
363 def test_allowed_parents_with_add_subprojects_permission
363 def test_allowed_parents_with_add_subprojects_permission
364 Role.find(1).remove_permission!(:add_project)
364 Role.find(1).remove_permission!(:add_project)
365 Role.find(1).add_permission!(:add_subprojects)
365 Role.find(1).add_permission!(:add_subprojects)
366 User.current = User.find(2)
366 User.current = User.find(2)
367 # new project
367 # new project
368 assert !Project.new.allowed_parents.include?(nil)
368 assert !Project.new.allowed_parents.include?(nil)
369 assert Project.new.allowed_parents.include?(Project.find(1))
369 assert Project.new.allowed_parents.include?(Project.find(1))
370 # existing root project
370 # existing root project
371 assert Project.find(1).allowed_parents.include?(nil)
371 assert Project.find(1).allowed_parents.include?(nil)
372 # existing child
372 # existing child
373 assert Project.find(3).allowed_parents.include?(Project.find(1))
373 assert Project.find(3).allowed_parents.include?(Project.find(1))
374 assert !Project.find(3).allowed_parents.include?(nil)
374 assert !Project.find(3).allowed_parents.include?(nil)
375 end
375 end
376
376
377 def test_allowed_parents_with_add_project_permission
377 def test_allowed_parents_with_add_project_permission
378 Role.find(1).add_permission!(:add_project)
378 Role.find(1).add_permission!(:add_project)
379 Role.find(1).remove_permission!(:add_subprojects)
379 Role.find(1).remove_permission!(:add_subprojects)
380 User.current = User.find(2)
380 User.current = User.find(2)
381 # new project
381 # new project
382 assert Project.new.allowed_parents.include?(nil)
382 assert Project.new.allowed_parents.include?(nil)
383 assert !Project.new.allowed_parents.include?(Project.find(1))
383 assert !Project.new.allowed_parents.include?(Project.find(1))
384 # existing root project
384 # existing root project
385 assert Project.find(1).allowed_parents.include?(nil)
385 assert Project.find(1).allowed_parents.include?(nil)
386 # existing child
386 # existing child
387 assert Project.find(3).allowed_parents.include?(Project.find(1))
387 assert Project.find(3).allowed_parents.include?(Project.find(1))
388 assert Project.find(3).allowed_parents.include?(nil)
388 assert Project.find(3).allowed_parents.include?(nil)
389 end
389 end
390
390
391 def test_allowed_parents_with_add_project_and_subprojects_permission
391 def test_allowed_parents_with_add_project_and_subprojects_permission
392 Role.find(1).add_permission!(:add_project)
392 Role.find(1).add_permission!(:add_project)
393 Role.find(1).add_permission!(:add_subprojects)
393 Role.find(1).add_permission!(:add_subprojects)
394 User.current = User.find(2)
394 User.current = User.find(2)
395 # new project
395 # new project
396 assert Project.new.allowed_parents.include?(nil)
396 assert Project.new.allowed_parents.include?(nil)
397 assert Project.new.allowed_parents.include?(Project.find(1))
397 assert Project.new.allowed_parents.include?(Project.find(1))
398 # existing root project
398 # existing root project
399 assert Project.find(1).allowed_parents.include?(nil)
399 assert Project.find(1).allowed_parents.include?(nil)
400 # existing child
400 # existing child
401 assert Project.find(3).allowed_parents.include?(Project.find(1))
401 assert Project.find(3).allowed_parents.include?(Project.find(1))
402 assert Project.find(3).allowed_parents.include?(nil)
402 assert Project.find(3).allowed_parents.include?(nil)
403 end
403 end
404
404
405 def test_users_by_role
405 def test_users_by_role
406 users_by_role = Project.find(1).users_by_role
406 users_by_role = Project.find(1).users_by_role
407 assert_kind_of Hash, users_by_role
407 assert_kind_of Hash, users_by_role
408 role = Role.find(1)
408 role = Role.find(1)
409 assert_kind_of Array, users_by_role[role]
409 assert_kind_of Array, users_by_role[role]
410 assert users_by_role[role].include?(User.find(2))
410 assert users_by_role[role].include?(User.find(2))
411 end
411 end
412
412
413 def test_rolled_up_trackers
413 def test_rolled_up_trackers
414 parent = Project.find(1)
414 parent = Project.find(1)
415 parent.trackers = Tracker.find([1,2])
415 parent.trackers = Tracker.find([1,2])
416 child = parent.children.find(3)
416 child = parent.children.find(3)
417
417
418 assert_equal [1, 2], parent.tracker_ids
418 assert_equal [1, 2], parent.tracker_ids
419 assert_equal [2, 3], child.trackers.collect(&:id)
419 assert_equal [2, 3], child.trackers.collect(&:id)
420
420
421 assert_kind_of Tracker, parent.rolled_up_trackers.first
421 assert_kind_of Tracker, parent.rolled_up_trackers.first
422 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
422 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
423
423
424 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
424 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
425 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
425 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
426 end
426 end
427
427
428 def test_rolled_up_trackers_should_ignore_archived_subprojects
428 def test_rolled_up_trackers_should_ignore_archived_subprojects
429 parent = Project.find(1)
429 parent = Project.find(1)
430 parent.trackers = Tracker.find([1,2])
430 parent.trackers = Tracker.find([1,2])
431 child = parent.children.find(3)
431 child = parent.children.find(3)
432 child.trackers = Tracker.find([1,3])
432 child.trackers = Tracker.find([1,3])
433 parent.children.each(&:archive)
433 parent.children.each(&:archive)
434
434
435 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
435 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
436 end
436 end
437
437
438 context "#rolled_up_versions" do
438 test "#rolled_up_versions should include the versions for the current project" do
439 setup do
439 project = Project.generate!
440 @project = Project.generate!
440 parent_version_1 = Version.generate!(:project => project)
441 @parent_version_1 = Version.generate!(:project => @project)
441 parent_version_2 = Version.generate!(:project => project)
442 @parent_version_2 = Version.generate!(:project => @project)
442 assert_same_elements [parent_version_1, parent_version_2], project.rolled_up_versions
443 end
443 end
444
444
445 should "include the versions for the current project" do
445 test "#rolled_up_versions should include versions for a subproject" do
446 assert_same_elements [@parent_version_1, @parent_version_2], @project.rolled_up_versions
446 project = Project.generate!
447 end
447 parent_version_1 = Version.generate!(:project => project)
448
448 parent_version_2 = Version.generate!(:project => project)
449 should "include versions for a subproject" do
449 subproject = Project.generate_with_parent!(project)
450 @subproject = Project.generate!
450 subproject_version = Version.generate!(:project => subproject)
451 @subproject.set_parent!(@project)
451
452 @subproject_version = Version.generate!(:project => @subproject)
452 assert_same_elements [
453
453 parent_version_1,
454 assert_same_elements [
454 parent_version_2,
455 @parent_version_1,
455 subproject_version
456 @parent_version_2,
456 ], project.rolled_up_versions
457 @subproject_version
457 end
458 ], @project.rolled_up_versions
458
459 end
459 test "#rolled_up_versions should include versions for a sub-subproject" do
460
460 project = Project.generate!
461 should "include versions for a sub-subproject" do
461 parent_version_1 = Version.generate!(:project => project)
462 @subproject = Project.generate!
462 parent_version_2 = Version.generate!(:project => project)
463 @subproject.set_parent!(@project)
463 subproject = Project.generate_with_parent!(project)
464 @sub_subproject = Project.generate!
464 sub_subproject = Project.generate_with_parent!(subproject)
465 @sub_subproject.set_parent!(@subproject)
465 sub_subproject_version = Version.generate!(:project => sub_subproject)
466 @sub_subproject_version = Version.generate!(:project => @sub_subproject)
466 project.reload
467
468 @project.reload
469
470 assert_same_elements [
471 @parent_version_1,
472 @parent_version_2,
473 @sub_subproject_version
474 ], @project.rolled_up_versions
475 end
476
467
477 should "only check active projects" do
468 assert_same_elements [
478 @subproject = Project.generate!
469 parent_version_1,
479 @subproject.set_parent!(@project)
470 parent_version_2,
480 @subproject_version = Version.generate!(:project => @subproject)
471 sub_subproject_version
481 assert @subproject.archive
472 ], project.rolled_up_versions
473 end
482
474
483 @project.reload
475 test "#rolled_up_versions should only check active projects" do
476 project = Project.generate!
477 parent_version_1 = Version.generate!(:project => project)
478 parent_version_2 = Version.generate!(:project => project)
479 subproject = Project.generate_with_parent!(project)
480 subproject_version = Version.generate!(:project => subproject)
481 assert subproject.archive
482 project.reload
484
483
485 assert !@subproject.active?
484 assert !subproject.active?
486 assert_same_elements [@parent_version_1, @parent_version_2], @project.rolled_up_versions
485 assert_same_elements [parent_version_1, parent_version_2], project.rolled_up_versions
487 end
488 end
486 end
489
487
490 def test_shared_versions_none_sharing
488 def test_shared_versions_none_sharing
491 p = Project.find(5)
489 p = Project.find(5)
492 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
490 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
493 assert p.shared_versions.include?(v)
491 assert p.shared_versions.include?(v)
494 assert !p.children.first.shared_versions.include?(v)
492 assert !p.children.first.shared_versions.include?(v)
495 assert !p.root.shared_versions.include?(v)
493 assert !p.root.shared_versions.include?(v)
496 assert !p.siblings.first.shared_versions.include?(v)
494 assert !p.siblings.first.shared_versions.include?(v)
497 assert !p.root.siblings.first.shared_versions.include?(v)
495 assert !p.root.siblings.first.shared_versions.include?(v)
498 end
496 end
499
497
500 def test_shared_versions_descendants_sharing
498 def test_shared_versions_descendants_sharing
501 p = Project.find(5)
499 p = Project.find(5)
502 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
500 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
503 assert p.shared_versions.include?(v)
501 assert p.shared_versions.include?(v)
504 assert p.children.first.shared_versions.include?(v)
502 assert p.children.first.shared_versions.include?(v)
505 assert !p.root.shared_versions.include?(v)
503 assert !p.root.shared_versions.include?(v)
506 assert !p.siblings.first.shared_versions.include?(v)
504 assert !p.siblings.first.shared_versions.include?(v)
507 assert !p.root.siblings.first.shared_versions.include?(v)
505 assert !p.root.siblings.first.shared_versions.include?(v)
508 end
506 end
509
507
510 def test_shared_versions_hierarchy_sharing
508 def test_shared_versions_hierarchy_sharing
511 p = Project.find(5)
509 p = Project.find(5)
512 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
510 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
513 assert p.shared_versions.include?(v)
511 assert p.shared_versions.include?(v)
514 assert p.children.first.shared_versions.include?(v)
512 assert p.children.first.shared_versions.include?(v)
515 assert p.root.shared_versions.include?(v)
513 assert p.root.shared_versions.include?(v)
516 assert !p.siblings.first.shared_versions.include?(v)
514 assert !p.siblings.first.shared_versions.include?(v)
517 assert !p.root.siblings.first.shared_versions.include?(v)
515 assert !p.root.siblings.first.shared_versions.include?(v)
518 end
516 end
519
517
520 def test_shared_versions_tree_sharing
518 def test_shared_versions_tree_sharing
521 p = Project.find(5)
519 p = Project.find(5)
522 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
520 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
523 assert p.shared_versions.include?(v)
521 assert p.shared_versions.include?(v)
524 assert p.children.first.shared_versions.include?(v)
522 assert p.children.first.shared_versions.include?(v)
525 assert p.root.shared_versions.include?(v)
523 assert p.root.shared_versions.include?(v)
526 assert p.siblings.first.shared_versions.include?(v)
524 assert p.siblings.first.shared_versions.include?(v)
527 assert !p.root.siblings.first.shared_versions.include?(v)
525 assert !p.root.siblings.first.shared_versions.include?(v)
528 end
526 end
529
527
530 def test_shared_versions_system_sharing
528 def test_shared_versions_system_sharing
531 p = Project.find(5)
529 p = Project.find(5)
532 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
530 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
533 assert p.shared_versions.include?(v)
531 assert p.shared_versions.include?(v)
534 assert p.children.first.shared_versions.include?(v)
532 assert p.children.first.shared_versions.include?(v)
535 assert p.root.shared_versions.include?(v)
533 assert p.root.shared_versions.include?(v)
536 assert p.siblings.first.shared_versions.include?(v)
534 assert p.siblings.first.shared_versions.include?(v)
537 assert p.root.siblings.first.shared_versions.include?(v)
535 assert p.root.siblings.first.shared_versions.include?(v)
538 end
536 end
539
537
540 def test_shared_versions
538 def test_shared_versions
541 parent = Project.find(1)
539 parent = Project.find(1)
542 child = parent.children.find(3)
540 child = parent.children.find(3)
543 private_child = parent.children.find(5)
541 private_child = parent.children.find(5)
544
542
545 assert_equal [1,2,3], parent.version_ids.sort
543 assert_equal [1,2,3], parent.version_ids.sort
546 assert_equal [4], child.version_ids
544 assert_equal [4], child.version_ids
547 assert_equal [6], private_child.version_ids
545 assert_equal [6], private_child.version_ids
548 assert_equal [7], Version.find_all_by_sharing('system').collect(&:id)
546 assert_equal [7], Version.find_all_by_sharing('system').collect(&:id)
549
547
550 assert_equal 6, parent.shared_versions.size
548 assert_equal 6, parent.shared_versions.size
551 parent.shared_versions.each do |version|
549 parent.shared_versions.each do |version|
552 assert_kind_of Version, version
550 assert_kind_of Version, version
553 end
551 end
554
552
555 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
553 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
556 end
554 end
557
555
558 def test_shared_versions_should_ignore_archived_subprojects
556 def test_shared_versions_should_ignore_archived_subprojects
559 parent = Project.find(1)
557 parent = Project.find(1)
560 child = parent.children.find(3)
558 child = parent.children.find(3)
561 child.archive
559 child.archive
562 parent.reload
560 parent.reload
563
561
564 assert_equal [1,2,3], parent.version_ids.sort
562 assert_equal [1,2,3], parent.version_ids.sort
565 assert_equal [4], child.version_ids
563 assert_equal [4], child.version_ids
566 assert !parent.shared_versions.collect(&:id).include?(4)
564 assert !parent.shared_versions.collect(&:id).include?(4)
567 end
565 end
568
566
569 def test_shared_versions_visible_to_user
567 def test_shared_versions_visible_to_user
570 user = User.find(3)
568 user = User.find(3)
571 parent = Project.find(1)
569 parent = Project.find(1)
572 child = parent.children.find(5)
570 child = parent.children.find(5)
573
571
574 assert_equal [1,2,3], parent.version_ids.sort
572 assert_equal [1,2,3], parent.version_ids.sort
575 assert_equal [6], child.version_ids
573 assert_equal [6], child.version_ids
576
574
577 versions = parent.shared_versions.visible(user)
575 versions = parent.shared_versions.visible(user)
578
576
579 assert_equal 4, versions.size
577 assert_equal 4, versions.size
580 versions.each do |version|
578 versions.each do |version|
581 assert_kind_of Version, version
579 assert_kind_of Version, version
582 end
580 end
583
581
584 assert !versions.collect(&:id).include?(6)
582 assert !versions.collect(&:id).include?(6)
585 end
583 end
586
584
587 def test_shared_versions_for_new_project_should_include_system_shared_versions
585 def test_shared_versions_for_new_project_should_include_system_shared_versions
588 p = Project.find(5)
586 p = Project.find(5)
589 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
587 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
590
588
591 assert_include v, Project.new.shared_versions
589 assert_include v, Project.new.shared_versions
592 end
590 end
593
591
594 def test_next_identifier
592 def test_next_identifier
595 ProjectCustomField.delete_all
593 ProjectCustomField.delete_all
596 Project.create!(:name => 'last', :identifier => 'p2008040')
594 Project.create!(:name => 'last', :identifier => 'p2008040')
597 assert_equal 'p2008041', Project.next_identifier
595 assert_equal 'p2008041', Project.next_identifier
598 end
596 end
599
597
600 def test_next_identifier_first_project
598 def test_next_identifier_first_project
601 Project.delete_all
599 Project.delete_all
602 assert_nil Project.next_identifier
600 assert_nil Project.next_identifier
603 end
601 end
604
602
605 def test_enabled_module_names
603 def test_enabled_module_names
606 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
604 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
607 project = Project.new
605 project = Project.new
608
606
609 project.enabled_module_names = %w(issue_tracking news)
607 project.enabled_module_names = %w(issue_tracking news)
610 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
608 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
611 end
609 end
612 end
610 end
613
611
614 context "enabled_modules" do
612 test "enabled_modules should define module by names and preserve ids" do
615 setup do
613 @project = Project.find(1)
616 @project = Project.find(1)
614 # Remove one module
617 end
615 modules = @project.enabled_modules.slice(0..-2)
618
616 assert modules.any?
619 should "define module by names and preserve ids" do
617 assert_difference 'EnabledModule.count', -1 do
620 # Remove one module
618 @project.enabled_module_names = modules.collect(&:name)
621 modules = @project.enabled_modules.slice(0..-2)
622 assert modules.any?
623 assert_difference 'EnabledModule.count', -1 do
624 @project.enabled_module_names = modules.collect(&:name)
625 end
626 @project.reload
627 # Ids should be preserved
628 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
629 end
630
631 should "enable a module" do
632 @project.enabled_module_names = []
633 @project.reload
634 assert_equal [], @project.enabled_module_names
635 #with string
636 @project.enable_module!("issue_tracking")
637 assert_equal ["issue_tracking"], @project.enabled_module_names
638 #with symbol
639 @project.enable_module!(:gantt)
640 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
641 #don't add a module twice
642 @project.enable_module!("issue_tracking")
643 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
644 end
645
646 should "disable a module" do
647 #with string
648 assert @project.enabled_module_names.include?("issue_tracking")
649 @project.disable_module!("issue_tracking")
650 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
651 #with symbol
652 assert @project.enabled_module_names.include?("gantt")
653 @project.disable_module!(:gantt)
654 assert ! @project.reload.enabled_module_names.include?("gantt")
655 #with EnabledModule object
656 first_module = @project.enabled_modules.first
657 @project.disable_module!(first_module)
658 assert ! @project.reload.enabled_module_names.include?(first_module.name)
659 end
619 end
620 @project.reload
621 # Ids should be preserved
622 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
623 end
624
625 test "enabled_modules should enable a module" do
626 @project = Project.find(1)
627 @project.enabled_module_names = []
628 @project.reload
629 assert_equal [], @project.enabled_module_names
630 #with string
631 @project.enable_module!("issue_tracking")
632 assert_equal ["issue_tracking"], @project.enabled_module_names
633 #with symbol
634 @project.enable_module!(:gantt)
635 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
636 #don't add a module twice
637 @project.enable_module!("issue_tracking")
638 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
639 end
640
641 test "enabled_modules should disable a module" do
642 @project = Project.find(1)
643 #with string
644 assert @project.enabled_module_names.include?("issue_tracking")
645 @project.disable_module!("issue_tracking")
646 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
647 #with symbol
648 assert @project.enabled_module_names.include?("gantt")
649 @project.disable_module!(:gantt)
650 assert ! @project.reload.enabled_module_names.include?("gantt")
651 #with EnabledModule object
652 first_module = @project.enabled_modules.first
653 @project.disable_module!(first_module)
654 assert ! @project.reload.enabled_module_names.include?(first_module.name)
660 end
655 end
661
656
662 def test_enabled_module_names_should_not_recreate_enabled_modules
657 def test_enabled_module_names_should_not_recreate_enabled_modules
663 project = Project.find(1)
658 project = Project.find(1)
664 # Remove one module
659 # Remove one module
665 modules = project.enabled_modules.slice(0..-2)
660 modules = project.enabled_modules.slice(0..-2)
666 assert modules.any?
661 assert modules.any?
667 assert_difference 'EnabledModule.count', -1 do
662 assert_difference 'EnabledModule.count', -1 do
668 project.enabled_module_names = modules.collect(&:name)
663 project.enabled_module_names = modules.collect(&:name)
669 end
664 end
670 project.reload
665 project.reload
671 # Ids should be preserved
666 # Ids should be preserved
672 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
667 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
673 end
668 end
674
669
675 def test_copy_from_existing_project
670 def test_copy_from_existing_project
676 source_project = Project.find(1)
671 source_project = Project.find(1)
677 copied_project = Project.copy_from(1)
672 copied_project = Project.copy_from(1)
678
673
679 assert copied_project
674 assert copied_project
680 # Cleared attributes
675 # Cleared attributes
681 assert copied_project.id.blank?
676 assert copied_project.id.blank?
682 assert copied_project.name.blank?
677 assert copied_project.name.blank?
683 assert copied_project.identifier.blank?
678 assert copied_project.identifier.blank?
684
679
685 # Duplicated attributes
680 # Duplicated attributes
686 assert_equal source_project.description, copied_project.description
681 assert_equal source_project.description, copied_project.description
687 assert_equal source_project.enabled_modules, copied_project.enabled_modules
682 assert_equal source_project.enabled_modules, copied_project.enabled_modules
688 assert_equal source_project.trackers, copied_project.trackers
683 assert_equal source_project.trackers, copied_project.trackers
689
684
690 # Default attributes
685 # Default attributes
691 assert_equal 1, copied_project.status
686 assert_equal 1, copied_project.status
692 end
687 end
693
688
694 def test_activities_should_use_the_system_activities
689 def test_activities_should_use_the_system_activities
695 project = Project.find(1)
690 project = Project.find(1)
696 assert_equal project.activities, TimeEntryActivity.where(:active => true).all
691 assert_equal project.activities, TimeEntryActivity.where(:active => true).all
697 end
692 end
698
693
699
694
700 def test_activities_should_use_the_project_specific_activities
695 def test_activities_should_use_the_project_specific_activities
701 project = Project.find(1)
696 project = Project.find(1)
702 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
697 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
703 assert overridden_activity.save!
698 assert overridden_activity.save!
704
699
705 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
700 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
706 end
701 end
707
702
708 def test_activities_should_not_include_the_inactive_project_specific_activities
703 def test_activities_should_not_include_the_inactive_project_specific_activities
709 project = Project.find(1)
704 project = Project.find(1)
710 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
705 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
711 assert overridden_activity.save!
706 assert overridden_activity.save!
712
707
713 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
708 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
714 end
709 end
715
710
716 def test_activities_should_not_include_project_specific_activities_from_other_projects
711 def test_activities_should_not_include_project_specific_activities_from_other_projects
717 project = Project.find(1)
712 project = Project.find(1)
718 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
713 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
719 assert overridden_activity.save!
714 assert overridden_activity.save!
720
715
721 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
716 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
722 end
717 end
723
718
724 def test_activities_should_handle_nils
719 def test_activities_should_handle_nils
725 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.first})
720 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.first})
726 TimeEntryActivity.delete_all
721 TimeEntryActivity.delete_all
727
722
728 # No activities
723 # No activities
729 project = Project.find(1)
724 project = Project.find(1)
730 assert project.activities.empty?
725 assert project.activities.empty?
731
726
732 # No system, one overridden
727 # No system, one overridden
733 assert overridden_activity.save!
728 assert overridden_activity.save!
734 project.reload
729 project.reload
735 assert_equal [overridden_activity], project.activities
730 assert_equal [overridden_activity], project.activities
736 end
731 end
737
732
738 def test_activities_should_override_system_activities_with_project_activities
733 def test_activities_should_override_system_activities_with_project_activities
739 project = Project.find(1)
734 project = Project.find(1)
740 parent_activity = TimeEntryActivity.first
735 parent_activity = TimeEntryActivity.first
741 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
736 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
742 assert overridden_activity.save!
737 assert overridden_activity.save!
743
738
744 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
739 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
745 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
740 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
746 end
741 end
747
742
748 def test_activities_should_include_inactive_activities_if_specified
743 def test_activities_should_include_inactive_activities_if_specified
749 project = Project.find(1)
744 project = Project.find(1)
750 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
745 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
751 assert overridden_activity.save!
746 assert overridden_activity.save!
752
747
753 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
748 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
754 end
749 end
755
750
756 test 'activities should not include active System activities if the project has an override that is inactive' do
751 test 'activities should not include active System activities if the project has an override that is inactive' do
757 project = Project.find(1)
752 project = Project.find(1)
758 system_activity = TimeEntryActivity.find_by_name('Design')
753 system_activity = TimeEntryActivity.find_by_name('Design')
759 assert system_activity.active?
754 assert system_activity.active?
760 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
755 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
761 assert overridden_activity.save!
756 assert overridden_activity.save!
762
757
763 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
758 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
764 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
759 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
765 end
760 end
766
761
767 def test_close_completed_versions
762 def test_close_completed_versions
768 Version.update_all("status = 'open'")
763 Version.update_all("status = 'open'")
769 project = Project.find(1)
764 project = Project.find(1)
770 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
765 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
771 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
766 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
772 project.close_completed_versions
767 project.close_completed_versions
773 project.reload
768 project.reload
774 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
769 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
775 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
770 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
776 end
771 end
777
772
778 context "Project#copy" do
773 context "Project#copy" do
779 setup do
774 setup do
780 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
775 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
781 Project.destroy_all :identifier => "copy-test"
776 Project.destroy_all :identifier => "copy-test"
782 @source_project = Project.find(2)
777 @source_project = Project.find(2)
783 @project = Project.new(:name => 'Copy Test', :identifier => 'copy-test')
778 @project = Project.new(:name => 'Copy Test', :identifier => 'copy-test')
784 @project.trackers = @source_project.trackers
779 @project.trackers = @source_project.trackers
785 @project.enabled_module_names = @source_project.enabled_modules.collect(&:name)
780 @project.enabled_module_names = @source_project.enabled_modules.collect(&:name)
786 end
781 end
787
782
788 should "copy issues" do
783 should "copy issues" do
789 @source_project.issues << Issue.generate!(:status => IssueStatus.find_by_name('Closed'),
784 @source_project.issues << Issue.generate!(:status => IssueStatus.find_by_name('Closed'),
790 :subject => "copy issue status",
785 :subject => "copy issue status",
791 :tracker_id => 1,
786 :tracker_id => 1,
792 :assigned_to_id => 2,
787 :assigned_to_id => 2,
793 :project_id => @source_project.id)
788 :project_id => @source_project.id)
794 assert @project.valid?
789 assert @project.valid?
795 assert @project.issues.empty?
790 assert @project.issues.empty?
796 assert @project.copy(@source_project)
791 assert @project.copy(@source_project)
797
792
798 assert_equal @source_project.issues.size, @project.issues.size
793 assert_equal @source_project.issues.size, @project.issues.size
799 @project.issues.each do |issue|
794 @project.issues.each do |issue|
800 assert issue.valid?
795 assert issue.valid?
801 assert ! issue.assigned_to.blank?
796 assert ! issue.assigned_to.blank?
802 assert_equal @project, issue.project
797 assert_equal @project, issue.project
803 end
798 end
804
799
805 copied_issue = @project.issues.first(:conditions => {:subject => "copy issue status"})
800 copied_issue = @project.issues.first(:conditions => {:subject => "copy issue status"})
806 assert copied_issue
801 assert copied_issue
807 assert copied_issue.status
802 assert copied_issue.status
808 assert_equal "Closed", copied_issue.status.name
803 assert_equal "Closed", copied_issue.status.name
809 end
804 end
810
805
811 should "copy issues assigned to a locked version" do
806 should "copy issues assigned to a locked version" do
812 User.current = User.find(1)
807 User.current = User.find(1)
813 assigned_version = Version.generate!(:name => "Assigned Issues")
808 assigned_version = Version.generate!(:name => "Assigned Issues")
814 @source_project.versions << assigned_version
809 @source_project.versions << assigned_version
815 Issue.generate!(:project => @source_project,
810 Issue.generate!(:project => @source_project,
816 :fixed_version_id => assigned_version.id,
811 :fixed_version_id => assigned_version.id,
817 :subject => "copy issues assigned to a locked version")
812 :subject => "copy issues assigned to a locked version")
818 assigned_version.update_attribute :status, 'locked'
813 assigned_version.update_attribute :status, 'locked'
819
814
820 assert @project.copy(@source_project)
815 assert @project.copy(@source_project)
821 @project.reload
816 @project.reload
822 copied_issue = @project.issues.first(:conditions => {:subject => "copy issues assigned to a locked version"})
817 copied_issue = @project.issues.first(:conditions => {:subject => "copy issues assigned to a locked version"})
823
818
824 assert copied_issue
819 assert copied_issue
825 assert copied_issue.fixed_version
820 assert copied_issue.fixed_version
826 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
821 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
827 assert_equal 'locked', copied_issue.fixed_version.status
822 assert_equal 'locked', copied_issue.fixed_version.status
828 end
823 end
829
824
830 should "change the new issues to use the copied version" do
825 should "change the new issues to use the copied version" do
831 User.current = User.find(1)
826 User.current = User.find(1)
832 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open')
827 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open')
833 @source_project.versions << assigned_version
828 @source_project.versions << assigned_version
834 assert_equal 3, @source_project.versions.size
829 assert_equal 3, @source_project.versions.size
835 Issue.generate!(:project => @source_project,
830 Issue.generate!(:project => @source_project,
836 :fixed_version_id => assigned_version.id,
831 :fixed_version_id => assigned_version.id,
837 :subject => "change the new issues to use the copied version")
832 :subject => "change the new issues to use the copied version")
838
833
839 assert @project.copy(@source_project)
834 assert @project.copy(@source_project)
840 @project.reload
835 @project.reload
841 copied_issue = @project.issues.first(:conditions => {:subject => "change the new issues to use the copied version"})
836 copied_issue = @project.issues.first(:conditions => {:subject => "change the new issues to use the copied version"})
842
837
843 assert copied_issue
838 assert copied_issue
844 assert copied_issue.fixed_version
839 assert copied_issue.fixed_version
845 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
840 assert_equal "Assigned Issues", copied_issue.fixed_version.name # Same name
846 assert_not_equal assigned_version.id, copied_issue.fixed_version.id # Different record
841 assert_not_equal assigned_version.id, copied_issue.fixed_version.id # Different record
847 end
842 end
848
843
849 should "keep target shared versions from other project" do
844 should "keep target shared versions from other project" do
850 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open', :project_id => 1, :sharing => 'system')
845 assigned_version = Version.generate!(:name => "Assigned Issues", :status => 'open', :project_id => 1, :sharing => 'system')
851 issue = Issue.generate!(:project => @source_project,
846 issue = Issue.generate!(:project => @source_project,
852 :fixed_version => assigned_version,
847 :fixed_version => assigned_version,
853 :subject => "keep target shared versions")
848 :subject => "keep target shared versions")
854
849
855 assert @project.copy(@source_project)
850 assert @project.copy(@source_project)
856 @project.reload
851 @project.reload
857 copied_issue = @project.issues.first(:conditions => {:subject => "keep target shared versions"})
852 copied_issue = @project.issues.first(:conditions => {:subject => "keep target shared versions"})
858
853
859 assert copied_issue
854 assert copied_issue
860 assert_equal assigned_version, copied_issue.fixed_version
855 assert_equal assigned_version, copied_issue.fixed_version
861 end
856 end
862
857
863 should "copy issue relations" do
858 should "copy issue relations" do
864 Setting.cross_project_issue_relations = '1'
859 Setting.cross_project_issue_relations = '1'
865
860
866 second_issue = Issue.generate!(:status_id => 5,
861 second_issue = Issue.generate!(:status_id => 5,
867 :subject => "copy issue relation",
862 :subject => "copy issue relation",
868 :tracker_id => 1,
863 :tracker_id => 1,
869 :assigned_to_id => 2,
864 :assigned_to_id => 2,
870 :project_id => @source_project.id)
865 :project_id => @source_project.id)
871 source_relation = IssueRelation.create!(:issue_from => Issue.find(4),
866 source_relation = IssueRelation.create!(:issue_from => Issue.find(4),
872 :issue_to => second_issue,
867 :issue_to => second_issue,
873 :relation_type => "relates")
868 :relation_type => "relates")
874 source_relation_cross_project = IssueRelation.create!(:issue_from => Issue.find(1),
869 source_relation_cross_project = IssueRelation.create!(:issue_from => Issue.find(1),
875 :issue_to => second_issue,
870 :issue_to => second_issue,
876 :relation_type => "duplicates")
871 :relation_type => "duplicates")
877
872
878 assert @project.copy(@source_project)
873 assert @project.copy(@source_project)
879 assert_equal @source_project.issues.count, @project.issues.count
874 assert_equal @source_project.issues.count, @project.issues.count
880 copied_issue = @project.issues.find_by_subject("Issue on project 2") # Was #4
875 copied_issue = @project.issues.find_by_subject("Issue on project 2") # Was #4
881 copied_second_issue = @project.issues.find_by_subject("copy issue relation")
876 copied_second_issue = @project.issues.find_by_subject("copy issue relation")
882
877
883 # First issue with a relation on project
878 # First issue with a relation on project
884 assert_equal 1, copied_issue.relations.size, "Relation not copied"
879 assert_equal 1, copied_issue.relations.size, "Relation not copied"
885 copied_relation = copied_issue.relations.first
880 copied_relation = copied_issue.relations.first
886 assert_equal "relates", copied_relation.relation_type
881 assert_equal "relates", copied_relation.relation_type
887 assert_equal copied_second_issue.id, copied_relation.issue_to_id
882 assert_equal copied_second_issue.id, copied_relation.issue_to_id
888 assert_not_equal source_relation.id, copied_relation.id
883 assert_not_equal source_relation.id, copied_relation.id
889
884
890 # Second issue with a cross project relation
885 # Second issue with a cross project relation
891 assert_equal 2, copied_second_issue.relations.size, "Relation not copied"
886 assert_equal 2, copied_second_issue.relations.size, "Relation not copied"
892 copied_relation = copied_second_issue.relations.select {|r| r.relation_type == 'duplicates'}.first
887 copied_relation = copied_second_issue.relations.select {|r| r.relation_type == 'duplicates'}.first
893 assert_equal "duplicates", copied_relation.relation_type
888 assert_equal "duplicates", copied_relation.relation_type
894 assert_equal 1, copied_relation.issue_from_id, "Cross project relation not kept"
889 assert_equal 1, copied_relation.issue_from_id, "Cross project relation not kept"
895 assert_not_equal source_relation_cross_project.id, copied_relation.id
890 assert_not_equal source_relation_cross_project.id, copied_relation.id
896 end
891 end
897
892
898 should "copy issue attachments" do
893 should "copy issue attachments" do
899 issue = Issue.generate!(:subject => "copy with attachment", :tracker_id => 1, :project_id => @source_project.id)
894 issue = Issue.generate!(:subject => "copy with attachment", :tracker_id => 1, :project_id => @source_project.id)
900 Attachment.create!(:container => issue, :file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 1)
895 Attachment.create!(:container => issue, :file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 1)
901 @source_project.issues << issue
896 @source_project.issues << issue
902 assert @project.copy(@source_project)
897 assert @project.copy(@source_project)
903
898
904 copied_issue = @project.issues.first(:conditions => {:subject => "copy with attachment"})
899 copied_issue = @project.issues.first(:conditions => {:subject => "copy with attachment"})
905 assert_not_nil copied_issue
900 assert_not_nil copied_issue
906 assert_equal 1, copied_issue.attachments.count, "Attachment not copied"
901 assert_equal 1, copied_issue.attachments.count, "Attachment not copied"
907 assert_equal "testfile.txt", copied_issue.attachments.first.filename
902 assert_equal "testfile.txt", copied_issue.attachments.first.filename
908 end
903 end
909
904
910 should "copy memberships" do
905 should "copy memberships" do
911 assert @project.valid?
906 assert @project.valid?
912 assert @project.members.empty?
907 assert @project.members.empty?
913 assert @project.copy(@source_project)
908 assert @project.copy(@source_project)
914
909
915 assert_equal @source_project.memberships.size, @project.memberships.size
910 assert_equal @source_project.memberships.size, @project.memberships.size
916 @project.memberships.each do |membership|
911 @project.memberships.each do |membership|
917 assert membership
912 assert membership
918 assert_equal @project, membership.project
913 assert_equal @project, membership.project
919 end
914 end
920 end
915 end
921
916
922 should "copy memberships with groups and additional roles" do
917 should "copy memberships with groups and additional roles" do
923 group = Group.create!(:lastname => "Copy group")
918 group = Group.create!(:lastname => "Copy group")
924 user = User.find(7)
919 user = User.find(7)
925 group.users << user
920 group.users << user
926 # group role
921 # group role
927 Member.create!(:project_id => @source_project.id, :principal => group, :role_ids => [2])
922 Member.create!(:project_id => @source_project.id, :principal => group, :role_ids => [2])
928 member = Member.find_by_user_id_and_project_id(user.id, @source_project.id)
923 member = Member.find_by_user_id_and_project_id(user.id, @source_project.id)
929 # additional role
924 # additional role
930 member.role_ids = [1]
925 member.role_ids = [1]
931
926
932 assert @project.copy(@source_project)
927 assert @project.copy(@source_project)
933 member = Member.find_by_user_id_and_project_id(user.id, @project.id)
928 member = Member.find_by_user_id_and_project_id(user.id, @project.id)
934 assert_not_nil member
929 assert_not_nil member
935 assert_equal [1, 2], member.role_ids.sort
930 assert_equal [1, 2], member.role_ids.sort
936 end
931 end
937
932
938 should "copy project specific queries" do
933 should "copy project specific queries" do
939 assert @project.valid?
934 assert @project.valid?
940 assert @project.queries.empty?
935 assert @project.queries.empty?
941 assert @project.copy(@source_project)
936 assert @project.copy(@source_project)
942
937
943 assert_equal @source_project.queries.size, @project.queries.size
938 assert_equal @source_project.queries.size, @project.queries.size
944 @project.queries.each do |query|
939 @project.queries.each do |query|
945 assert query
940 assert query
946 assert_equal @project, query.project
941 assert_equal @project, query.project
947 end
942 end
948 assert_equal @source_project.queries.map(&:user_id).sort, @project.queries.map(&:user_id).sort
943 assert_equal @source_project.queries.map(&:user_id).sort, @project.queries.map(&:user_id).sort
949 end
944 end
950
945
951 should "copy versions" do
946 should "copy versions" do
952 @source_project.versions << Version.generate!
947 @source_project.versions << Version.generate!
953 @source_project.versions << Version.generate!
948 @source_project.versions << Version.generate!
954
949
955 assert @project.versions.empty?
950 assert @project.versions.empty?
956 assert @project.copy(@source_project)
951 assert @project.copy(@source_project)
957
952
958 assert_equal @source_project.versions.size, @project.versions.size
953 assert_equal @source_project.versions.size, @project.versions.size
959 @project.versions.each do |version|
954 @project.versions.each do |version|
960 assert version
955 assert version
961 assert_equal @project, version.project
956 assert_equal @project, version.project
962 end
957 end
963 end
958 end
964
959
965 should "copy wiki" do
960 should "copy wiki" do
966 assert_difference 'Wiki.count' do
961 assert_difference 'Wiki.count' do
967 assert @project.copy(@source_project)
962 assert @project.copy(@source_project)
968 end
963 end
969
964
970 assert @project.wiki
965 assert @project.wiki
971 assert_not_equal @source_project.wiki, @project.wiki
966 assert_not_equal @source_project.wiki, @project.wiki
972 assert_equal "Start page", @project.wiki.start_page
967 assert_equal "Start page", @project.wiki.start_page
973 end
968 end
974
969
975 should "copy wiki pages and content with hierarchy" do
970 should "copy wiki pages and content with hierarchy" do
976 assert_difference 'WikiPage.count', @source_project.wiki.pages.size do
971 assert_difference 'WikiPage.count', @source_project.wiki.pages.size do
977 assert @project.copy(@source_project)
972 assert @project.copy(@source_project)
978 end
973 end
979
974
980 assert @project.wiki
975 assert @project.wiki
981 assert_equal @source_project.wiki.pages.size, @project.wiki.pages.size
976 assert_equal @source_project.wiki.pages.size, @project.wiki.pages.size
982
977
983 @project.wiki.pages.each do |wiki_page|
978 @project.wiki.pages.each do |wiki_page|
984 assert wiki_page.content
979 assert wiki_page.content
985 assert !@source_project.wiki.pages.include?(wiki_page)
980 assert !@source_project.wiki.pages.include?(wiki_page)
986 end
981 end
987
982
988 parent = @project.wiki.find_page('Parent_page')
983 parent = @project.wiki.find_page('Parent_page')
989 child1 = @project.wiki.find_page('Child_page_1')
984 child1 = @project.wiki.find_page('Child_page_1')
990 child2 = @project.wiki.find_page('Child_page_2')
985 child2 = @project.wiki.find_page('Child_page_2')
991 assert_equal parent, child1.parent
986 assert_equal parent, child1.parent
992 assert_equal parent, child2.parent
987 assert_equal parent, child2.parent
993 end
988 end
994
989
995 should "copy issue categories" do
990 should "copy issue categories" do
996 assert @project.copy(@source_project)
991 assert @project.copy(@source_project)
997
992
998 assert_equal 2, @project.issue_categories.size
993 assert_equal 2, @project.issue_categories.size
999 @project.issue_categories.each do |issue_category|
994 @project.issue_categories.each do |issue_category|
1000 assert !@source_project.issue_categories.include?(issue_category)
995 assert !@source_project.issue_categories.include?(issue_category)
1001 end
996 end
1002 end
997 end
1003
998
1004 should "copy boards" do
999 should "copy boards" do
1005 assert @project.copy(@source_project)
1000 assert @project.copy(@source_project)
1006
1001
1007 assert_equal 1, @project.boards.size
1002 assert_equal 1, @project.boards.size
1008 @project.boards.each do |board|
1003 @project.boards.each do |board|
1009 assert !@source_project.boards.include?(board)
1004 assert !@source_project.boards.include?(board)
1010 end
1005 end
1011 end
1006 end
1012
1007
1013 should "change the new issues to use the copied issue categories" do
1008 should "change the new issues to use the copied issue categories" do
1014 issue = Issue.find(4)
1009 issue = Issue.find(4)
1015 issue.update_attribute(:category_id, 3)
1010 issue.update_attribute(:category_id, 3)
1016
1011
1017 assert @project.copy(@source_project)
1012 assert @project.copy(@source_project)
1018
1013
1019 @project.issues.each do |issue|
1014 @project.issues.each do |issue|
1020 assert issue.category
1015 assert issue.category
1021 assert_equal "Stock management", issue.category.name # Same name
1016 assert_equal "Stock management", issue.category.name # Same name
1022 assert_not_equal IssueCategory.find(3), issue.category # Different record
1017 assert_not_equal IssueCategory.find(3), issue.category # Different record
1023 end
1018 end
1024 end
1019 end
1025
1020
1026 should "limit copy with :only option" do
1021 should "limit copy with :only option" do
1027 assert @project.members.empty?
1022 assert @project.members.empty?
1028 assert @project.issue_categories.empty?
1023 assert @project.issue_categories.empty?
1029 assert @source_project.issues.any?
1024 assert @source_project.issues.any?
1030
1025
1031 assert @project.copy(@source_project, :only => ['members', 'issue_categories'])
1026 assert @project.copy(@source_project, :only => ['members', 'issue_categories'])
1032
1027
1033 assert @project.members.any?
1028 assert @project.members.any?
1034 assert @project.issue_categories.any?
1029 assert @project.issue_categories.any?
1035 assert @project.issues.empty?
1030 assert @project.issues.empty?
1036 end
1031 end
1037 end
1032 end
1038
1033
1039 def test_copy_should_copy_subtasks
1034 def test_copy_should_copy_subtasks
1040 source = Project.generate!(:tracker_ids => [1])
1035 source = Project.generate!(:tracker_ids => [1])
1041 issue = Issue.generate_with_descendants!(:project => source)
1036 issue = Issue.generate_with_descendants!(:project => source)
1042 project = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1])
1037 project = Project.new(:name => 'Copy', :identifier => 'copy', :tracker_ids => [1])
1043
1038
1044 assert_difference 'Project.count' do
1039 assert_difference 'Project.count' do
1045 assert_difference 'Issue.count', 1+issue.descendants.count do
1040 assert_difference 'Issue.count', 1+issue.descendants.count do
1046 assert project.copy(source.reload)
1041 assert project.copy(source.reload)
1047 end
1042 end
1048 end
1043 end
1049 copy = Issue.where(:parent_id => nil).order("id DESC").first
1044 copy = Issue.where(:parent_id => nil).order("id DESC").first
1050 assert_equal project, copy.project
1045 assert_equal project, copy.project
1051 assert_equal issue.descendants.count, copy.descendants.count
1046 assert_equal issue.descendants.count, copy.descendants.count
1052 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1047 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1053 assert child_copy.descendants.any?
1048 assert child_copy.descendants.any?
1054 end
1049 end
1055
1050
1056 context "#start_date" do
1051 context "#start_date" do
1057 setup do
1052 setup do
1058 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1053 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1059 @project = Project.generate!(:identifier => 'test0')
1054 @project = Project.generate!(:identifier => 'test0')
1060 @project.trackers << Tracker.generate!
1055 @project.trackers << Tracker.generate!
1061 end
1056 end
1062
1057
1063 should "be nil if there are no issues on the project" do
1058 should "be nil if there are no issues on the project" do
1064 assert_nil @project.start_date
1059 assert_nil @project.start_date
1065 end
1060 end
1066
1061
1067 should "be tested when issues have no start date"
1062 should "be tested when issues have no start date"
1068
1063
1069 should "be the earliest start date of it's issues" do
1064 should "be the earliest start date of it's issues" do
1070 early = 7.days.ago.to_date
1065 early = 7.days.ago.to_date
1071 Issue.generate!(:project => @project, :start_date => Date.today)
1066 Issue.generate!(:project => @project, :start_date => Date.today)
1072 Issue.generate!(:project => @project, :start_date => early)
1067 Issue.generate!(:project => @project, :start_date => early)
1073
1068
1074 assert_equal early, @project.start_date
1069 assert_equal early, @project.start_date
1075 end
1070 end
1076
1071
1077 end
1072 end
1078
1073
1079 context "#due_date" do
1074 context "#due_date" do
1080 setup do
1075 setup do
1081 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1076 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1082 @project = Project.generate!(:identifier => 'test0')
1077 @project = Project.generate!(:identifier => 'test0')
1083 @project.trackers << Tracker.generate!
1078 @project.trackers << Tracker.generate!
1084 end
1079 end
1085
1080
1086 should "be nil if there are no issues on the project" do
1081 should "be nil if there are no issues on the project" do
1087 assert_nil @project.due_date
1082 assert_nil @project.due_date
1088 end
1083 end
1089
1084
1090 should "be tested when issues have no due date"
1085 should "be tested when issues have no due date"
1091
1086
1092 should "be the latest due date of it's issues" do
1087 should "be the latest due date of it's issues" do
1093 future = 7.days.from_now.to_date
1088 future = 7.days.from_now.to_date
1094 Issue.generate!(:project => @project, :due_date => future)
1089 Issue.generate!(:project => @project, :due_date => future)
1095 Issue.generate!(:project => @project, :due_date => Date.today)
1090 Issue.generate!(:project => @project, :due_date => Date.today)
1096
1091
1097 assert_equal future, @project.due_date
1092 assert_equal future, @project.due_date
1098 end
1093 end
1099
1094
1100 should "be the latest due date of it's versions" do
1095 should "be the latest due date of it's versions" do
1101 future = 7.days.from_now.to_date
1096 future = 7.days.from_now.to_date
1102 @project.versions << Version.generate!(:effective_date => future)
1097 @project.versions << Version.generate!(:effective_date => future)
1103 @project.versions << Version.generate!(:effective_date => Date.today)
1098 @project.versions << Version.generate!(:effective_date => Date.today)
1104
1099
1105
1100
1106 assert_equal future, @project.due_date
1101 assert_equal future, @project.due_date
1107
1102
1108 end
1103 end
1109
1104
1110 should "pick the latest date from it's issues and versions" do
1105 should "pick the latest date from it's issues and versions" do
1111 future = 7.days.from_now.to_date
1106 future = 7.days.from_now.to_date
1112 far_future = 14.days.from_now.to_date
1107 far_future = 14.days.from_now.to_date
1113 Issue.generate!(:project => @project, :due_date => far_future)
1108 Issue.generate!(:project => @project, :due_date => far_future)
1114 @project.versions << Version.generate!(:effective_date => future)
1109 @project.versions << Version.generate!(:effective_date => future)
1115
1110
1116 assert_equal far_future, @project.due_date
1111 assert_equal far_future, @project.due_date
1117 end
1112 end
1118
1113
1119 end
1114 end
1120
1115
1121 context "Project#completed_percent" do
1116 context "Project#completed_percent" do
1122 setup do
1117 setup do
1123 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1118 ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
1124 @project = Project.generate!(:identifier => 'test0')
1119 @project = Project.generate!(:identifier => 'test0')
1125 @project.trackers << Tracker.generate!
1120 @project.trackers << Tracker.generate!
1126 end
1121 end
1127
1122
1128 context "no versions" do
1123 context "no versions" do
1129 should "be 100" do
1124 should "be 100" do
1130 assert_equal 100, @project.completed_percent
1125 assert_equal 100, @project.completed_percent
1131 end
1126 end
1132 end
1127 end
1133
1128
1134 context "with versions" do
1129 context "with versions" do
1135 should "return 0 if the versions have no issues" do
1130 should "return 0 if the versions have no issues" do
1136 Version.generate!(:project => @project)
1131 Version.generate!(:project => @project)
1137 Version.generate!(:project => @project)
1132 Version.generate!(:project => @project)
1138
1133
1139 assert_equal 0, @project.completed_percent
1134 assert_equal 0, @project.completed_percent
1140 end
1135 end
1141
1136
1142 should "return 100 if the version has only closed issues" do
1137 should "return 100 if the version has only closed issues" do
1143 v1 = Version.generate!(:project => @project)
1138 v1 = Version.generate!(:project => @project)
1144 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
1139 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
1145 v2 = Version.generate!(:project => @project)
1140 v2 = Version.generate!(:project => @project)
1146 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
1141 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
1147
1142
1148 assert_equal 100, @project.completed_percent
1143 assert_equal 100, @project.completed_percent
1149 end
1144 end
1150
1145
1151 should "return the averaged completed percent of the versions (not weighted)" do
1146 should "return the averaged completed percent of the versions (not weighted)" do
1152 v1 = Version.generate!(:project => @project)
1147 v1 = Version.generate!(:project => @project)
1153 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
1148 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
1154 v2 = Version.generate!(:project => @project)
1149 v2 = Version.generate!(:project => @project)
1155 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
1150 Issue.generate!(:project => @project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
1156
1151
1157 assert_equal 50, @project.completed_percent
1152 assert_equal 50, @project.completed_percent
1158 end
1153 end
1159
1154
1160 end
1155 end
1161 end
1156 end
1162
1157
1163 context "#notified_users" do
1158 context "#notified_users" do
1164 setup do
1159 setup do
1165 @project = Project.generate!
1160 @project = Project.generate!
1166 @role = Role.generate!
1161 @role = Role.generate!
1167
1162
1168 @user_with_membership_notification = User.generate!(:mail_notification => 'selected')
1163 @user_with_membership_notification = User.generate!(:mail_notification => 'selected')
1169 Member.create!(:project => @project, :roles => [@role], :principal => @user_with_membership_notification, :mail_notification => true)
1164 Member.create!(:project => @project, :roles => [@role], :principal => @user_with_membership_notification, :mail_notification => true)
1170
1165
1171 @all_events_user = User.generate!(:mail_notification => 'all')
1166 @all_events_user = User.generate!(:mail_notification => 'all')
1172 Member.create!(:project => @project, :roles => [@role], :principal => @all_events_user)
1167 Member.create!(:project => @project, :roles => [@role], :principal => @all_events_user)
1173
1168
1174 @no_events_user = User.generate!(:mail_notification => 'none')
1169 @no_events_user = User.generate!(:mail_notification => 'none')
1175 Member.create!(:project => @project, :roles => [@role], :principal => @no_events_user)
1170 Member.create!(:project => @project, :roles => [@role], :principal => @no_events_user)
1176
1171
1177 @only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
1172 @only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
1178 Member.create!(:project => @project, :roles => [@role], :principal => @only_my_events_user)
1173 Member.create!(:project => @project, :roles => [@role], :principal => @only_my_events_user)
1179
1174
1180 @only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
1175 @only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
1181 Member.create!(:project => @project, :roles => [@role], :principal => @only_assigned_user)
1176 Member.create!(:project => @project, :roles => [@role], :principal => @only_assigned_user)
1182
1177
1183 @only_owned_user = User.generate!(:mail_notification => 'only_owner')
1178 @only_owned_user = User.generate!(:mail_notification => 'only_owner')
1184 Member.create!(:project => @project, :roles => [@role], :principal => @only_owned_user)
1179 Member.create!(:project => @project, :roles => [@role], :principal => @only_owned_user)
1185 end
1180 end
1186
1181
1187 should "include members with a mail notification" do
1182 should "include members with a mail notification" do
1188 assert @project.notified_users.include?(@user_with_membership_notification)
1183 assert @project.notified_users.include?(@user_with_membership_notification)
1189 end
1184 end
1190
1185
1191 should "include users with the 'all' notification option" do
1186 should "include users with the 'all' notification option" do
1192 assert @project.notified_users.include?(@all_events_user)
1187 assert @project.notified_users.include?(@all_events_user)
1193 end
1188 end
1194
1189
1195 should "not include users with the 'none' notification option" do
1190 should "not include users with the 'none' notification option" do
1196 assert !@project.notified_users.include?(@no_events_user)
1191 assert !@project.notified_users.include?(@no_events_user)
1197 end
1192 end
1198
1193
1199 should "not include users with the 'only_my_events' notification option" do
1194 should "not include users with the 'only_my_events' notification option" do
1200 assert !@project.notified_users.include?(@only_my_events_user)
1195 assert !@project.notified_users.include?(@only_my_events_user)
1201 end
1196 end
1202
1197
1203 should "not include users with the 'only_assigned' notification option" do
1198 should "not include users with the 'only_assigned' notification option" do
1204 assert !@project.notified_users.include?(@only_assigned_user)
1199 assert !@project.notified_users.include?(@only_assigned_user)
1205 end
1200 end
1206
1201
1207 should "not include users with the 'only_owner' notification option" do
1202 should "not include users with the 'only_owner' notification option" do
1208 assert !@project.notified_users.include?(@only_owned_user)
1203 assert !@project.notified_users.include?(@only_owned_user)
1209 end
1204 end
1210 end
1205 end
1211
1206
1212 end
1207 end
General Comments 0
You need to be logged in to leave comments. Login now