##// END OF EJS Templates
Drop shoulda....
Jean-Philippe Lang -
r13294:f04148695a4a
parent child
Show More
@@ -1,104 +1,103
1 1 source 'https://rubygems.org'
2 2
3 3 gem "rails", "4.1.8"
4 4 gem "jquery-rails", "~> 3.1.1"
5 5 gem "coderay", "~> 1.1.0"
6 6 gem "builder", ">= 3.0.4"
7 7 gem "request_store", "1.0.5"
8 8 gem "mime-types"
9 9 gem "awesome_nested_set", "3.0.0"
10 10 gem "protected_attributes"
11 11 gem "actionpack-action_caching"
12 12
13 13 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
14 14 gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin, :jruby]
15 15 gem "rbpdf", "~> 1.18.2"
16 16
17 17 # Optional gem for LDAP authentication
18 18 group :ldap do
19 19 gem "net-ldap", "~> 0.3.1"
20 20 end
21 21
22 22 # Optional gem for OpenID authentication
23 23 group :openid do
24 24 gem "ruby-openid", "~> 2.3.0", :require => "openid"
25 25 gem "rack-openid"
26 26 end
27 27
28 28 platforms :mri, :mingw, :x64_mingw do
29 29 # Optional gem for exporting the gantt to a PNG file, not supported with jruby
30 30 group :rmagick do
31 31 gem "rmagick", ">= 2.0.0"
32 32 end
33 33
34 34 # Optional Markdown support, not for JRuby
35 35 group :markdown do
36 36 gem "redcarpet", "~> 3.1.2"
37 37 end
38 38 end
39 39
40 40 platforms :jruby do
41 41 # jruby-openssl is bundled with JRuby 1.7.0
42 42 gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0'
43 43 gem "activerecord-jdbc-adapter", "~> 1.3.2"
44 44 end
45 45
46 46 # Include database gems for the adapters found in the database
47 47 # configuration file
48 48 require 'erb'
49 49 require 'yaml'
50 50 database_file = File.join(File.dirname(__FILE__), "config/database.yml")
51 51 if File.exist?(database_file)
52 52 database_config = YAML::load(ERB.new(IO.read(database_file)).result)
53 53 adapters = database_config.values.map {|c| c['adapter']}.compact.uniq
54 54 if adapters.any?
55 55 adapters.each do |adapter|
56 56 case adapter
57 57 when 'mysql2'
58 58 gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw, :x64_mingw]
59 59 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
60 60 when 'mysql'
61 61 gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
62 62 when /postgresql/
63 63 gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw, :x64_mingw]
64 64 gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
65 65 when /sqlite3/
66 66 gem "sqlite3", :platforms => [:mri, :mingw, :x64_mingw]
67 67 gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
68 68 when /sqlserver/
69 69 gem "tiny_tds", "~> 0.6.2", :platforms => [:mri, :mingw, :x64_mingw]
70 70 gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
71 71 else
72 72 warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
73 73 end
74 74 end
75 75 else
76 76 warn("No adapter found in config/database.yml, please configure it first")
77 77 end
78 78 else
79 79 warn("Please configure your config/database.yml first")
80 80 end
81 81
82 82 group :development do
83 83 gem "rdoc", ">= 2.4.2"
84 84 gem "yard"
85 85 end
86 86
87 87 group :test do
88 88 gem "minitest"
89 gem "shoulda-context"
90 89 gem "mocha", "~> 1.0.0", :require => 'mocha/api'
91 90 # For running UI tests
92 91 gem "capybara", "~> 2.1.0"
93 92 gem "selenium-webdriver"
94 93 end
95 94
96 95 local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
97 96 if File.exists?(local_gemfile)
98 97 eval_gemfile local_gemfile
99 98 end
100 99
101 100 # Load plugins' Gemfiles
102 101 Dir.glob File.expand_path("../plugins/*/{Gemfile,PluginGemfile}", __FILE__) do |file|
103 102 eval_gemfile file
104 103 end
@@ -1,293 +1,289
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 #require 'shoulda'
19 18 ENV["RAILS_ENV"] = "test"
20 19 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
21 20 require 'rails/test_help'
22 21 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
23 22
24 23 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
25 24 include ObjectHelpers
26 25
27 26 require 'awesome_nested_set/version'
28 27 require 'net/ldap'
29 28
30 29 class ActionView::TestCase
31 30 helper :application
32 31 include ApplicationHelper
33 32 end
34 33
35 34 class ActiveSupport::TestCase
36 35 include ActionDispatch::TestProcess
37 include Shoulda::Context::Assertions
38 include Shoulda::Context::InstanceMethods
39 extend Shoulda::Context::ClassMethods
40 36
41 37 self.use_transactional_fixtures = true
42 38 self.use_instantiated_fixtures = false
43 39
44 40 #ESCAPED_CANT = 'can&#x27;t'
45 41 #ESCAPED_UCANT = 'Can&#x27;t'
46 42 # Rails 4.0.2
47 43 ESCAPED_CANT = 'can&#39;t'
48 44 ESCAPED_UCANT = 'Can&#39;t'
49 45
50 46 def log_user(login, password)
51 47 User.anonymous
52 48 get "/login"
53 49 assert_equal nil, session[:user_id]
54 50 assert_response :success
55 51 assert_template "account/login"
56 52 post "/login", :username => login, :password => password
57 53 assert_equal login, User.find(session[:user_id]).login
58 54 end
59 55
60 56 def uploaded_test_file(name, mime)
61 57 fixture_file_upload("files/#{name}", mime, true)
62 58 end
63 59
64 60 def credentials(user, password=nil)
65 61 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
66 62 end
67 63
68 64 # Mock out a file
69 65 def self.mock_file
70 66 file = 'a_file.png'
71 67 file.stubs(:size).returns(32)
72 68 file.stubs(:original_filename).returns('a_file.png')
73 69 file.stubs(:content_type).returns('image/png')
74 70 file.stubs(:read).returns(false)
75 71 file
76 72 end
77 73
78 74 def mock_file
79 75 self.class.mock_file
80 76 end
81 77
82 78 def mock_file_with_options(options={})
83 79 file = ''
84 80 file.stubs(:size).returns(32)
85 81 original_filename = options[:original_filename] || nil
86 82 file.stubs(:original_filename).returns(original_filename)
87 83 content_type = options[:content_type] || nil
88 84 file.stubs(:content_type).returns(content_type)
89 85 file.stubs(:read).returns(false)
90 86 file
91 87 end
92 88
93 89 # Use a temporary directory for attachment related tests
94 90 def set_tmp_attachments_directory
95 91 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
96 92 unless File.directory?("#{Rails.root}/tmp/test/attachments")
97 93 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
98 94 end
99 95 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
100 96 end
101 97
102 98 def set_fixtures_attachments_directory
103 99 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
104 100 end
105 101
106 102 def with_settings(options, &block)
107 103 saved_settings = options.keys.inject({}) do |h, k|
108 104 h[k] = case Setting[k]
109 105 when Symbol, false, true, nil
110 106 Setting[k]
111 107 else
112 108 Setting[k].dup
113 109 end
114 110 h
115 111 end
116 112 options.each {|k, v| Setting[k] = v}
117 113 yield
118 114 ensure
119 115 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
120 116 end
121 117
122 118 # Yields the block with user as the current user
123 119 def with_current_user(user, &block)
124 120 saved_user = User.current
125 121 User.current = user
126 122 yield
127 123 ensure
128 124 User.current = saved_user
129 125 end
130 126
131 127 def with_locale(locale, &block)
132 128 saved_localed = ::I18n.locale
133 129 ::I18n.locale = locale
134 130 yield
135 131 ensure
136 132 ::I18n.locale = saved_localed
137 133 end
138 134
139 135 def change_user_password(login, new_password)
140 136 user = User.where(:login => login).first
141 137 user.password, user.password_confirmation = new_password, new_password
142 138 user.save!
143 139 end
144 140
145 141 def self.ldap_configured?
146 142 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
147 143 return @test_ldap.bind
148 144 rescue Exception => e
149 145 # LDAP is not listening
150 146 return nil
151 147 end
152 148
153 149 def self.convert_installed?
154 150 Redmine::Thumbnail.convert_available?
155 151 end
156 152
157 153 # Returns the path to the test +vendor+ repository
158 154 def self.repository_path(vendor)
159 155 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
160 156 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
161 157 path.tr("\\", "/")
162 158 end
163 159
164 160 # Returns the url of the subversion test repository
165 161 def self.subversion_repository_url
166 162 path = repository_path('subversion')
167 163 path = '/' + path unless path.starts_with?('/')
168 164 "file://#{path}"
169 165 end
170 166
171 167 # Returns true if the +vendor+ test repository is configured
172 168 def self.repository_configured?(vendor)
173 169 File.directory?(repository_path(vendor))
174 170 end
175 171
176 172 def repository_path_hash(arr)
177 173 hs = {}
178 174 hs[:path] = arr.join("/")
179 175 hs[:param] = arr.join("/")
180 176 hs
181 177 end
182 178
183 179 def assert_save(object)
184 180 saved = object.save
185 181 message = "#{object.class} could not be saved"
186 182 errors = object.errors.full_messages.map {|m| "- #{m}"}
187 183 message << ":\n#{errors.join("\n")}" if errors.any?
188 184 assert_equal true, saved, message
189 185 end
190 186
191 187 def assert_select_error(arg)
192 188 assert_select '#errorExplanation', :text => arg
193 189 end
194 190
195 191 def assert_include(expected, s, message=nil)
196 192 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
197 193 end
198 194
199 195 def assert_not_include(expected, s, message=nil)
200 196 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
201 197 end
202 198
203 199 def assert_select_in(text, *args, &block)
204 200 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
205 201 assert_select(d, *args, &block)
206 202 end
207 203
208 204 def assert_mail_body_match(expected, mail, message=nil)
209 205 if expected.is_a?(String)
210 206 assert_include expected, mail_body(mail), message
211 207 else
212 208 assert_match expected, mail_body(mail), message
213 209 end
214 210 end
215 211
216 212 def assert_mail_body_no_match(expected, mail, message=nil)
217 213 if expected.is_a?(String)
218 214 assert_not_include expected, mail_body(mail), message
219 215 else
220 216 assert_no_match expected, mail_body(mail), message
221 217 end
222 218 end
223 219
224 220 def mail_body(mail)
225 221 mail.parts.first.body.encoded
226 222 end
227 223
228 224 # awesome_nested_set new node lft and rgt value changed this refactor revision.
229 225 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
230 226 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
231 227 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
232 228 # It seems correct behavior because of this line comment.
233 229 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
234 230 def new_issue_lft
235 231 # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
236 232 Issue.maximum(:rgt) + 1
237 233 end
238 234 end
239 235
240 236 module Redmine
241 237 class RoutingTest < ActionDispatch::IntegrationTest
242 238 def should_route(arg)
243 239 arg = arg.dup
244 240 request = arg.keys.detect {|key| key.is_a?(String)}
245 241 raise ArgumentError unless request
246 242 options = arg.slice!(request)
247 243
248 244 raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/
249 245 method, path = $1.downcase.to_sym, $2
250 246
251 247 raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/
252 248 controller, action = $1, $2
253 249
254 250 assert_routing(
255 251 {:method => method, :path => path},
256 252 options.merge(:controller => controller, :action => action)
257 253 )
258 254 end
259 255 end
260 256
261 257 module ApiTest
262 258 API_FORMATS = %w(json xml).freeze
263 259
264 260 # Base class for API tests
265 261 class Base < ActionDispatch::IntegrationTest
266 262 end
267 263
268 264 class Routing < Redmine::RoutingTest
269 265 def should_route(arg)
270 266 arg = arg.dup
271 267 request = arg.keys.detect {|key| key.is_a?(String)}
272 268 raise ArgumentError unless request
273 269 options = arg.slice!(request)
274 270
275 271 API_FORMATS.each do |format|
276 272 format_request = request.sub /$/, ".#{format}"
277 273 super options.merge(format_request => arg[request], :format => format)
278 274 end
279 275 end
280 276 end
281 277 end
282 278 end
283 279
284 280 # URL helpers do not work with config.threadsafe!
285 281 # https://github.com/rspec/rspec-rails/issues/476#issuecomment-4705454
286 282 ActionView::TestCase::TestController.instance_eval do
287 283 helper Rails.application.routes.url_helpers
288 284 end
289 285 ActionView::TestCase::TestController.class_eval do
290 286 def _routes
291 287 Rails.application.routes
292 288 end
293 289 end
@@ -1,952 +1,952
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2014 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../test_helper', __FILE__)
21 21
22 22 class MailHandlerTest < ActiveSupport::TestCase
23 23 fixtures :users, :projects, :enabled_modules, :roles,
24 24 :members, :member_roles, :users,
25 25 :issues, :issue_statuses,
26 26 :workflows, :trackers, :projects_trackers,
27 27 :versions, :enumerations, :issue_categories,
28 28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
29 29 :boards, :messages
30 30
31 31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
32 32
33 33 def setup
34 34 ActionMailer::Base.deliveries.clear
35 35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
36 36 end
37 37
38 38 def teardown
39 39 Setting.clear_cache
40 40 end
41 41
42 42 def test_add_issue
43 43 ActionMailer::Base.deliveries.clear
44 44 lft1 = new_issue_lft
45 45 # This email contains: 'Project: onlinestore'
46 46 issue = submit_email('ticket_on_given_project.eml')
47 47 assert issue.is_a?(Issue)
48 48 assert !issue.new_record?
49 49 issue.reload
50 50 assert_equal Project.find(2), issue.project
51 51 assert_equal issue.project.trackers.first, issue.tracker
52 52 assert_equal 'New ticket on a given project', issue.subject
53 53 assert_equal User.find_by_login('jsmith'), issue.author
54 54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
55 55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
56 56 assert_equal '2010-01-01', issue.start_date.to_s
57 57 assert_equal '2010-12-31', issue.due_date.to_s
58 58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
59 59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
60 60 assert_equal 2.5, issue.estimated_hours
61 61 assert_equal 30, issue.done_ratio
62 62 assert_equal [issue.id, lft1, lft1 + 1], [issue.root_id, issue.lft, issue.rgt]
63 63 # keywords should be removed from the email body
64 64 assert !issue.description.match(/^Project:/i)
65 65 assert !issue.description.match(/^Status:/i)
66 66 assert !issue.description.match(/^Start Date:/i)
67 67 # Email notification should be sent
68 68 mail = ActionMailer::Base.deliveries.last
69 69 assert_not_nil mail
70 70 assert mail.subject.include?("##{issue.id}")
71 71 assert mail.subject.include?('New ticket on a given project')
72 72 end
73 73
74 74 def test_add_issue_with_default_tracker
75 75 # This email contains: 'Project: onlinestore'
76 76 issue = submit_email(
77 77 'ticket_on_given_project.eml',
78 78 :issue => {:tracker => 'Support request'}
79 79 )
80 80 assert issue.is_a?(Issue)
81 81 assert !issue.new_record?
82 82 issue.reload
83 83 assert_equal 'Support request', issue.tracker.name
84 84 end
85 85
86 86 def test_add_issue_with_status
87 87 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
88 88 issue = submit_email('ticket_on_given_project.eml')
89 89 assert issue.is_a?(Issue)
90 90 assert !issue.new_record?
91 91 issue.reload
92 92 assert_equal Project.find(2), issue.project
93 93 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
94 94 end
95 95
96 96 def test_add_issue_with_attributes_override
97 97 issue = submit_email(
98 98 'ticket_with_attributes.eml',
99 99 :allow_override => 'tracker,category,priority'
100 100 )
101 101 assert issue.is_a?(Issue)
102 102 assert !issue.new_record?
103 103 issue.reload
104 104 assert_equal 'New ticket on a given project', issue.subject
105 105 assert_equal User.find_by_login('jsmith'), issue.author
106 106 assert_equal Project.find(2), issue.project
107 107 assert_equal 'Feature request', issue.tracker.to_s
108 108 assert_equal 'Stock management', issue.category.to_s
109 109 assert_equal 'Urgent', issue.priority.to_s
110 110 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
111 111 end
112 112
113 113 def test_add_issue_with_group_assignment
114 114 with_settings :issue_group_assignment => '1' do
115 115 issue = submit_email('ticket_on_given_project.eml') do |email|
116 116 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
117 117 end
118 118 assert issue.is_a?(Issue)
119 119 assert !issue.new_record?
120 120 issue.reload
121 121 assert_equal Group.find(11), issue.assigned_to
122 122 end
123 123 end
124 124
125 125 def test_add_issue_with_partial_attributes_override
126 126 issue = submit_email(
127 127 'ticket_with_attributes.eml',
128 128 :issue => {:priority => 'High'},
129 129 :allow_override => ['tracker']
130 130 )
131 131 assert issue.is_a?(Issue)
132 132 assert !issue.new_record?
133 133 issue.reload
134 134 assert_equal 'New ticket on a given project', issue.subject
135 135 assert_equal User.find_by_login('jsmith'), issue.author
136 136 assert_equal Project.find(2), issue.project
137 137 assert_equal 'Feature request', issue.tracker.to_s
138 138 assert_nil issue.category
139 139 assert_equal 'High', issue.priority.to_s
140 140 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
141 141 end
142 142
143 143 def test_add_issue_with_spaces_between_attribute_and_separator
144 144 issue = submit_email(
145 145 'ticket_with_spaces_between_attribute_and_separator.eml',
146 146 :allow_override => 'tracker,category,priority'
147 147 )
148 148 assert issue.is_a?(Issue)
149 149 assert !issue.new_record?
150 150 issue.reload
151 151 assert_equal 'New ticket on a given project', issue.subject
152 152 assert_equal User.find_by_login('jsmith'), issue.author
153 153 assert_equal Project.find(2), issue.project
154 154 assert_equal 'Feature request', issue.tracker.to_s
155 155 assert_equal 'Stock management', issue.category.to_s
156 156 assert_equal 'Urgent', issue.priority.to_s
157 157 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
158 158 end
159 159
160 160 def test_add_issue_with_attachment_to_specific_project
161 161 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
162 162 assert issue.is_a?(Issue)
163 163 assert !issue.new_record?
164 164 issue.reload
165 165 assert_equal 'Ticket created by email with attachment', issue.subject
166 166 assert_equal User.find_by_login('jsmith'), issue.author
167 167 assert_equal Project.find(2), issue.project
168 168 assert_equal 'This is a new ticket with attachments', issue.description
169 169 # Attachment properties
170 170 assert_equal 1, issue.attachments.size
171 171 assert_equal 'Paella.jpg', issue.attachments.first.filename
172 172 assert_equal 'image/jpeg', issue.attachments.first.content_type
173 173 assert_equal 10790, issue.attachments.first.filesize
174 174 end
175 175
176 176 def test_add_issue_with_custom_fields
177 177 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
178 178 assert issue.is_a?(Issue)
179 179 assert !issue.new_record?
180 180 issue.reload
181 181 assert_equal 'New ticket with custom field values', issue.subject
182 182 assert_equal 'PostgreSQL', issue.custom_field_value(1)
183 183 assert_equal 'Value for a custom field', issue.custom_field_value(2)
184 184 assert !issue.description.match(/^searchable field:/i)
185 185 end
186 186
187 187 def test_add_issue_with_version_custom_fields
188 188 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
189 189
190 190 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
191 191 email << "Affected version: 1.0\n"
192 192 end
193 193 assert issue.is_a?(Issue)
194 194 assert !issue.new_record?
195 195 issue.reload
196 196 assert_equal '2', issue.custom_field_value(field)
197 197 end
198 198
199 199 def test_add_issue_should_match_assignee_on_display_name
200 200 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
201 201 User.add_to_project(user, Project.find(2))
202 202 issue = submit_email('ticket_on_given_project.eml') do |email|
203 203 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
204 204 end
205 205 assert issue.is_a?(Issue)
206 206 assert_equal user, issue.assigned_to
207 207 end
208 208
209 209 def test_add_issue_should_set_default_start_date
210 210 with_settings :default_issue_start_date_to_creation_date => '1' do
211 211 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
212 212 assert issue.is_a?(Issue)
213 213 assert_equal Date.today, issue.start_date
214 214 end
215 215 end
216 216
217 217 def test_add_issue_with_cc
218 218 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
219 219 assert issue.is_a?(Issue)
220 220 assert !issue.new_record?
221 221 issue.reload
222 222 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
223 223 assert_equal 1, issue.watcher_user_ids.size
224 224 end
225 225
226 226 def test_add_issue_by_unknown_user
227 227 assert_no_difference 'User.count' do
228 228 assert_equal false,
229 229 submit_email(
230 230 'ticket_by_unknown_user.eml',
231 231 :issue => {:project => 'ecookbook'}
232 232 )
233 233 end
234 234 end
235 235
236 236 def test_add_issue_by_anonymous_user
237 237 Role.anonymous.add_permission!(:add_issues)
238 238 assert_no_difference 'User.count' do
239 239 issue = submit_email(
240 240 'ticket_by_unknown_user.eml',
241 241 :issue => {:project => 'ecookbook'},
242 242 :unknown_user => 'accept'
243 243 )
244 244 assert issue.is_a?(Issue)
245 245 assert issue.author.anonymous?
246 246 end
247 247 end
248 248
249 249 def test_add_issue_by_anonymous_user_with_no_from_address
250 250 Role.anonymous.add_permission!(:add_issues)
251 251 assert_no_difference 'User.count' do
252 252 issue = submit_email(
253 253 'ticket_by_empty_user.eml',
254 254 :issue => {:project => 'ecookbook'},
255 255 :unknown_user => 'accept'
256 256 )
257 257 assert issue.is_a?(Issue)
258 258 assert issue.author.anonymous?
259 259 end
260 260 end
261 261
262 262 def test_add_issue_by_anonymous_user_on_private_project
263 263 Role.anonymous.add_permission!(:add_issues)
264 264 assert_no_difference 'User.count' do
265 265 assert_no_difference 'Issue.count' do
266 266 assert_equal false,
267 267 submit_email(
268 268 'ticket_by_unknown_user.eml',
269 269 :issue => {:project => 'onlinestore'},
270 270 :unknown_user => 'accept'
271 271 )
272 272 end
273 273 end
274 274 end
275 275
276 276 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
277 277 lft1 = new_issue_lft
278 278 assert_no_difference 'User.count' do
279 279 assert_difference 'Issue.count' do
280 280 issue = submit_email(
281 281 'ticket_by_unknown_user.eml',
282 282 :issue => {:project => 'onlinestore'},
283 283 :no_permission_check => '1',
284 284 :unknown_user => 'accept'
285 285 )
286 286 assert issue.is_a?(Issue)
287 287 assert issue.author.anonymous?
288 288 assert !issue.project.is_public?
289 289 assert_equal [issue.id, lft1, lft1 + 1], [issue.root_id, issue.lft, issue.rgt]
290 290 end
291 291 end
292 292 end
293 293
294 294 def test_add_issue_by_created_user
295 295 Setting.default_language = 'en'
296 296 assert_difference 'User.count' do
297 297 issue = submit_email(
298 298 'ticket_by_unknown_user.eml',
299 299 :issue => {:project => 'ecookbook'},
300 300 :unknown_user => 'create'
301 301 )
302 302 assert issue.is_a?(Issue)
303 303 assert issue.author.active?
304 304 assert_equal 'john.doe@somenet.foo', issue.author.mail
305 305 assert_equal 'John', issue.author.firstname
306 306 assert_equal 'Doe', issue.author.lastname
307 307
308 308 # account information
309 309 email = ActionMailer::Base.deliveries.first
310 310 assert_not_nil email
311 311 assert email.subject.include?('account activation')
312 312 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
313 313 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
314 314 assert_equal issue.author, User.try_to_login(login, password)
315 315 end
316 316 end
317 317
318 318 def test_created_user_should_be_added_to_groups
319 319 group1 = Group.generate!
320 320 group2 = Group.generate!
321 321
322 322 assert_difference 'User.count' do
323 323 submit_email(
324 324 'ticket_by_unknown_user.eml',
325 325 :issue => {:project => 'ecookbook'},
326 326 :unknown_user => 'create',
327 327 :default_group => "#{group1.name},#{group2.name}"
328 328 )
329 329 end
330 330 user = User.order('id DESC').first
331 assert_same_elements [group1, group2], user.groups
331 assert_equal [group1, group2].sort, user.groups.sort
332 332 end
333 333
334 334 def test_created_user_should_not_receive_account_information_with_no_account_info_option
335 335 assert_difference 'User.count' do
336 336 submit_email(
337 337 'ticket_by_unknown_user.eml',
338 338 :issue => {:project => 'ecookbook'},
339 339 :unknown_user => 'create',
340 340 :no_account_notice => '1'
341 341 )
342 342 end
343 343
344 344 # only 1 email for the new issue notification
345 345 assert_equal 1, ActionMailer::Base.deliveries.size
346 346 email = ActionMailer::Base.deliveries.first
347 347 assert_include 'Ticket by unknown user', email.subject
348 348 end
349 349
350 350 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
351 351 assert_difference 'User.count' do
352 352 submit_email(
353 353 'ticket_by_unknown_user.eml',
354 354 :issue => {:project => 'ecookbook'},
355 355 :unknown_user => 'create',
356 356 :no_notification => '1'
357 357 )
358 358 end
359 359 user = User.order('id DESC').first
360 360 assert_equal 'none', user.mail_notification
361 361 end
362 362
363 363 def test_add_issue_without_from_header
364 364 Role.anonymous.add_permission!(:add_issues)
365 365 assert_equal false, submit_email('ticket_without_from_header.eml')
366 366 end
367 367
368 368 def test_add_issue_with_invalid_attributes
369 369 with_settings :default_issue_start_date_to_creation_date => '0' do
370 370 issue = submit_email(
371 371 'ticket_with_invalid_attributes.eml',
372 372 :allow_override => 'tracker,category,priority'
373 373 )
374 374 assert issue.is_a?(Issue)
375 375 assert !issue.new_record?
376 376 issue.reload
377 377 assert_nil issue.assigned_to
378 378 assert_nil issue.start_date
379 379 assert_nil issue.due_date
380 380 assert_equal 0, issue.done_ratio
381 381 assert_equal 'Normal', issue.priority.to_s
382 382 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
383 383 end
384 384 end
385 385
386 386 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
387 387 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
388 388 email.gsub!(/^Project:.+$/, 'Project: invalid')
389 389 end
390 390 assert issue.is_a?(Issue)
391 391 assert !issue.new_record?
392 392 assert_equal 'ecookbook', issue.project.identifier
393 393 end
394 394
395 395 def test_add_issue_with_localized_attributes
396 396 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
397 397 issue = submit_email(
398 398 'ticket_with_localized_attributes.eml',
399 399 :allow_override => 'tracker,category,priority'
400 400 )
401 401 assert issue.is_a?(Issue)
402 402 assert !issue.new_record?
403 403 issue.reload
404 404 assert_equal 'New ticket on a given project', issue.subject
405 405 assert_equal User.find_by_login('jsmith'), issue.author
406 406 assert_equal Project.find(2), issue.project
407 407 assert_equal 'Feature request', issue.tracker.to_s
408 408 assert_equal 'Stock management', issue.category.to_s
409 409 assert_equal 'Urgent', issue.priority.to_s
410 410 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
411 411 end
412 412
413 413 def test_add_issue_with_japanese_keywords
414 414 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
415 415 tracker = Tracker.generate!(:name => ja_dev)
416 416 Project.find(1).trackers << tracker
417 417 issue = submit_email(
418 418 'japanese_keywords_iso_2022_jp.eml',
419 419 :issue => {:project => 'ecookbook'},
420 420 :allow_override => 'tracker'
421 421 )
422 422 assert_kind_of Issue, issue
423 423 assert_equal tracker, issue.tracker
424 424 end
425 425
426 426 def test_add_issue_from_apple_mail
427 427 issue = submit_email(
428 428 'apple_mail_with_attachment.eml',
429 429 :issue => {:project => 'ecookbook'}
430 430 )
431 431 assert_kind_of Issue, issue
432 432 assert_equal 1, issue.attachments.size
433 433
434 434 attachment = issue.attachments.first
435 435 assert_equal 'paella.jpg', attachment.filename
436 436 assert_equal 10790, attachment.filesize
437 437 assert File.exist?(attachment.diskfile)
438 438 assert_equal 10790, File.size(attachment.diskfile)
439 439 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
440 440 end
441 441
442 442 def test_thunderbird_with_attachment_ja
443 443 issue = submit_email(
444 444 'thunderbird_with_attachment_ja.eml',
445 445 :issue => {:project => 'ecookbook'}
446 446 )
447 447 assert_kind_of Issue, issue
448 448 assert_equal 1, issue.attachments.size
449 449 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
450 450 attachment = issue.attachments.first
451 451 assert_equal ja, attachment.filename
452 452 assert_equal 5, attachment.filesize
453 453 assert File.exist?(attachment.diskfile)
454 454 assert_equal 5, File.size(attachment.diskfile)
455 455 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
456 456 end
457 457
458 458 def test_gmail_with_attachment_ja
459 459 issue = submit_email(
460 460 'gmail_with_attachment_ja.eml',
461 461 :issue => {:project => 'ecookbook'}
462 462 )
463 463 assert_kind_of Issue, issue
464 464 assert_equal 1, issue.attachments.size
465 465 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
466 466 attachment = issue.attachments.first
467 467 assert_equal ja, attachment.filename
468 468 assert_equal 5, attachment.filesize
469 469 assert File.exist?(attachment.diskfile)
470 470 assert_equal 5, File.size(attachment.diskfile)
471 471 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
472 472 end
473 473
474 474 def test_thunderbird_with_attachment_latin1
475 475 issue = submit_email(
476 476 'thunderbird_with_attachment_iso-8859-1.eml',
477 477 :issue => {:project => 'ecookbook'}
478 478 )
479 479 assert_kind_of Issue, issue
480 480 assert_equal 1, issue.attachments.size
481 481 u = "".force_encoding('UTF-8')
482 482 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
483 483 11.times { u << u1 }
484 484 attachment = issue.attachments.first
485 485 assert_equal "#{u}.png", attachment.filename
486 486 assert_equal 130, attachment.filesize
487 487 assert File.exist?(attachment.diskfile)
488 488 assert_equal 130, File.size(attachment.diskfile)
489 489 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
490 490 end
491 491
492 492 def test_gmail_with_attachment_latin1
493 493 issue = submit_email(
494 494 'gmail_with_attachment_iso-8859-1.eml',
495 495 :issue => {:project => 'ecookbook'}
496 496 )
497 497 assert_kind_of Issue, issue
498 498 assert_equal 1, issue.attachments.size
499 499 u = "".force_encoding('UTF-8')
500 500 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
501 501 11.times { u << u1 }
502 502 attachment = issue.attachments.first
503 503 assert_equal "#{u}.txt", attachment.filename
504 504 assert_equal 5, attachment.filesize
505 505 assert File.exist?(attachment.diskfile)
506 506 assert_equal 5, File.size(attachment.diskfile)
507 507 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
508 508 end
509 509
510 510 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
511 511 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
512 512 assert_include 'first', issue.description
513 513 assert_include 'second', issue.description
514 514 assert_include 'third', issue.description
515 515 end
516 516
517 517 def test_attachment_text_part_should_be_added_as_issue_attachment
518 518 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
519 519 assert_not_include 'Plain text attachment', issue.description
520 520 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
521 521 assert_not_nil attachment
522 522 assert_include 'Plain text attachment', File.read(attachment.diskfile)
523 523 end
524 524
525 525 def test_add_issue_with_iso_8859_1_subject
526 526 issue = submit_email(
527 527 'subject_as_iso-8859-1.eml',
528 528 :issue => {:project => 'ecookbook'}
529 529 )
530 530 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
531 531 assert_kind_of Issue, issue
532 532 assert_equal str, issue.subject
533 533 end
534 534
535 535 def test_quoted_printable_utf8
536 536 issue = submit_email(
537 537 'quoted_printable_utf8.eml',
538 538 :issue => {:project => 'ecookbook'}
539 539 )
540 540 assert_kind_of Issue, issue
541 541 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
542 542 assert_equal str, issue.description
543 543 end
544 544
545 545 def test_gmail_iso8859_2
546 546 issue = submit_email(
547 547 'gmail-iso8859-2.eml',
548 548 :issue => {:project => 'ecookbook'}
549 549 )
550 550 assert_kind_of Issue, issue
551 551 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
552 552 assert issue.description.include?(str)
553 553 end
554 554
555 555 def test_add_issue_with_japanese_subject
556 556 issue = submit_email(
557 557 'subject_japanese_1.eml',
558 558 :issue => {:project => 'ecookbook'}
559 559 )
560 560 assert_kind_of Issue, issue
561 561 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
562 562 assert_equal ja, issue.subject
563 563 end
564 564
565 565 def test_add_issue_with_korean_body
566 566 # Make sure mail bodies with a charset unknown to Ruby
567 567 # but known to the Mail gem 2.5.4 are handled correctly
568 568 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
569 569 issue = submit_email(
570 570 'body_ks_c_5601-1987.eml',
571 571 :issue => {:project => 'ecookbook'}
572 572 )
573 573 assert_kind_of Issue, issue
574 574 assert_equal kr, issue.description
575 575 end
576 576
577 577 def test_add_issue_with_no_subject_header
578 578 issue = submit_email(
579 579 'no_subject_header.eml',
580 580 :issue => {:project => 'ecookbook'}
581 581 )
582 582 assert_kind_of Issue, issue
583 583 assert_equal '(no subject)', issue.subject
584 584 end
585 585
586 586 def test_add_issue_with_mixed_japanese_subject
587 587 issue = submit_email(
588 588 'subject_japanese_2.eml',
589 589 :issue => {:project => 'ecookbook'}
590 590 )
591 591 assert_kind_of Issue, issue
592 592 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
593 593 assert_equal ja, issue.subject
594 594 end
595 595
596 596 def test_should_ignore_emails_from_locked_users
597 597 User.find(2).lock!
598 598
599 599 MailHandler.any_instance.expects(:dispatch).never
600 600 assert_no_difference 'Issue.count' do
601 601 assert_equal false, submit_email('ticket_on_given_project.eml')
602 602 end
603 603 end
604 604
605 605 def test_should_ignore_emails_from_emission_address
606 606 Role.anonymous.add_permission!(:add_issues)
607 607 assert_no_difference 'User.count' do
608 608 assert_equal false,
609 609 submit_email(
610 610 'ticket_from_emission_address.eml',
611 611 :issue => {:project => 'ecookbook'},
612 612 :unknown_user => 'create'
613 613 )
614 614 end
615 615 end
616 616
617 617 def test_should_ignore_auto_replied_emails
618 618 MailHandler.any_instance.expects(:dispatch).never
619 619 [
620 620 "X-Auto-Response-Suppress: OOF",
621 621 "Auto-Submitted: auto-replied",
622 622 "Auto-Submitted: Auto-Replied",
623 623 "Auto-Submitted: auto-generated"
624 624 ].each do |header|
625 625 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
626 626 raw = header + "\n" + raw
627 627
628 628 assert_no_difference 'Issue.count' do
629 629 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
630 630 end
631 631 end
632 632 end
633 633
634 634 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
635 635 [
636 636 "Auto-Submitted: auto-forwarded"
637 637 ].each do |header|
638 638 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
639 639 raw = header + "\n" + raw
640 640
641 641 assert_difference 'Issue.count', 1 do
642 642 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
643 643 end
644 644 end
645 645 end
646 646
647 647 def test_add_issue_should_send_email_notification
648 648 Setting.notified_events = ['issue_added']
649 649 ActionMailer::Base.deliveries.clear
650 650 # This email contains: 'Project: onlinestore'
651 651 issue = submit_email('ticket_on_given_project.eml')
652 652 assert issue.is_a?(Issue)
653 653 assert_equal 1, ActionMailer::Base.deliveries.size
654 654 end
655 655
656 656 def test_update_issue
657 657 journal = submit_email('ticket_reply.eml')
658 658 assert journal.is_a?(Journal)
659 659 assert_equal User.find_by_login('jsmith'), journal.user
660 660 assert_equal Issue.find(2), journal.journalized
661 661 assert_match /This is reply/, journal.notes
662 662 assert_equal false, journal.private_notes
663 663 assert_equal 'Feature request', journal.issue.tracker.name
664 664 end
665 665
666 666 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
667 667 journal = submit_email('ticket_reply_with_status.eml') do |email|
668 668 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
669 669 end
670 670 assert journal.is_a?(Journal)
671 671 assert_equal Issue.find(2), journal.journalized
672 672 end
673 673
674 674 def test_update_issue_should_accept_issue_id_inside_brackets
675 675 journal = submit_email('ticket_reply_with_status.eml') do |email|
676 676 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
677 677 end
678 678 assert journal.is_a?(Journal)
679 679 assert_equal Issue.find(2), journal.journalized
680 680 end
681 681
682 682 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
683 683 journal = submit_email('ticket_reply_with_status.eml') do |email|
684 684 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
685 685 end
686 686 assert journal.is_a?(Journal)
687 687 assert_equal Issue.find(2), journal.journalized
688 688 end
689 689
690 690 def test_update_issue_with_attribute_changes
691 691 # This email contains: 'Status: Resolved'
692 692 journal = submit_email('ticket_reply_with_status.eml')
693 693 assert journal.is_a?(Journal)
694 694 issue = Issue.find(journal.issue.id)
695 695 assert_equal User.find_by_login('jsmith'), journal.user
696 696 assert_equal Issue.find(2), journal.journalized
697 697 assert_match /This is reply/, journal.notes
698 698 assert_equal 'Feature request', journal.issue.tracker.name
699 699 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
700 700 assert_equal '2010-01-01', issue.start_date.to_s
701 701 assert_equal '2010-12-31', issue.due_date.to_s
702 702 assert_equal User.find_by_login('jsmith'), issue.assigned_to
703 703 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
704 704 # keywords should be removed from the email body
705 705 assert !journal.notes.match(/^Status:/i)
706 706 assert !journal.notes.match(/^Start Date:/i)
707 707 end
708 708
709 709 def test_update_issue_with_attachment
710 710 assert_difference 'Journal.count' do
711 711 assert_difference 'JournalDetail.count' do
712 712 assert_difference 'Attachment.count' do
713 713 assert_no_difference 'Issue.count' do
714 714 journal = submit_email('ticket_with_attachment.eml') do |raw|
715 715 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
716 716 end
717 717 end
718 718 end
719 719 end
720 720 end
721 721 journal = Journal.order('id DESC').first
722 722 assert_equal Issue.find(2), journal.journalized
723 723 assert_equal 1, journal.details.size
724 724
725 725 detail = journal.details.first
726 726 assert_equal 'attachment', detail.property
727 727 assert_equal 'Paella.jpg', detail.value
728 728 end
729 729
730 730 def test_update_issue_should_send_email_notification
731 731 ActionMailer::Base.deliveries.clear
732 732 journal = submit_email('ticket_reply.eml')
733 733 assert journal.is_a?(Journal)
734 734 assert_equal 1, ActionMailer::Base.deliveries.size
735 735 end
736 736
737 737 def test_update_issue_should_not_set_defaults
738 738 journal = submit_email(
739 739 'ticket_reply.eml',
740 740 :issue => {:tracker => 'Support request', :priority => 'High'}
741 741 )
742 742 assert journal.is_a?(Journal)
743 743 assert_match /This is reply/, journal.notes
744 744 assert_equal 'Feature request', journal.issue.tracker.name
745 745 assert_equal 'Normal', journal.issue.priority.name
746 746 end
747 747
748 748 def test_replying_to_a_private_note_should_add_reply_as_private
749 749 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
750 750
751 751 assert_difference 'Journal.count' do
752 752 journal = submit_email('ticket_reply.eml') do |email|
753 753 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
754 754 end
755 755
756 756 assert_kind_of Journal, journal
757 757 assert_match /This is reply/, journal.notes
758 758 assert_equal true, journal.private_notes
759 759 end
760 760 end
761 761
762 762 def test_reply_to_a_message
763 763 m = submit_email('message_reply.eml')
764 764 assert m.is_a?(Message)
765 765 assert !m.new_record?
766 766 m.reload
767 767 assert_equal 'Reply via email', m.subject
768 768 # The email replies to message #2 which is part of the thread of message #1
769 769 assert_equal Message.find(1), m.parent
770 770 end
771 771
772 772 def test_reply_to_a_message_by_subject
773 773 m = submit_email('message_reply_by_subject.eml')
774 774 assert m.is_a?(Message)
775 775 assert !m.new_record?
776 776 m.reload
777 777 assert_equal 'Reply to the first post', m.subject
778 778 assert_equal Message.find(1), m.parent
779 779 end
780 780
781 781 def test_should_strip_tags_of_html_only_emails
782 782 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
783 783 assert issue.is_a?(Issue)
784 784 assert !issue.new_record?
785 785 issue.reload
786 786 assert_equal 'HTML email', issue.subject
787 787 assert_equal 'This is a html-only email.', issue.description
788 788 end
789 789
790 790 test "truncate emails with no setting should add the entire email into the issue" do
791 791 with_settings :mail_handler_body_delimiters => '' do
792 792 issue = submit_email('ticket_on_given_project.eml')
793 793 assert_issue_created(issue)
794 794 assert issue.description.include?('---')
795 795 assert issue.description.include?('This paragraph is after the delimiter')
796 796 end
797 797 end
798 798
799 799 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
800 800 with_settings :mail_handler_body_delimiters => '---' do
801 801 issue = submit_email('ticket_on_given_project.eml')
802 802 assert_issue_created(issue)
803 803 assert issue.description.include?('This paragraph is before delimiters')
804 804 assert issue.description.include?('--- This line starts with a delimiter')
805 805 assert !issue.description.match(/^---$/)
806 806 assert !issue.description.include?('This paragraph is after the delimiter')
807 807 end
808 808 end
809 809
810 810 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
811 811 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
812 812 journal = submit_email('issue_update_with_quoted_reply_above.eml')
813 813 assert journal.is_a?(Journal)
814 814 assert journal.notes.include?('An update to the issue by the sender.')
815 815 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
816 816 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
817 817 end
818 818 end
819 819
820 820 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
821 821 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
822 822 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
823 823 assert journal.is_a?(Journal)
824 824 assert journal.notes.include?('An update to the issue by the sender.')
825 825 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
826 826 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
827 827 end
828 828 end
829 829
830 830 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
831 831 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
832 832 issue = submit_email('ticket_on_given_project.eml')
833 833 assert_issue_created(issue)
834 834 assert issue.description.include?('This paragraph is before delimiters')
835 835 assert !issue.description.include?('BREAK')
836 836 assert !issue.description.include?('This paragraph is between delimiters')
837 837 assert !issue.description.match(/^---$/)
838 838 assert !issue.description.include?('This paragraph is after the delimiter')
839 839 end
840 840 end
841 841
842 842 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
843 843 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
844 844 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
845 845 assert issue.is_a?(Issue)
846 846 assert !issue.new_record?
847 847 assert_equal 0, issue.reload.attachments.size
848 848 end
849 849 end
850 850
851 851 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
852 852 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
853 853 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
854 854 assert issue.is_a?(Issue)
855 855 assert !issue.new_record?
856 856 assert_equal 1, issue.reload.attachments.size
857 857 end
858 858 end
859 859
860 860 def test_email_with_long_subject_line
861 861 issue = submit_email('ticket_with_long_subject.eml')
862 862 assert issue.is_a?(Issue)
863 863 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]
864 864 end
865 865
866 866 def test_first_keyword_should_be_matched
867 867 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
868 868 assert issue.is_a?(Issue)
869 869 assert_equal 'High', issue.priority.name
870 870 end
871 871
872 872 def test_keyword_after_delimiter_should_be_ignored
873 873 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
874 874 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
875 875 assert issue.is_a?(Issue)
876 876 assert_equal 'Normal', issue.priority.name
877 877 end
878 878 end
879 879
880 880 def test_new_user_from_attributes_should_return_valid_user
881 881 to_test = {
882 882 # [address, name] => [login, firstname, lastname]
883 883 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
884 884 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
885 885 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
886 886 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
887 887 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
888 888 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
889 889 }
890 890
891 891 to_test.each do |attrs, expected|
892 892 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
893 893
894 894 assert user.valid?, user.errors.full_messages.to_s
895 895 assert_equal attrs.first, user.mail
896 896 assert_equal expected[0], user.login
897 897 assert_equal expected[1], user.firstname
898 898 assert_equal expected[2], user.lastname
899 899 assert_equal 'only_my_events', user.mail_notification
900 900 end
901 901 end
902 902
903 903 def test_new_user_from_attributes_should_use_default_login_if_invalid
904 904 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
905 905 assert user.valid?
906 906 assert user.login =~ /^user[a-f0-9]+$/
907 907 assert_equal 'foo+bar@example.net', user.mail
908 908 end
909 909
910 910 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
911 911 assert_difference 'User.count' do
912 912 issue = submit_email(
913 913 'fullname_of_sender_as_utf8_encoded.eml',
914 914 :issue => {:project => 'ecookbook'},
915 915 :unknown_user => 'create'
916 916 )
917 917 end
918 918 user = User.order('id DESC').first
919 919 assert_equal "foo@example.org", user.mail
920 920 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
921 921 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
922 922 assert_equal str1, user.firstname
923 923 assert_equal str2, user.lastname
924 924 end
925 925
926 926 def test_extract_options_from_env_should_return_options
927 927 options = MailHandler.extract_options_from_env({
928 928 'tracker' => 'defect',
929 929 'project' => 'foo',
930 930 'unknown_user' => 'create'
931 931 })
932 932
933 933 assert_equal({
934 934 :issue => {:tracker => 'defect', :project => 'foo'},
935 935 :unknown_user => 'create'
936 936 }, options)
937 937 end
938 938
939 939 private
940 940
941 941 def submit_email(filename, options={})
942 942 raw = IO.read(File.join(FIXTURES_PATH, filename))
943 943 yield raw if block_given?
944 944 MailHandler.receive(raw, options)
945 945 end
946 946
947 947 def assert_issue_created(issue)
948 948 assert issue.is_a?(Issue)
949 949 assert !issue.new_record?
950 950 issue.reload
951 951 end
952 952 end
@@ -1,954 +1,950
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class ProjectTest < ActiveSupport::TestCase
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :journals, :journal_details,
23 23 :enumerations, :users, :issue_categories,
24 24 :projects_trackers,
25 25 :custom_fields,
26 26 :custom_fields_projects,
27 27 :custom_fields_trackers,
28 28 :custom_values,
29 29 :roles,
30 30 :member_roles,
31 31 :members,
32 32 :enabled_modules,
33 33 :versions,
34 34 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
35 35 :groups_users,
36 36 :boards, :messages,
37 37 :repositories,
38 38 :news, :comments,
39 39 :documents,
40 40 :workflows
41 41
42 42 def setup
43 43 @ecookbook = Project.find(1)
44 44 @ecookbook_sub1 = Project.find(3)
45 45 set_tmp_attachments_directory
46 46 User.current = nil
47 47 end
48 48
49 49 def test_truth
50 50 assert_kind_of Project, @ecookbook
51 51 assert_equal "eCookbook", @ecookbook.name
52 52 end
53 53
54 54 def test_default_attributes
55 55 with_settings :default_projects_public => '1' do
56 56 assert_equal true, Project.new.is_public
57 57 assert_equal false, Project.new(:is_public => false).is_public
58 58 end
59 59
60 60 with_settings :default_projects_public => '0' do
61 61 assert_equal false, Project.new.is_public
62 62 assert_equal true, Project.new(:is_public => true).is_public
63 63 end
64 64
65 65 with_settings :sequential_project_identifiers => '1' do
66 66 assert !Project.new.identifier.blank?
67 67 assert Project.new(:identifier => '').identifier.blank?
68 68 end
69 69
70 70 with_settings :sequential_project_identifiers => '0' do
71 71 assert Project.new.identifier.blank?
72 72 assert !Project.new(:identifier => 'test').blank?
73 73 end
74 74
75 75 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
76 76 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
77 77 end
78 78 end
79 79
80 80 def test_default_trackers_should_match_default_tracker_ids_setting
81 81 with_settings :default_projects_tracker_ids => ['1', '3'] do
82 82 assert_equal Tracker.find(1, 3).sort, Project.new.trackers.sort
83 83 end
84 84 end
85 85
86 86 def test_default_trackers_should_be_all_trackers_with_blank_setting
87 87 with_settings :default_projects_tracker_ids => nil do
88 88 assert_equal Tracker.all.sort, Project.new.trackers.sort
89 89 end
90 90 end
91 91
92 92 def test_default_trackers_should_be_empty_with_empty_setting
93 93 with_settings :default_projects_tracker_ids => [] do
94 94 assert_equal [], Project.new.trackers
95 95 end
96 96 end
97 97
98 98 def test_default_trackers_should_not_replace_initialized_trackers
99 99 with_settings :default_projects_tracker_ids => ['1', '3'] do
100 100 assert_equal Tracker.find(1, 2).sort, Project.new(:tracker_ids => [1, 2]).trackers.sort
101 101 end
102 102 end
103 103
104 104 def test_update
105 105 assert_equal "eCookbook", @ecookbook.name
106 106 @ecookbook.name = "eCook"
107 107 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
108 108 @ecookbook.reload
109 109 assert_equal "eCook", @ecookbook.name
110 110 end
111 111
112 112 def test_validate_identifier
113 113 to_test = {"abc" => true,
114 114 "ab12" => true,
115 115 "ab-12" => true,
116 116 "ab_12" => true,
117 117 "12" => false,
118 118 "new" => false}
119 119
120 120 to_test.each do |identifier, valid|
121 121 p = Project.new
122 122 p.identifier = identifier
123 123 p.valid?
124 124 if valid
125 125 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
126 126 else
127 127 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
128 128 end
129 129 end
130 130 end
131 131
132 132 def test_identifier_should_not_be_frozen_for_a_new_project
133 133 assert_equal false, Project.new.identifier_frozen?
134 134 end
135 135
136 136 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
137 137 Project.where(:id => 1).update_all(["identifier = ''"])
138 138 assert_equal false, Project.find(1).identifier_frozen?
139 139 end
140 140
141 141 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
142 142 assert_equal true, Project.find(1).identifier_frozen?
143 143 end
144 144
145 145 def test_members_should_be_active_users
146 146 Project.all.each do |project|
147 147 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
148 148 end
149 149 end
150 150
151 151 def test_users_should_be_active_users
152 152 Project.all.each do |project|
153 153 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
154 154 end
155 155 end
156 156
157 157 def test_open_scope_on_issues_association
158 158 assert_kind_of Issue, Project.find(1).issues.open.first
159 159 end
160 160
161 161 def test_archive
162 162 user = @ecookbook.members.first.user
163 163 @ecookbook.archive
164 164 @ecookbook.reload
165 165
166 166 assert !@ecookbook.active?
167 167 assert @ecookbook.archived?
168 168 assert !user.projects.include?(@ecookbook)
169 169 # Subproject are also archived
170 170 assert !@ecookbook.children.empty?
171 171 assert @ecookbook.descendants.active.empty?
172 172 end
173 173
174 174 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
175 175 # Assign an issue of a project to a version of a child project
176 176 Issue.find(4).update_attribute :fixed_version_id, 4
177 177
178 178 assert_no_difference "Project.where(:status => Project::STATUS_ARCHIVED).count" do
179 179 assert_equal false, @ecookbook.archive
180 180 end
181 181 @ecookbook.reload
182 182 assert @ecookbook.active?
183 183 end
184 184
185 185 def test_unarchive
186 186 user = @ecookbook.members.first.user
187 187 @ecookbook.archive
188 188 # A subproject of an archived project can not be unarchived
189 189 assert !@ecookbook_sub1.unarchive
190 190
191 191 # Unarchive project
192 192 assert @ecookbook.unarchive
193 193 @ecookbook.reload
194 194 assert @ecookbook.active?
195 195 assert !@ecookbook.archived?
196 196 assert user.projects.include?(@ecookbook)
197 197 # Subproject can now be unarchived
198 198 @ecookbook_sub1.reload
199 199 assert @ecookbook_sub1.unarchive
200 200 end
201 201
202 202 def test_destroy
203 203 # 2 active members
204 204 assert_equal 2, @ecookbook.members.size
205 205 # and 1 is locked
206 206 assert_equal 3, Member.where(:project_id => @ecookbook.id).count
207 207 # some boards
208 208 assert @ecookbook.boards.any?
209 209
210 210 @ecookbook.destroy
211 211 # make sure that the project non longer exists
212 212 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
213 213 # make sure related data was removed
214 214 assert_nil Member.where(:project_id => @ecookbook.id).first
215 215 assert_nil Board.where(:project_id => @ecookbook.id).first
216 216 assert_nil Issue.where(:project_id => @ecookbook.id).first
217 217 end
218 218
219 219 def test_destroy_should_destroy_subtasks
220 220 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
221 221 issues[0].update_attribute :parent_issue_id, issues[1].id
222 222 issues[2].update_attribute :parent_issue_id, issues[1].id
223 223 assert_equal 2, issues[1].children.count
224 224
225 225 assert_nothing_raised do
226 226 Project.find(1).destroy
227 227 end
228 228 assert_equal 0, Issue.where(:id => issues.map(&:id)).count
229 229 end
230 230
231 231 def test_destroying_root_projects_should_clear_data
232 232 Project.roots.each do |root|
233 233 root.destroy
234 234 end
235 235
236 236 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
237 237 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
238 238 assert_equal 0, MemberRole.count
239 239 assert_equal 0, Issue.count
240 240 assert_equal 0, Journal.count
241 241 assert_equal 0, JournalDetail.count
242 242 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
243 243 assert_equal 0, EnabledModule.count
244 244 assert_equal 0, IssueCategory.count
245 245 assert_equal 0, IssueRelation.count
246 246 assert_equal 0, Board.count
247 247 assert_equal 0, Message.count
248 248 assert_equal 0, News.count
249 249 assert_equal 0, Query.where("project_id IS NOT NULL").count
250 250 assert_equal 0, Repository.count
251 251 assert_equal 0, Changeset.count
252 252 assert_equal 0, Change.count
253 253 assert_equal 0, Comment.count
254 254 assert_equal 0, TimeEntry.count
255 255 assert_equal 0, Version.count
256 256 assert_equal 0, Watcher.count
257 257 assert_equal 0, Wiki.count
258 258 assert_equal 0, WikiPage.count
259 259 assert_equal 0, WikiContent.count
260 260 assert_equal 0, WikiContent::Version.count
261 261 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").count
262 262 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").count
263 263 assert_equal 0, CustomValue.where(:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']).count
264 264 end
265 265
266 266 def test_destroy_should_delete_time_entries_custom_values
267 267 project = Project.generate!
268 268 time_entry = TimeEntry.generate!(:project => project, :custom_field_values => {10 => '1'})
269 269
270 270 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
271 271 assert project.destroy
272 272 end
273 273 end
274 274
275 275 def test_move_an_orphan_project_to_a_root_project
276 276 sub = Project.find(2)
277 277 sub.set_parent! @ecookbook
278 278 assert_equal @ecookbook.id, sub.parent.id
279 279 @ecookbook.reload
280 280 assert_equal 4, @ecookbook.children.size
281 281 end
282 282
283 283 def test_move_an_orphan_project_to_a_subproject
284 284 sub = Project.find(2)
285 285 assert sub.set_parent!(@ecookbook_sub1)
286 286 end
287 287
288 288 def test_move_a_root_project_to_a_project
289 289 sub = @ecookbook
290 290 assert sub.set_parent!(Project.find(2))
291 291 end
292 292
293 293 def test_should_not_move_a_project_to_its_children
294 294 sub = @ecookbook
295 295 assert !(sub.set_parent!(Project.find(3)))
296 296 end
297 297
298 298 def test_set_parent_should_add_roots_in_alphabetical_order
299 299 ProjectCustomField.delete_all
300 300 Project.delete_all
301 301 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
302 302 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
303 303 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
304 304 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
305 305
306 306 assert_equal 4, Project.count
307 307 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
308 308 end
309 309
310 310 def test_set_parent_should_add_children_in_alphabetical_order
311 311 ProjectCustomField.delete_all
312 312 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
313 313 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
314 314 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
315 315 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
316 316 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
317 317
318 318 parent.reload
319 319 assert_equal 4, parent.children.size
320 320 assert_equal parent.children.sort_by(&:name), parent.children.to_a
321 321 end
322 322
323 323 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
324 324 # Parent issue with a hierarchy project's fixed version
325 325 parent_issue = Issue.find(1)
326 326 parent_issue.update_attribute(:fixed_version_id, 4)
327 327 parent_issue.reload
328 328 assert_equal 4, parent_issue.fixed_version_id
329 329
330 330 # Should keep fixed versions for the issues
331 331 issue_with_local_fixed_version = Issue.find(5)
332 332 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
333 333 issue_with_local_fixed_version.reload
334 334 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
335 335
336 336 # Local issue with hierarchy fixed_version
337 337 issue_with_hierarchy_fixed_version = Issue.find(13)
338 338 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
339 339 issue_with_hierarchy_fixed_version.reload
340 340 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
341 341
342 342 # Move project out of the issue's hierarchy
343 343 moved_project = Project.find(3)
344 344 moved_project.set_parent!(Project.find(2))
345 345 parent_issue.reload
346 346 issue_with_local_fixed_version.reload
347 347 issue_with_hierarchy_fixed_version.reload
348 348
349 349 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
350 350 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"
351 351 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
352 352 end
353 353
354 354 def test_parent
355 355 p = Project.find(6).parent
356 356 assert p.is_a?(Project)
357 357 assert_equal 5, p.id
358 358 end
359 359
360 360 def test_ancestors
361 361 a = Project.find(6).ancestors
362 362 assert a.first.is_a?(Project)
363 363 assert_equal [1, 5], a.collect(&:id)
364 364 end
365 365
366 366 def test_root
367 367 r = Project.find(6).root
368 368 assert r.is_a?(Project)
369 369 assert_equal 1, r.id
370 370 end
371 371
372 372 def test_children
373 373 c = Project.find(1).children
374 374 assert c.first.is_a?(Project)
375 375 assert_equal [5, 3, 4], c.collect(&:id)
376 376 end
377 377
378 378 def test_descendants
379 379 d = Project.find(1).descendants
380 380 assert d.first.is_a?(Project)
381 381 assert_equal [5, 6, 3, 4], d.collect(&:id)
382 382 end
383 383
384 384 def test_allowed_parents_should_be_empty_for_non_member_user
385 385 Role.non_member.add_permission!(:add_project)
386 386 user = User.find(9)
387 387 assert user.memberships.empty?
388 388 User.current = user
389 389 assert Project.new.allowed_parents.compact.empty?
390 390 end
391 391
392 392 def test_allowed_parents_with_add_subprojects_permission
393 393 Role.find(1).remove_permission!(:add_project)
394 394 Role.find(1).add_permission!(:add_subprojects)
395 395 User.current = User.find(2)
396 396 # new project
397 397 assert !Project.new.allowed_parents.include?(nil)
398 398 assert Project.new.allowed_parents.include?(Project.find(1))
399 399 # existing root project
400 400 assert Project.find(1).allowed_parents.include?(nil)
401 401 # existing child
402 402 assert Project.find(3).allowed_parents.include?(Project.find(1))
403 403 assert !Project.find(3).allowed_parents.include?(nil)
404 404 end
405 405
406 406 def test_allowed_parents_with_add_project_permission
407 407 Role.find(1).add_permission!(:add_project)
408 408 Role.find(1).remove_permission!(:add_subprojects)
409 409 User.current = User.find(2)
410 410 # new project
411 411 assert Project.new.allowed_parents.include?(nil)
412 412 assert !Project.new.allowed_parents.include?(Project.find(1))
413 413 # existing root project
414 414 assert Project.find(1).allowed_parents.include?(nil)
415 415 # existing child
416 416 assert Project.find(3).allowed_parents.include?(Project.find(1))
417 417 assert Project.find(3).allowed_parents.include?(nil)
418 418 end
419 419
420 420 def test_allowed_parents_with_add_project_and_subprojects_permission
421 421 Role.find(1).add_permission!(:add_project)
422 422 Role.find(1).add_permission!(:add_subprojects)
423 423 User.current = User.find(2)
424 424 # new project
425 425 assert Project.new.allowed_parents.include?(nil)
426 426 assert Project.new.allowed_parents.include?(Project.find(1))
427 427 # existing root project
428 428 assert Project.find(1).allowed_parents.include?(nil)
429 429 # existing child
430 430 assert Project.find(3).allowed_parents.include?(Project.find(1))
431 431 assert Project.find(3).allowed_parents.include?(nil)
432 432 end
433 433
434 434 def test_users_by_role
435 435 users_by_role = Project.find(1).users_by_role
436 436 assert_kind_of Hash, users_by_role
437 437 role = Role.find(1)
438 438 assert_kind_of Array, users_by_role[role]
439 439 assert users_by_role[role].include?(User.find(2))
440 440 end
441 441
442 442 def test_rolled_up_trackers
443 443 parent = Project.find(1)
444 444 parent.trackers = Tracker.find([1,2])
445 445 child = parent.children.find(3)
446 446
447 447 assert_equal [1, 2], parent.tracker_ids
448 448 assert_equal [2, 3], child.trackers.collect(&:id)
449 449
450 450 assert_kind_of Tracker, parent.rolled_up_trackers.first
451 451 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
452 452
453 453 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
454 454 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
455 455 end
456 456
457 457 def test_rolled_up_trackers_should_ignore_archived_subprojects
458 458 parent = Project.find(1)
459 459 parent.trackers = Tracker.find([1,2])
460 460 child = parent.children.find(3)
461 461 child.trackers = Tracker.find([1,3])
462 462 parent.children.each(&:archive)
463 463
464 464 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
465 465 end
466 466
467 467 test "#rolled_up_trackers should ignore projects with issue_tracking module disabled" do
468 468 parent = Project.generate!
469 469 parent.trackers = Tracker.find([1, 2])
470 470 child = Project.generate_with_parent!(parent)
471 471 child.trackers = Tracker.find([2, 3])
472 472
473 473 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id).sort
474 474
475 475 assert child.disable_module!(:issue_tracking)
476 476 parent.reload
477 477 assert_equal [1, 2], parent.rolled_up_trackers.collect(&:id).sort
478 478 end
479 479
480 480 test "#rolled_up_versions should include the versions for the current project" do
481 481 project = Project.generate!
482 482 parent_version_1 = Version.generate!(:project => project)
483 483 parent_version_2 = Version.generate!(:project => project)
484 assert_same_elements [parent_version_1, parent_version_2], project.rolled_up_versions
484 assert_equal [parent_version_1, parent_version_2].sort,
485 project.rolled_up_versions.sort
485 486 end
486 487
487 488 test "#rolled_up_versions should include versions for a subproject" do
488 489 project = Project.generate!
489 490 parent_version_1 = Version.generate!(:project => project)
490 491 parent_version_2 = Version.generate!(:project => project)
491 492 subproject = Project.generate_with_parent!(project)
492 493 subproject_version = Version.generate!(:project => subproject)
493 494
494 assert_same_elements [
495 parent_version_1,
496 parent_version_2,
497 subproject_version
498 ], project.rolled_up_versions
495 assert_equal [parent_version_1, parent_version_2, subproject_version].sort,
496 project.rolled_up_versions.sort
499 497 end
500 498
501 499 test "#rolled_up_versions should include versions for a sub-subproject" do
502 500 project = Project.generate!
503 501 parent_version_1 = Version.generate!(:project => project)
504 502 parent_version_2 = Version.generate!(:project => project)
505 503 subproject = Project.generate_with_parent!(project)
506 504 sub_subproject = Project.generate_with_parent!(subproject)
507 505 sub_subproject_version = Version.generate!(:project => sub_subproject)
508 506 project.reload
509 507
510 assert_same_elements [
511 parent_version_1,
512 parent_version_2,
513 sub_subproject_version
514 ], project.rolled_up_versions
508 assert_equal [parent_version_1, parent_version_2, sub_subproject_version].sort,
509 project.rolled_up_versions.sort
515 510 end
516 511
517 512 test "#rolled_up_versions should only check active projects" do
518 513 project = Project.generate!
519 514 parent_version_1 = Version.generate!(:project => project)
520 515 parent_version_2 = Version.generate!(:project => project)
521 516 subproject = Project.generate_with_parent!(project)
522 517 subproject_version = Version.generate!(:project => subproject)
523 518 assert subproject.archive
524 519 project.reload
525 520
526 521 assert !subproject.active?
527 assert_same_elements [parent_version_1, parent_version_2], project.rolled_up_versions
522 assert_equal [parent_version_1, parent_version_2].sort,
523 project.rolled_up_versions.sort
528 524 end
529 525
530 526 def test_shared_versions_none_sharing
531 527 p = Project.find(5)
532 528 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
533 529 assert p.shared_versions.include?(v)
534 530 assert !p.children.first.shared_versions.include?(v)
535 531 assert !p.root.shared_versions.include?(v)
536 532 assert !p.siblings.first.shared_versions.include?(v)
537 533 assert !p.root.siblings.first.shared_versions.include?(v)
538 534 end
539 535
540 536 def test_shared_versions_descendants_sharing
541 537 p = Project.find(5)
542 538 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
543 539 assert p.shared_versions.include?(v)
544 540 assert p.children.first.shared_versions.include?(v)
545 541 assert !p.root.shared_versions.include?(v)
546 542 assert !p.siblings.first.shared_versions.include?(v)
547 543 assert !p.root.siblings.first.shared_versions.include?(v)
548 544 end
549 545
550 546 def test_shared_versions_hierarchy_sharing
551 547 p = Project.find(5)
552 548 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
553 549 assert p.shared_versions.include?(v)
554 550 assert p.children.first.shared_versions.include?(v)
555 551 assert p.root.shared_versions.include?(v)
556 552 assert !p.siblings.first.shared_versions.include?(v)
557 553 assert !p.root.siblings.first.shared_versions.include?(v)
558 554 end
559 555
560 556 def test_shared_versions_tree_sharing
561 557 p = Project.find(5)
562 558 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
563 559 assert p.shared_versions.include?(v)
564 560 assert p.children.first.shared_versions.include?(v)
565 561 assert p.root.shared_versions.include?(v)
566 562 assert p.siblings.first.shared_versions.include?(v)
567 563 assert !p.root.siblings.first.shared_versions.include?(v)
568 564 end
569 565
570 566 def test_shared_versions_system_sharing
571 567 p = Project.find(5)
572 568 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
573 569 assert p.shared_versions.include?(v)
574 570 assert p.children.first.shared_versions.include?(v)
575 571 assert p.root.shared_versions.include?(v)
576 572 assert p.siblings.first.shared_versions.include?(v)
577 573 assert p.root.siblings.first.shared_versions.include?(v)
578 574 end
579 575
580 576 def test_shared_versions
581 577 parent = Project.find(1)
582 578 child = parent.children.find(3)
583 579 private_child = parent.children.find(5)
584 580
585 581 assert_equal [1,2,3], parent.version_ids.sort
586 582 assert_equal [4], child.version_ids
587 583 assert_equal [6], private_child.version_ids
588 584 assert_equal [7], Version.where(:sharing => 'system').collect(&:id)
589 585
590 586 assert_equal 6, parent.shared_versions.size
591 587 parent.shared_versions.each do |version|
592 588 assert_kind_of Version, version
593 589 end
594 590
595 591 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
596 592 end
597 593
598 594 def test_shared_versions_should_ignore_archived_subprojects
599 595 parent = Project.find(1)
600 596 child = parent.children.find(3)
601 597 child.archive
602 598 parent.reload
603 599
604 600 assert_equal [1,2,3], parent.version_ids.sort
605 601 assert_equal [4], child.version_ids
606 602 assert !parent.shared_versions.collect(&:id).include?(4)
607 603 end
608 604
609 605 def test_shared_versions_visible_to_user
610 606 user = User.find(3)
611 607 parent = Project.find(1)
612 608 child = parent.children.find(5)
613 609
614 610 assert_equal [1,2,3], parent.version_ids.sort
615 611 assert_equal [6], child.version_ids
616 612
617 613 versions = parent.shared_versions.visible(user)
618 614
619 615 assert_equal 4, versions.size
620 616 versions.each do |version|
621 617 assert_kind_of Version, version
622 618 end
623 619
624 620 assert !versions.collect(&:id).include?(6)
625 621 end
626 622
627 623 def test_shared_versions_for_new_project_should_include_system_shared_versions
628 624 p = Project.find(5)
629 625 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
630 626
631 627 assert_include v, Project.new.shared_versions
632 628 end
633 629
634 630 def test_next_identifier
635 631 ProjectCustomField.delete_all
636 632 Project.create!(:name => 'last', :identifier => 'p2008040')
637 633 assert_equal 'p2008041', Project.next_identifier
638 634 end
639 635
640 636 def test_next_identifier_first_project
641 637 Project.delete_all
642 638 assert_nil Project.next_identifier
643 639 end
644 640
645 641 def test_enabled_module_names
646 642 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
647 643 project = Project.new
648 644
649 645 project.enabled_module_names = %w(issue_tracking news)
650 646 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
651 647 end
652 648 end
653 649
654 650 test "enabled_modules should define module by names and preserve ids" do
655 651 @project = Project.find(1)
656 652 # Remove one module
657 653 modules = @project.enabled_modules.slice(0..-2)
658 654 assert modules.any?
659 655 assert_difference 'EnabledModule.count', -1 do
660 656 @project.enabled_module_names = modules.collect(&:name)
661 657 end
662 658 @project.reload
663 659 # Ids should be preserved
664 660 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
665 661 end
666 662
667 663 test "enabled_modules should enable a module" do
668 664 @project = Project.find(1)
669 665 @project.enabled_module_names = []
670 666 @project.reload
671 667 assert_equal [], @project.enabled_module_names
672 668 #with string
673 669 @project.enable_module!("issue_tracking")
674 670 assert_equal ["issue_tracking"], @project.enabled_module_names
675 671 #with symbol
676 672 @project.enable_module!(:gantt)
677 673 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
678 674 #don't add a module twice
679 675 @project.enable_module!("issue_tracking")
680 676 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
681 677 end
682 678
683 679 test "enabled_modules should disable a module" do
684 680 @project = Project.find(1)
685 681 #with string
686 682 assert @project.enabled_module_names.include?("issue_tracking")
687 683 @project.disable_module!("issue_tracking")
688 684 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
689 685 #with symbol
690 686 assert @project.enabled_module_names.include?("gantt")
691 687 @project.disable_module!(:gantt)
692 688 assert ! @project.reload.enabled_module_names.include?("gantt")
693 689 #with EnabledModule object
694 690 first_module = @project.enabled_modules.first
695 691 @project.disable_module!(first_module)
696 692 assert ! @project.reload.enabled_module_names.include?(first_module.name)
697 693 end
698 694
699 695 def test_enabled_module_names_should_not_recreate_enabled_modules
700 696 project = Project.find(1)
701 697 # Remove one module
702 698 modules = project.enabled_modules.slice(0..-2)
703 699 assert modules.any?
704 700 assert_difference 'EnabledModule.count', -1 do
705 701 project.enabled_module_names = modules.collect(&:name)
706 702 end
707 703 project.reload
708 704 # Ids should be preserved
709 705 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
710 706 end
711 707
712 708 def test_copy_from_existing_project
713 709 source_project = Project.find(1)
714 710 copied_project = Project.copy_from(1)
715 711
716 712 assert copied_project
717 713 # Cleared attributes
718 714 assert copied_project.id.blank?
719 715 assert copied_project.name.blank?
720 716 assert copied_project.identifier.blank?
721 717
722 718 # Duplicated attributes
723 719 assert_equal source_project.description, copied_project.description
724 720 assert_equal source_project.enabled_modules, copied_project.enabled_modules
725 721 assert_equal source_project.trackers, copied_project.trackers
726 722
727 723 # Default attributes
728 724 assert_equal 1, copied_project.status
729 725 end
730 726
731 727 def test_activities_should_use_the_system_activities
732 728 project = Project.find(1)
733 729 assert_equal project.activities.to_a, TimeEntryActivity.where(:active => true).to_a
734 730 assert_kind_of ActiveRecord::Relation, project.activities
735 731 end
736 732
737 733
738 734 def test_activities_should_use_the_project_specific_activities
739 735 project = Project.find(1)
740 736 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
741 737 assert overridden_activity.save!
742 738
743 739 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
744 740 assert_kind_of ActiveRecord::Relation, project.activities
745 741 end
746 742
747 743 def test_activities_should_not_include_the_inactive_project_specific_activities
748 744 project = Project.find(1)
749 745 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
750 746 assert overridden_activity.save!
751 747
752 748 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
753 749 end
754 750
755 751 def test_activities_should_not_include_project_specific_activities_from_other_projects
756 752 project = Project.find(1)
757 753 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
758 754 assert overridden_activity.save!
759 755
760 756 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
761 757 end
762 758
763 759 def test_activities_should_handle_nils
764 760 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.first})
765 761 TimeEntryActivity.delete_all
766 762
767 763 # No activities
768 764 project = Project.find(1)
769 765 assert project.activities.empty?
770 766
771 767 # No system, one overridden
772 768 assert overridden_activity.save!
773 769 project.reload
774 770 assert_equal [overridden_activity], project.activities
775 771 end
776 772
777 773 def test_activities_should_override_system_activities_with_project_activities
778 774 project = Project.find(1)
779 775 parent_activity = TimeEntryActivity.first
780 776 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
781 777 assert overridden_activity.save!
782 778
783 779 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
784 780 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
785 781 end
786 782
787 783 def test_activities_should_include_inactive_activities_if_specified
788 784 project = Project.find(1)
789 785 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
790 786 assert overridden_activity.save!
791 787
792 788 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
793 789 end
794 790
795 791 test 'activities should not include active System activities if the project has an override that is inactive' do
796 792 project = Project.find(1)
797 793 system_activity = TimeEntryActivity.find_by_name('Design')
798 794 assert system_activity.active?
799 795 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
800 796 assert overridden_activity.save!
801 797
802 798 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
803 799 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
804 800 end
805 801
806 802 def test_close_completed_versions
807 803 Version.update_all("status = 'open'")
808 804 project = Project.find(1)
809 805 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
810 806 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
811 807 project.close_completed_versions
812 808 project.reload
813 809 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
814 810 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
815 811 end
816 812
817 813 test "#start_date should be nil if there are no issues on the project" do
818 814 project = Project.generate!
819 815 assert_nil project.start_date
820 816 end
821 817
822 818 test "#start_date should be nil when issues have no start date" do
823 819 project = Project.generate!
824 820 project.trackers << Tracker.generate!
825 821 early = 7.days.ago.to_date
826 822 Issue.generate!(:project => project, :start_date => nil)
827 823
828 824 assert_nil project.start_date
829 825 end
830 826
831 827 test "#start_date should be the earliest start date of it's issues" do
832 828 project = Project.generate!
833 829 project.trackers << Tracker.generate!
834 830 early = 7.days.ago.to_date
835 831 Issue.generate!(:project => project, :start_date => Date.today)
836 832 Issue.generate!(:project => project, :start_date => early)
837 833
838 834 assert_equal early, project.start_date
839 835 end
840 836
841 837 test "#due_date should be nil if there are no issues on the project" do
842 838 project = Project.generate!
843 839 assert_nil project.due_date
844 840 end
845 841
846 842 test "#due_date should be nil if there are no issues with due dates" do
847 843 project = Project.generate!
848 844 project.trackers << Tracker.generate!
849 845 Issue.generate!(:project => project, :due_date => nil)
850 846
851 847 assert_nil project.due_date
852 848 end
853 849
854 850 test "#due_date should be the latest due date of it's issues" do
855 851 project = Project.generate!
856 852 project.trackers << Tracker.generate!
857 853 future = 7.days.from_now.to_date
858 854 Issue.generate!(:project => project, :due_date => future)
859 855 Issue.generate!(:project => project, :due_date => Date.today)
860 856
861 857 assert_equal future, project.due_date
862 858 end
863 859
864 860 test "#due_date should be the latest due date of it's versions" do
865 861 project = Project.generate!
866 862 future = 7.days.from_now.to_date
867 863 project.versions << Version.generate!(:effective_date => future)
868 864 project.versions << Version.generate!(:effective_date => Date.today)
869 865
870 866 assert_equal future, project.due_date
871 867 end
872 868
873 869 test "#due_date should pick the latest date from it's issues and versions" do
874 870 project = Project.generate!
875 871 project.trackers << Tracker.generate!
876 872 future = 7.days.from_now.to_date
877 873 far_future = 14.days.from_now.to_date
878 874 Issue.generate!(:project => project, :due_date => far_future)
879 875 project.versions << Version.generate!(:effective_date => future)
880 876
881 877 assert_equal far_future, project.due_date
882 878 end
883 879
884 880 test "#completed_percent with no versions should be 100" do
885 881 project = Project.generate!
886 882 assert_equal 100, project.completed_percent
887 883 end
888 884
889 885 test "#completed_percent with versions should return 0 if the versions have no issues" do
890 886 project = Project.generate!
891 887 Version.generate!(:project => project)
892 888 Version.generate!(:project => project)
893 889
894 890 assert_equal 0, project.completed_percent
895 891 end
896 892
897 893 test "#completed_percent with versions should return 100 if the version has only closed issues" do
898 894 project = Project.generate!
899 895 project.trackers << Tracker.generate!
900 896 v1 = Version.generate!(:project => project)
901 897 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
902 898 v2 = Version.generate!(:project => project)
903 899 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
904 900
905 901 assert_equal 100, project.completed_percent
906 902 end
907 903
908 904 test "#completed_percent with versions should return the averaged completed percent of the versions (not weighted)" do
909 905 project = Project.generate!
910 906 project.trackers << Tracker.generate!
911 907 v1 = Version.generate!(:project => project)
912 908 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
913 909 v2 = Version.generate!(:project => project)
914 910 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
915 911
916 912 assert_equal 50, project.completed_percent
917 913 end
918 914
919 915 test "#notified_users" do
920 916 project = Project.generate!
921 917 role = Role.generate!
922 918
923 919 user_with_membership_notification = User.generate!(:mail_notification => 'selected')
924 920 Member.create!(:project => project, :roles => [role], :principal => user_with_membership_notification, :mail_notification => true)
925 921
926 922 all_events_user = User.generate!(:mail_notification => 'all')
927 923 Member.create!(:project => project, :roles => [role], :principal => all_events_user)
928 924
929 925 no_events_user = User.generate!(:mail_notification => 'none')
930 926 Member.create!(:project => project, :roles => [role], :principal => no_events_user)
931 927
932 928 only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
933 929 Member.create!(:project => project, :roles => [role], :principal => only_my_events_user)
934 930
935 931 only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
936 932 Member.create!(:project => project, :roles => [role], :principal => only_assigned_user)
937 933
938 934 only_owned_user = User.generate!(:mail_notification => 'only_owner')
939 935 Member.create!(:project => project, :roles => [role], :principal => only_owned_user)
940 936
941 937 assert project.notified_users.include?(user_with_membership_notification), "should include members with a mail notification"
942 938 assert project.notified_users.include?(all_events_user), "should include users with the 'all' notification option"
943 939 assert !project.notified_users.include?(no_events_user), "should not include users with the 'none' notification option"
944 940 assert !project.notified_users.include?(only_my_events_user), "should not include users with the 'only_my_events' notification option"
945 941 assert !project.notified_users.include?(only_assigned_user), "should not include users with the 'only_assigned' notification option"
946 942 assert !project.notified_users.include?(only_owned_user), "should not include users with the 'only_owner' notification option"
947 943 end
948 944
949 945 def test_override_roles_without_builtin_group_memberships
950 946 project = Project.generate!
951 947 assert_equal [Role.anonymous], project.override_roles(Role.anonymous)
952 948 assert_equal [Role.non_member], project.override_roles(Role.non_member)
953 949 end
954 950 end
General Comments 0
You need to be logged in to leave comments. Login now