##// END OF EJS Templates
"I don't want to be notified of changes that I make myself" as Default for all User (#14574)....
Jean-Philippe Lang -
r14906:05a9275fdaab
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,68 +1,71
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class UserPreference < ActiveRecord::Base
18 class UserPreference < ActiveRecord::Base
19 belongs_to :user
19 belongs_to :user
20 serialize :others
20 serialize :others
21
21
22 attr_protected :others, :user_id
22 attr_protected :others, :user_id
23
23
24 before_save :set_others_hash
24 before_save :set_others_hash
25
25
26 def initialize(attributes=nil, *args)
26 def initialize(attributes=nil, *args)
27 super
27 super
28 if new_record? && !(attributes && attributes.key?(:hide_mail))
28 if new_record? && !(attributes && attributes.key?(:hide_mail))
29 self.hide_mail = Setting.default_users_hide_mail?
29 self.hide_mail = Setting.default_users_hide_mail?
30 end
30 end
31 if new_record? && !(attributes && attributes.key?(:no_self_notified))
32 self.no_self_notified = true
33 end
31 self.others ||= {}
34 self.others ||= {}
32 end
35 end
33
36
34 def set_others_hash
37 def set_others_hash
35 self.others ||= {}
38 self.others ||= {}
36 end
39 end
37
40
38 def [](attr_name)
41 def [](attr_name)
39 if has_attribute? attr_name
42 if has_attribute? attr_name
40 super
43 super
41 else
44 else
42 others ? others[attr_name] : nil
45 others ? others[attr_name] : nil
43 end
46 end
44 end
47 end
45
48
46 def []=(attr_name, value)
49 def []=(attr_name, value)
47 if has_attribute? attr_name
50 if has_attribute? attr_name
48 super
51 super
49 else
52 else
50 h = (read_attribute(:others) || {}).dup
53 h = (read_attribute(:others) || {}).dup
51 h.update(attr_name => value)
54 h.update(attr_name => value)
52 write_attribute(:others, h)
55 write_attribute(:others, h)
53 value
56 value
54 end
57 end
55 end
58 end
56
59
57 def comments_sorting; self[:comments_sorting] end
60 def comments_sorting; self[:comments_sorting] end
58 def comments_sorting=(order); self[:comments_sorting]=order end
61 def comments_sorting=(order); self[:comments_sorting]=order end
59
62
60 def warn_on_leaving_unsaved; self[:warn_on_leaving_unsaved] || '1'; end
63 def warn_on_leaving_unsaved; self[:warn_on_leaving_unsaved] || '1'; end
61 def warn_on_leaving_unsaved=(value); self[:warn_on_leaving_unsaved]=value; end
64 def warn_on_leaving_unsaved=(value); self[:warn_on_leaving_unsaved]=value; end
62
65
63 def no_self_notified; (self[:no_self_notified] == true || self[:no_self_notified] == '1'); end
66 def no_self_notified; (self[:no_self_notified] == true || self[:no_self_notified] == '1'); end
64 def no_self_notified=(value); self[:no_self_notified]=value; end
67 def no_self_notified=(value); self[:no_self_notified]=value; end
65
68
66 def activity_scope; Array(self[:activity_scope]) ; end
69 def activity_scope; Array(self[:activity_scope]) ; end
67 def activity_scope=(value); self[:activity_scope]=value ; end
70 def activity_scope=(value); self[:activity_scope]=value ; end
68 end
71 end
@@ -1,31 +1,41
1 ---
1 ---
2 user_preferences_001:
2 user_preferences_001:
3 others: |
3 others: |
4 ---
4 ---
5 :no_self_notified: false
5 :my_page_layout:
6 :my_page_layout:
6 left:
7 left:
7 - latestnews
8 - latestnews
8 - documents
9 - documents
9 right:
10 right:
10 - issuesassignedtome
11 - issuesassignedtome
11 top:
12 top:
12 - calendar
13 - calendar
13
14
14 id: 1
15 id: 1
15 user_id: 1
16 user_id: 1
16 hide_mail: true
17 hide_mail: true
17 user_preferences_002:
18 user_preferences_002:
18 others: |
19 others: |
19 ---
20 ---
21 :no_self_notified: false
22
23 id: 2
24 user_id: 2
25 hide_mail: true
26 user_preferences_003:
27 others: |
28 ---
29 :no_self_notified: false
20 :my_page_layout:
30 :my_page_layout:
21 left:
31 left:
22 - latestnews
32 - latestnews
23 - documents
33 - documents
24 right:
34 right:
25 - issuesassignedtome
35 - issuesassignedtome
26 top:
36 top:
27 - calendar
37 - calendar
28
38
29 id: 2
39 id: 3
30 user_id: 3
40 user_id: 3
31 hide_mail: false
41 hide_mail: false
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,322 +1,322
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssuesCustomFieldsVisibilityTest < ActionController::TestCase
20 class IssuesCustomFieldsVisibilityTest < ActionController::TestCase
21 tests IssuesController
21 tests IssuesController
22 fixtures :projects,
22 fixtures :projects,
23 :users, :email_addresses,
23 :users, :email_addresses, :user_preferences,
24 :roles,
24 :roles,
25 :members,
25 :members,
26 :member_roles,
26 :member_roles,
27 :issue_statuses,
27 :issue_statuses,
28 :trackers,
28 :trackers,
29 :projects_trackers,
29 :projects_trackers,
30 :enabled_modules,
30 :enabled_modules,
31 :enumerations,
31 :enumerations,
32 :workflows
32 :workflows
33
33
34 def setup
34 def setup
35 CustomField.delete_all
35 CustomField.delete_all
36 Issue.delete_all
36 Issue.delete_all
37 field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
37 field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
38 @fields = []
38 @fields = []
39 @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
39 @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
40 @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
40 @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
41 @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
41 @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
42 @issue = Issue.generate!(
42 @issue = Issue.generate!(
43 :author_id => 1,
43 :author_id => 1,
44 :project_id => 1,
44 :project_id => 1,
45 :tracker_id => 1,
45 :tracker_id => 1,
46 :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
46 :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
47 )
47 )
48
48
49 @user_with_role_on_other_project = User.generate!
49 @user_with_role_on_other_project = User.generate!
50 User.add_to_project(@user_with_role_on_other_project, Project.find(2), Role.find(3))
50 User.add_to_project(@user_with_role_on_other_project, Project.find(2), Role.find(3))
51
51
52 @users_to_test = {
52 @users_to_test = {
53 User.find(1) => [@field1, @field2, @field3],
53 User.find(1) => [@field1, @field2, @field3],
54 User.find(3) => [@field1, @field2],
54 User.find(3) => [@field1, @field2],
55 @user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
55 @user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
56 User.generate! => [@field1],
56 User.generate! => [@field1],
57 User.anonymous => [@field1]
57 User.anonymous => [@field1]
58 }
58 }
59
59
60 Member.where(:project_id => 1).each do |member|
60 Member.where(:project_id => 1).each do |member|
61 member.destroy unless @users_to_test.keys.include?(member.principal)
61 member.destroy unless @users_to_test.keys.include?(member.principal)
62 end
62 end
63 end
63 end
64
64
65 def test_show_should_show_visible_custom_fields_only
65 def test_show_should_show_visible_custom_fields_only
66 @users_to_test.each do |user, fields|
66 @users_to_test.each do |user, fields|
67 @request.session[:user_id] = user.id
67 @request.session[:user_id] = user.id
68 get :show, :id => @issue.id
68 get :show, :id => @issue.id
69 @fields.each_with_index do |field, i|
69 @fields.each_with_index do |field, i|
70 if fields.include?(field)
70 if fields.include?(field)
71 assert_select '.value', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name}"
71 assert_select '.value', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name}"
72 else
72 else
73 assert_select '.value', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name}"
73 assert_select '.value', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name}"
74 end
74 end
75 end
75 end
76 end
76 end
77 end
77 end
78
78
79 def test_show_should_show_visible_custom_fields_only_in_api
79 def test_show_should_show_visible_custom_fields_only_in_api
80 @users_to_test.each do |user, fields|
80 @users_to_test.each do |user, fields|
81 with_settings :rest_api_enabled => '1' do
81 with_settings :rest_api_enabled => '1' do
82 get :show, :id => @issue.id, :format => 'xml', :include => 'custom_fields', :key => user.api_key
82 get :show, :id => @issue.id, :format => 'xml', :include => 'custom_fields', :key => user.api_key
83 end
83 end
84 @fields.each_with_index do |field, i|
84 @fields.each_with_index do |field, i|
85 if fields.include?(field)
85 if fields.include?(field)
86 assert_select "custom_field[id=?] value", field.id.to_s, {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} in API"
86 assert_select "custom_field[id=?] value", field.id.to_s, {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} in API"
87 else
87 else
88 assert_select "custom_field[id=?] value", field.id.to_s, {:text => "Value#{i}", :count => 0}, "User #{user.id} was not able to view #{field.name} in API"
88 assert_select "custom_field[id=?] value", field.id.to_s, {:text => "Value#{i}", :count => 0}, "User #{user.id} was not able to view #{field.name} in API"
89 end
89 end
90 end
90 end
91 end
91 end
92 end
92 end
93
93
94 def test_show_should_show_visible_custom_fields_only_in_history
94 def test_show_should_show_visible_custom_fields_only_in_history
95 @issue.init_journal(User.find(1))
95 @issue.init_journal(User.find(1))
96 @issue.custom_field_values = {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
96 @issue.custom_field_values = {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
97 @issue.save!
97 @issue.save!
98
98
99 @users_to_test.each do |user, fields|
99 @users_to_test.each do |user, fields|
100 @request.session[:user_id] = user.id
100 @request.session[:user_id] = user.id
101 get :show, :id => @issue.id
101 get :show, :id => @issue.id
102 @fields.each_with_index do |field, i|
102 @fields.each_with_index do |field, i|
103 if fields.include?(field)
103 if fields.include?(field)
104 assert_select 'ul.details i', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} change"
104 assert_select 'ul.details i', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} change"
105 else
105 else
106 assert_select 'ul.details i', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name} change"
106 assert_select 'ul.details i', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name} change"
107 end
107 end
108 end
108 end
109 end
109 end
110 end
110 end
111
111
112 def test_show_should_show_visible_custom_fields_only_in_history_api
112 def test_show_should_show_visible_custom_fields_only_in_history_api
113 @issue.init_journal(User.find(1))
113 @issue.init_journal(User.find(1))
114 @issue.custom_field_values = {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
114 @issue.custom_field_values = {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
115 @issue.save!
115 @issue.save!
116
116
117 @users_to_test.each do |user, fields|
117 @users_to_test.each do |user, fields|
118 with_settings :rest_api_enabled => '1' do
118 with_settings :rest_api_enabled => '1' do
119 get :show, :id => @issue.id, :format => 'xml', :include => 'journals', :key => user.api_key
119 get :show, :id => @issue.id, :format => 'xml', :include => 'journals', :key => user.api_key
120 end
120 end
121 @fields.each_with_index do |field, i|
121 @fields.each_with_index do |field, i|
122 if fields.include?(field)
122 if fields.include?(field)
123 assert_select 'details old_value', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} change in API"
123 assert_select 'details old_value', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} change in API"
124 else
124 else
125 assert_select 'details old_value', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name} change in API"
125 assert_select 'details old_value', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name} change in API"
126 end
126 end
127 end
127 end
128 end
128 end
129 end
129 end
130
130
131 def test_edit_should_show_visible_custom_fields_only
131 def test_edit_should_show_visible_custom_fields_only
132 Role.anonymous.add_permission! :edit_issues
132 Role.anonymous.add_permission! :edit_issues
133
133
134 @users_to_test.each do |user, fields|
134 @users_to_test.each do |user, fields|
135 @request.session[:user_id] = user.id
135 @request.session[:user_id] = user.id
136 get :edit, :id => @issue.id
136 get :edit, :id => @issue.id
137 @fields.each_with_index do |field, i|
137 @fields.each_with_index do |field, i|
138 if fields.include?(field)
138 if fields.include?(field)
139 assert_select 'input[value=?]', "Value#{i}", 1, "User #{user.id} was not able to edit #{field.name}"
139 assert_select 'input[value=?]', "Value#{i}", 1, "User #{user.id} was not able to edit #{field.name}"
140 else
140 else
141 assert_select 'input[value=?]', "Value#{i}", 0, "User #{user.id} was able to edit #{field.name}"
141 assert_select 'input[value=?]', "Value#{i}", 0, "User #{user.id} was able to edit #{field.name}"
142 end
142 end
143 end
143 end
144 end
144 end
145 end
145 end
146
146
147 def test_update_should_update_visible_custom_fields_only
147 def test_update_should_update_visible_custom_fields_only
148 Role.anonymous.add_permission! :edit_issues
148 Role.anonymous.add_permission! :edit_issues
149
149
150 @users_to_test.each do |user, fields|
150 @users_to_test.each do |user, fields|
151 @request.session[:user_id] = user.id
151 @request.session[:user_id] = user.id
152 put :update, :id => @issue.id,
152 put :update, :id => @issue.id,
153 :issue => {:custom_field_values => {
153 :issue => {:custom_field_values => {
154 @field1.id.to_s => "User#{user.id}Value0",
154 @field1.id.to_s => "User#{user.id}Value0",
155 @field2.id.to_s => "User#{user.id}Value1",
155 @field2.id.to_s => "User#{user.id}Value1",
156 @field3.id.to_s => "User#{user.id}Value2",
156 @field3.id.to_s => "User#{user.id}Value2",
157 }}
157 }}
158 @issue.reload
158 @issue.reload
159 @fields.each_with_index do |field, i|
159 @fields.each_with_index do |field, i|
160 if fields.include?(field)
160 if fields.include?(field)
161 assert_equal "User#{user.id}Value#{i}", @issue.custom_field_value(field), "User #{user.id} was not able to update #{field.name}"
161 assert_equal "User#{user.id}Value#{i}", @issue.custom_field_value(field), "User #{user.id} was not able to update #{field.name}"
162 else
162 else
163 assert_not_equal "User#{user.id}Value#{i}", @issue.custom_field_value(field), "User #{user.id} was able to update #{field.name}"
163 assert_not_equal "User#{user.id}Value#{i}", @issue.custom_field_value(field), "User #{user.id} was able to update #{field.name}"
164 end
164 end
165 end
165 end
166 end
166 end
167 end
167 end
168
168
169 def test_index_should_show_visible_custom_fields_only
169 def test_index_should_show_visible_custom_fields_only
170 @users_to_test.each do |user, fields|
170 @users_to_test.each do |user, fields|
171 @request.session[:user_id] = user.id
171 @request.session[:user_id] = user.id
172 get :index, :c => (["subject"] + @fields.map{|f| "cf_#{f.id}"})
172 get :index, :c => (["subject"] + @fields.map{|f| "cf_#{f.id}"})
173 @fields.each_with_index do |field, i|
173 @fields.each_with_index do |field, i|
174 if fields.include?(field)
174 if fields.include?(field)
175 assert_select 'td', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name}"
175 assert_select 'td', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name}"
176 else
176 else
177 assert_select 'td', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name}"
177 assert_select 'td', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name}"
178 end
178 end
179 end
179 end
180 end
180 end
181 end
181 end
182
182
183 def test_index_as_csv_should_show_visible_custom_fields_only
183 def test_index_as_csv_should_show_visible_custom_fields_only
184 @users_to_test.each do |user, fields|
184 @users_to_test.each do |user, fields|
185 @request.session[:user_id] = user.id
185 @request.session[:user_id] = user.id
186 get :index, :c => (["subject"] + @fields.map{|f| "cf_#{f.id}"}), :format => 'csv'
186 get :index, :c => (["subject"] + @fields.map{|f| "cf_#{f.id}"}), :format => 'csv'
187 @fields.each_with_index do |field, i|
187 @fields.each_with_index do |field, i|
188 if fields.include?(field)
188 if fields.include?(field)
189 assert_include "Value#{i}", response.body, "User #{user.id} was not able to view #{field.name} in CSV"
189 assert_include "Value#{i}", response.body, "User #{user.id} was not able to view #{field.name} in CSV"
190 else
190 else
191 assert_not_include "Value#{i}", response.body, "User #{user.id} was able to view #{field.name} in CSV"
191 assert_not_include "Value#{i}", response.body, "User #{user.id} was able to view #{field.name} in CSV"
192 end
192 end
193 end
193 end
194 end
194 end
195 end
195 end
196
196
197 def test_index_with_partial_custom_field_visibility
197 def test_index_with_partial_custom_field_visibility
198 Issue.delete_all
198 Issue.delete_all
199 p1 = Project.generate!
199 p1 = Project.generate!
200 p2 = Project.generate!
200 p2 = Project.generate!
201 user = User.generate!
201 user = User.generate!
202 User.add_to_project(user, p1, Role.where(:id => [1, 3]).to_a)
202 User.add_to_project(user, p1, Role.where(:id => [1, 3]).to_a)
203 User.add_to_project(user, p2, Role.where(:id => 3).to_a)
203 User.add_to_project(user, p2, Role.where(:id => 3).to_a)
204 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueA'})
204 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueA'})
205 Issue.generate!(:project => p2, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueB'})
205 Issue.generate!(:project => p2, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueB'})
206 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueC'})
206 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueC'})
207
207
208 @request.session[:user_id] = user.id
208 @request.session[:user_id] = user.id
209 get :index, :c => ["subject", "cf_#{@field2.id}"]
209 get :index, :c => ["subject", "cf_#{@field2.id}"]
210 assert_select 'td', :text => 'ValueA'
210 assert_select 'td', :text => 'ValueA'
211 assert_select 'td', :text => 'ValueB', :count => 0
211 assert_select 'td', :text => 'ValueB', :count => 0
212 assert_select 'td', :text => 'ValueC'
212 assert_select 'td', :text => 'ValueC'
213
213
214 get :index, :sort => "cf_#{@field2.id}"
214 get :index, :sort => "cf_#{@field2.id}"
215 # ValueB is not visible to user and ignored while sorting
215 # ValueB is not visible to user and ignored while sorting
216 assert_equal %w(ValueB ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
216 assert_equal %w(ValueB ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
217
217
218 get :index, :set_filter => '1', "cf_#{@field2.id}" => '*'
218 get :index, :set_filter => '1', "cf_#{@field2.id}" => '*'
219 assert_equal %w(ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
219 assert_equal %w(ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
220
220
221 CustomField.update_all(:field_format => 'list')
221 CustomField.update_all(:field_format => 'list')
222 get :index, :group => "cf_#{@field2.id}"
222 get :index, :group => "cf_#{@field2.id}"
223 assert_equal %w(ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
223 assert_equal %w(ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
224 end
224 end
225
225
226 def test_create_should_send_notifications_according_custom_fields_visibility
226 def test_create_should_send_notifications_according_custom_fields_visibility
227 # anonymous user is never notified
227 # anonymous user is never notified
228 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
228 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
229
229
230 ActionMailer::Base.deliveries.clear
230 ActionMailer::Base.deliveries.clear
231 @request.session[:user_id] = 1
231 @request.session[:user_id] = 1
232 with_settings :bcc_recipients => '1' do
232 with_settings :bcc_recipients => '1' do
233 assert_difference 'Issue.count' do
233 assert_difference 'Issue.count' do
234 post :create,
234 post :create,
235 :project_id => 1,
235 :project_id => 1,
236 :issue => {
236 :issue => {
237 :tracker_id => 1,
237 :tracker_id => 1,
238 :status_id => 1,
238 :status_id => 1,
239 :subject => 'New issue',
239 :subject => 'New issue',
240 :priority_id => 5,
240 :priority_id => 5,
241 :custom_field_values => {@field1.id.to_s => 'Value0', @field2.id.to_s => 'Value1', @field3.id.to_s => 'Value2'},
241 :custom_field_values => {@field1.id.to_s => 'Value0', @field2.id.to_s => 'Value1', @field3.id.to_s => 'Value2'},
242 :watcher_user_ids => users_to_test.keys.map(&:id)
242 :watcher_user_ids => users_to_test.keys.map(&:id)
243 }
243 }
244 assert_response 302
244 assert_response 302
245 end
245 end
246 end
246 end
247 assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
247 assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
248 # tests that each user receives 1 email with the custom fields he is allowed to see only
248 # tests that each user receives 1 email with the custom fields he is allowed to see only
249 users_to_test.each do |user, fields|
249 users_to_test.each do |user, fields|
250 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
250 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
251 assert_equal 1, mails.size
251 assert_equal 1, mails.size
252 mail = mails.first
252 mail = mails.first
253 @fields.each_with_index do |field, i|
253 @fields.each_with_index do |field, i|
254 if fields.include?(field)
254 if fields.include?(field)
255 assert_mail_body_match "Value#{i}", mail, "User #{user.id} was not able to view #{field.name} in notification"
255 assert_mail_body_match "Value#{i}", mail, "User #{user.id} was not able to view #{field.name} in notification"
256 else
256 else
257 assert_mail_body_no_match "Value#{i}", mail, "User #{user.id} was able to view #{field.name} in notification"
257 assert_mail_body_no_match "Value#{i}", mail, "User #{user.id} was able to view #{field.name} in notification"
258 end
258 end
259 end
259 end
260 end
260 end
261 end
261 end
262
262
263 def test_update_should_send_notifications_according_custom_fields_visibility
263 def test_update_should_send_notifications_according_custom_fields_visibility
264 # anonymous user is never notified
264 # anonymous user is never notified
265 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
265 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
266
266
267 users_to_test.keys.each do |user|
267 users_to_test.keys.each do |user|
268 Watcher.create!(:user => user, :watchable => @issue)
268 Watcher.create!(:user => user, :watchable => @issue)
269 end
269 end
270 ActionMailer::Base.deliveries.clear
270 ActionMailer::Base.deliveries.clear
271 @request.session[:user_id] = 1
271 @request.session[:user_id] = 1
272 with_settings :bcc_recipients => '1' do
272 with_settings :bcc_recipients => '1' do
273 put :update,
273 put :update,
274 :id => @issue.id,
274 :id => @issue.id,
275 :issue => {
275 :issue => {
276 :custom_field_values => {@field1.id.to_s => 'NewValue0', @field2.id.to_s => 'NewValue1', @field3.id.to_s => 'NewValue2'}
276 :custom_field_values => {@field1.id.to_s => 'NewValue0', @field2.id.to_s => 'NewValue1', @field3.id.to_s => 'NewValue2'}
277 }
277 }
278 assert_response 302
278 assert_response 302
279 end
279 end
280 assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
280 assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
281 # tests that each user receives 1 email with the custom fields he is allowed to see only
281 # tests that each user receives 1 email with the custom fields he is allowed to see only
282 users_to_test.each do |user, fields|
282 users_to_test.each do |user, fields|
283 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
283 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
284 assert_equal 1, mails.size
284 assert_equal 1, mails.size
285 mail = mails.first
285 mail = mails.first
286 @fields.each_with_index do |field, i|
286 @fields.each_with_index do |field, i|
287 if fields.include?(field)
287 if fields.include?(field)
288 assert_mail_body_match "Value#{i}", mail, "User #{user.id} was not able to view #{field.name} in notification"
288 assert_mail_body_match "Value#{i}", mail, "User #{user.id} was not able to view #{field.name} in notification"
289 else
289 else
290 assert_mail_body_no_match "Value#{i}", mail, "User #{user.id} was able to view #{field.name} in notification"
290 assert_mail_body_no_match "Value#{i}", mail, "User #{user.id} was able to view #{field.name} in notification"
291 end
291 end
292 end
292 end
293 end
293 end
294 end
294 end
295
295
296 def test_updating_hidden_custom_fields_only_should_not_notifiy_user
296 def test_updating_hidden_custom_fields_only_should_not_notifiy_user
297 # anonymous user is never notified
297 # anonymous user is never notified
298 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
298 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
299
299
300 users_to_test.keys.each do |user|
300 users_to_test.keys.each do |user|
301 Watcher.create!(:user => user, :watchable => @issue)
301 Watcher.create!(:user => user, :watchable => @issue)
302 end
302 end
303 ActionMailer::Base.deliveries.clear
303 ActionMailer::Base.deliveries.clear
304 @request.session[:user_id] = 1
304 @request.session[:user_id] = 1
305 with_settings :bcc_recipients => '1' do
305 with_settings :bcc_recipients => '1' do
306 put :update,
306 put :update,
307 :id => @issue.id,
307 :id => @issue.id,
308 :issue => {
308 :issue => {
309 :custom_field_values => {@field2.id.to_s => 'NewValue1', @field3.id.to_s => 'NewValue2'}
309 :custom_field_values => {@field2.id.to_s => 'NewValue1', @field3.id.to_s => 'NewValue2'}
310 }
310 }
311 assert_response 302
311 assert_response 302
312 end
312 end
313 users_to_test.each do |user, fields|
313 users_to_test.each do |user, fields|
314 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
314 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
315 if (fields & [@field2, @field3]).any?
315 if (fields & [@field2, @field3]).any?
316 assert_equal 1, mails.size, "User #{user.id} was not notified"
316 assert_equal 1, mails.size, "User #{user.id} was not notified"
317 else
317 else
318 assert_equal 0, mails.size, "User #{user.id} was notified"
318 assert_equal 0, mails.size, "User #{user.id} was notified"
319 end
319 end
320 end
320 end
321 end
321 end
322 end
322 end
@@ -1,219 +1,219
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class MessagesControllerTest < ActionController::TestCase
20 class MessagesControllerTest < ActionController::TestCase
21 fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :boards, :messages, :enabled_modules
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles, :boards, :messages, :enabled_modules
22
22
23 def setup
23 def setup
24 User.current = nil
24 User.current = nil
25 end
25 end
26
26
27 def test_show
27 def test_show
28 get :show, :board_id => 1, :id => 1
28 get :show, :board_id => 1, :id => 1
29 assert_response :success
29 assert_response :success
30 assert_template 'show'
30 assert_template 'show'
31 assert_not_nil assigns(:board)
31 assert_not_nil assigns(:board)
32 assert_not_nil assigns(:project)
32 assert_not_nil assigns(:project)
33 assert_not_nil assigns(:topic)
33 assert_not_nil assigns(:topic)
34 end
34 end
35
35
36 def test_show_should_contain_reply_field_tags_for_quoting
36 def test_show_should_contain_reply_field_tags_for_quoting
37 @request.session[:user_id] = 2
37 @request.session[:user_id] = 2
38 get :show, :board_id => 1, :id => 1
38 get :show, :board_id => 1, :id => 1
39 assert_response :success
39 assert_response :success
40
40
41 # tags required by MessagesController#quote
41 # tags required by MessagesController#quote
42 assert_select 'input#message_subject'
42 assert_select 'input#message_subject'
43 assert_select 'textarea#message_content'
43 assert_select 'textarea#message_content'
44 assert_select 'div#reply'
44 assert_select 'div#reply'
45 end
45 end
46
46
47 def test_show_with_pagination
47 def test_show_with_pagination
48 message = Message.find(1)
48 message = Message.find(1)
49 assert_difference 'Message.count', 30 do
49 assert_difference 'Message.count', 30 do
50 30.times do
50 30.times do
51 message.children << Message.new(:subject => 'Reply',
51 message.children << Message.new(:subject => 'Reply',
52 :content => 'Reply body',
52 :content => 'Reply body',
53 :author_id => 2,
53 :author_id => 2,
54 :board_id => 1)
54 :board_id => 1)
55 end
55 end
56 end
56 end
57 get :show, :board_id => 1, :id => 1, :r => message.children.order('id').last.id
57 get :show, :board_id => 1, :id => 1, :r => message.children.order('id').last.id
58 assert_response :success
58 assert_response :success
59 assert_template 'show'
59 assert_template 'show'
60 replies = assigns(:replies)
60 replies = assigns(:replies)
61 assert_not_nil replies
61 assert_not_nil replies
62 assert_not_include message.children.reorder('id').first, replies
62 assert_not_include message.children.reorder('id').first, replies
63 assert_include message.children.reorder('id').last, replies
63 assert_include message.children.reorder('id').last, replies
64 end
64 end
65
65
66 def test_show_with_reply_permission
66 def test_show_with_reply_permission
67 @request.session[:user_id] = 2
67 @request.session[:user_id] = 2
68 get :show, :board_id => 1, :id => 1
68 get :show, :board_id => 1, :id => 1
69 assert_response :success
69 assert_response :success
70 assert_template 'show'
70 assert_template 'show'
71 assert_select 'div#reply textarea#message_content'
71 assert_select 'div#reply textarea#message_content'
72 end
72 end
73
73
74 def test_show_message_not_found
74 def test_show_message_not_found
75 get :show, :board_id => 1, :id => 99999
75 get :show, :board_id => 1, :id => 99999
76 assert_response 404
76 assert_response 404
77 end
77 end
78
78
79 def test_show_message_from_invalid_board_should_respond_with_404
79 def test_show_message_from_invalid_board_should_respond_with_404
80 get :show, :board_id => 999, :id => 1
80 get :show, :board_id => 999, :id => 1
81 assert_response 404
81 assert_response 404
82 end
82 end
83
83
84 def test_get_new
84 def test_get_new
85 @request.session[:user_id] = 2
85 @request.session[:user_id] = 2
86 get :new, :board_id => 1
86 get :new, :board_id => 1
87 assert_response :success
87 assert_response :success
88 assert_template 'new'
88 assert_template 'new'
89 end
89 end
90
90
91 def test_get_new_with_invalid_board
91 def test_get_new_with_invalid_board
92 @request.session[:user_id] = 2
92 @request.session[:user_id] = 2
93 get :new, :board_id => 99
93 get :new, :board_id => 99
94 assert_response 404
94 assert_response 404
95 end
95 end
96
96
97 def test_post_new
97 def test_post_new
98 @request.session[:user_id] = 2
98 @request.session[:user_id] = 2
99 ActionMailer::Base.deliveries.clear
99 ActionMailer::Base.deliveries.clear
100
100
101 with_settings :notified_events => %w(message_posted) do
101 with_settings :notified_events => %w(message_posted) do
102 post :new, :board_id => 1,
102 post :new, :board_id => 1,
103 :message => { :subject => 'Test created message',
103 :message => { :subject => 'Test created message',
104 :content => 'Message body'}
104 :content => 'Message body'}
105 end
105 end
106 message = Message.find_by_subject('Test created message')
106 message = Message.find_by_subject('Test created message')
107 assert_not_nil message
107 assert_not_nil message
108 assert_redirected_to "/boards/1/topics/#{message.to_param}"
108 assert_redirected_to "/boards/1/topics/#{message.to_param}"
109 assert_equal 'Message body', message.content
109 assert_equal 'Message body', message.content
110 assert_equal 2, message.author_id
110 assert_equal 2, message.author_id
111 assert_equal 1, message.board_id
111 assert_equal 1, message.board_id
112
112
113 mail = ActionMailer::Base.deliveries.last
113 mail = ActionMailer::Base.deliveries.last
114 assert_not_nil mail
114 assert_not_nil mail
115 assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject
115 assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject
116 assert_mail_body_match 'Message body', mail
116 assert_mail_body_match 'Message body', mail
117 # author
117 # author
118 assert mail.bcc.include?('jsmith@somenet.foo')
118 assert mail.bcc.include?('jsmith@somenet.foo')
119 # project member
119 # project member
120 assert mail.bcc.include?('dlopper@somenet.foo')
120 assert mail.bcc.include?('dlopper@somenet.foo')
121 end
121 end
122
122
123 def test_get_edit
123 def test_get_edit
124 @request.session[:user_id] = 2
124 @request.session[:user_id] = 2
125 get :edit, :board_id => 1, :id => 1
125 get :edit, :board_id => 1, :id => 1
126 assert_response :success
126 assert_response :success
127 assert_template 'edit'
127 assert_template 'edit'
128 end
128 end
129
129
130 def test_post_edit
130 def test_post_edit
131 @request.session[:user_id] = 2
131 @request.session[:user_id] = 2
132 post :edit, :board_id => 1, :id => 1,
132 post :edit, :board_id => 1, :id => 1,
133 :message => { :subject => 'New subject',
133 :message => { :subject => 'New subject',
134 :content => 'New body'}
134 :content => 'New body'}
135 assert_redirected_to '/boards/1/topics/1'
135 assert_redirected_to '/boards/1/topics/1'
136 message = Message.find(1)
136 message = Message.find(1)
137 assert_equal 'New subject', message.subject
137 assert_equal 'New subject', message.subject
138 assert_equal 'New body', message.content
138 assert_equal 'New body', message.content
139 end
139 end
140
140
141 def test_post_edit_sticky_and_locked
141 def test_post_edit_sticky_and_locked
142 @request.session[:user_id] = 2
142 @request.session[:user_id] = 2
143 post :edit, :board_id => 1, :id => 1,
143 post :edit, :board_id => 1, :id => 1,
144 :message => { :subject => 'New subject',
144 :message => { :subject => 'New subject',
145 :content => 'New body',
145 :content => 'New body',
146 :locked => '1',
146 :locked => '1',
147 :sticky => '1'}
147 :sticky => '1'}
148 assert_redirected_to '/boards/1/topics/1'
148 assert_redirected_to '/boards/1/topics/1'
149 message = Message.find(1)
149 message = Message.find(1)
150 assert_equal true, message.sticky?
150 assert_equal true, message.sticky?
151 assert_equal true, message.locked?
151 assert_equal true, message.locked?
152 end
152 end
153
153
154 def test_post_edit_should_allow_to_change_board
154 def test_post_edit_should_allow_to_change_board
155 @request.session[:user_id] = 2
155 @request.session[:user_id] = 2
156 post :edit, :board_id => 1, :id => 1,
156 post :edit, :board_id => 1, :id => 1,
157 :message => { :subject => 'New subject',
157 :message => { :subject => 'New subject',
158 :content => 'New body',
158 :content => 'New body',
159 :board_id => 2}
159 :board_id => 2}
160 assert_redirected_to '/boards/2/topics/1'
160 assert_redirected_to '/boards/2/topics/1'
161 message = Message.find(1)
161 message = Message.find(1)
162 assert_equal Board.find(2), message.board
162 assert_equal Board.find(2), message.board
163 end
163 end
164
164
165 def test_reply
165 def test_reply
166 @request.session[:user_id] = 2
166 @request.session[:user_id] = 2
167 post :reply, :board_id => 1, :id => 1, :reply => { :content => 'This is a test reply', :subject => 'Test reply' }
167 post :reply, :board_id => 1, :id => 1, :reply => { :content => 'This is a test reply', :subject => 'Test reply' }
168 reply = Message.order('id DESC').first
168 reply = Message.order('id DESC').first
169 assert_redirected_to "/boards/1/topics/1?r=#{reply.id}"
169 assert_redirected_to "/boards/1/topics/1?r=#{reply.id}"
170 assert Message.find_by_subject('Test reply')
170 assert Message.find_by_subject('Test reply')
171 end
171 end
172
172
173 def test_destroy_topic
173 def test_destroy_topic
174 @request.session[:user_id] = 2
174 @request.session[:user_id] = 2
175 assert_difference 'Message.count', -3 do
175 assert_difference 'Message.count', -3 do
176 post :destroy, :board_id => 1, :id => 1
176 post :destroy, :board_id => 1, :id => 1
177 end
177 end
178 assert_redirected_to '/projects/ecookbook/boards/1'
178 assert_redirected_to '/projects/ecookbook/boards/1'
179 assert_nil Message.find_by_id(1)
179 assert_nil Message.find_by_id(1)
180 end
180 end
181
181
182 def test_destroy_reply
182 def test_destroy_reply
183 @request.session[:user_id] = 2
183 @request.session[:user_id] = 2
184 assert_difference 'Message.count', -1 do
184 assert_difference 'Message.count', -1 do
185 post :destroy, :board_id => 1, :id => 2
185 post :destroy, :board_id => 1, :id => 2
186 end
186 end
187 assert_redirected_to '/boards/1/topics/1?r=2'
187 assert_redirected_to '/boards/1/topics/1?r=2'
188 assert_nil Message.find_by_id(2)
188 assert_nil Message.find_by_id(2)
189 end
189 end
190
190
191 def test_quote
191 def test_quote
192 @request.session[:user_id] = 2
192 @request.session[:user_id] = 2
193 xhr :get, :quote, :board_id => 1, :id => 3
193 xhr :get, :quote, :board_id => 1, :id => 3
194 assert_response :success
194 assert_response :success
195 assert_equal 'text/javascript', response.content_type
195 assert_equal 'text/javascript', response.content_type
196 assert_template 'quote'
196 assert_template 'quote'
197 assert_include 'RE: First post', response.body
197 assert_include 'RE: First post', response.body
198 assert_include '> An other reply', response.body
198 assert_include '> An other reply', response.body
199 end
199 end
200
200
201 def test_preview_new
201 def test_preview_new
202 @request.session[:user_id] = 2
202 @request.session[:user_id] = 2
203 post :preview,
203 post :preview,
204 :board_id => 1,
204 :board_id => 1,
205 :message => {:subject => "", :content => "Previewed text"}
205 :message => {:subject => "", :content => "Previewed text"}
206 assert_response :success
206 assert_response :success
207 assert_template 'common/_preview'
207 assert_template 'common/_preview'
208 end
208 end
209
209
210 def test_preview_edit
210 def test_preview_edit
211 @request.session[:user_id] = 2
211 @request.session[:user_id] = 2
212 post :preview,
212 post :preview,
213 :id => 4,
213 :id => 4,
214 :board_id => 1,
214 :board_id => 1,
215 :message => {:subject => "", :content => "Previewed text"}
215 :message => {:subject => "", :content => "Previewed text"}
216 assert_response :success
216 assert_response :success
217 assert_template 'common/_preview'
217 assert_template 'common/_preview'
218 end
218 end
219 end
219 end
@@ -1,2748 +1,2748
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssueTest < ActiveSupport::TestCase
20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles,
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles,
22 :groups_users,
22 :groups_users,
23 :trackers, :projects_trackers,
23 :trackers, :projects_trackers,
24 :enabled_modules,
24 :enabled_modules,
25 :versions,
25 :versions,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 :enumerations,
27 :enumerations,
28 :issues, :journals, :journal_details,
28 :issues, :journals, :journal_details,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 :time_entries
30 :time_entries
31
31
32 include Redmine::I18n
32 include Redmine::I18n
33
33
34 def setup
34 def setup
35 set_language_if_valid 'en'
35 set_language_if_valid 'en'
36 end
36 end
37
37
38 def teardown
38 def teardown
39 User.current = nil
39 User.current = nil
40 end
40 end
41
41
42 def test_initialize
42 def test_initialize
43 issue = Issue.new
43 issue = Issue.new
44
44
45 assert_nil issue.project_id
45 assert_nil issue.project_id
46 assert_nil issue.tracker_id
46 assert_nil issue.tracker_id
47 assert_nil issue.status_id
47 assert_nil issue.status_id
48 assert_nil issue.author_id
48 assert_nil issue.author_id
49 assert_nil issue.assigned_to_id
49 assert_nil issue.assigned_to_id
50 assert_nil issue.category_id
50 assert_nil issue.category_id
51
51
52 assert_equal IssuePriority.default, issue.priority
52 assert_equal IssuePriority.default, issue.priority
53 end
53 end
54
54
55 def test_create
55 def test_create
56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
57 :status_id => 1, :priority => IssuePriority.all.first,
57 :status_id => 1, :priority => IssuePriority.all.first,
58 :subject => 'test_create',
58 :subject => 'test_create',
59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
60 assert issue.save
60 assert issue.save
61 issue.reload
61 issue.reload
62 assert_equal 1.5, issue.estimated_hours
62 assert_equal 1.5, issue.estimated_hours
63 end
63 end
64
64
65 def test_create_minimal
65 def test_create_minimal
66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
67 assert issue.save
67 assert issue.save
68 assert_equal issue.tracker.default_status, issue.status
68 assert_equal issue.tracker.default_status, issue.status
69 assert issue.description.nil?
69 assert issue.description.nil?
70 assert_nil issue.estimated_hours
70 assert_nil issue.estimated_hours
71 end
71 end
72
72
73 def test_create_with_all_fields_disabled
73 def test_create_with_all_fields_disabled
74 tracker = Tracker.find(1)
74 tracker = Tracker.find(1)
75 tracker.core_fields = []
75 tracker.core_fields = []
76 tracker.save!
76 tracker.save!
77
77
78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
79 assert_save issue
79 assert_save issue
80 end
80 end
81
81
82 def test_start_date_format_should_be_validated
82 def test_start_date_format_should_be_validated
83 set_language_if_valid 'en'
83 set_language_if_valid 'en'
84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
85 issue = Issue.new(:start_date => invalid_date)
85 issue = Issue.new(:start_date => invalid_date)
86 assert !issue.valid?
86 assert !issue.valid?
87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
88 end
88 end
89 end
89 end
90
90
91 def test_due_date_format_should_be_validated
91 def test_due_date_format_should_be_validated
92 set_language_if_valid 'en'
92 set_language_if_valid 'en'
93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
94 issue = Issue.new(:due_date => invalid_date)
94 issue = Issue.new(:due_date => invalid_date)
95 assert !issue.valid?
95 assert !issue.valid?
96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
97 end
97 end
98 end
98 end
99
99
100 def test_due_date_lesser_than_start_date_should_not_validate
100 def test_due_date_lesser_than_start_date_should_not_validate
101 set_language_if_valid 'en'
101 set_language_if_valid 'en'
102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
103 assert !issue.valid?
103 assert !issue.valid?
104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
105 end
105 end
106
106
107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
108 issue = Issue.generate(:start_date => '2013-06-04')
108 issue = Issue.generate(:start_date => '2013-06-04')
109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
110 assert !issue.valid?
110 assert !issue.valid?
111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
112 end
112 end
113
113
114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
115 issue = Issue.generate!(:start_date => '2013-06-04')
115 issue = Issue.generate!(:start_date => '2013-06-04')
116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
117 issue.start_date = '2013-06-07'
117 issue.start_date = '2013-06-07'
118 assert !issue.valid?
118 assert !issue.valid?
119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
120 end
120 end
121
121
122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
123 issue = Issue.generate!(:start_date => '2013-06-04')
123 issue = Issue.generate!(:start_date => '2013-06-04')
124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
125 assert issue.valid?
125 assert issue.valid?
126 end
126 end
127
127
128 def test_estimated_hours_should_be_validated
128 def test_estimated_hours_should_be_validated
129 set_language_if_valid 'en'
129 set_language_if_valid 'en'
130 ['-2'].each do |invalid|
130 ['-2'].each do |invalid|
131 issue = Issue.new(:estimated_hours => invalid)
131 issue = Issue.new(:estimated_hours => invalid)
132 assert !issue.valid?
132 assert !issue.valid?
133 assert_include 'Estimated time is invalid', issue.errors.full_messages
133 assert_include 'Estimated time is invalid', issue.errors.full_messages
134 end
134 end
135 end
135 end
136
136
137 def test_create_with_required_custom_field
137 def test_create_with_required_custom_field
138 set_language_if_valid 'en'
138 set_language_if_valid 'en'
139 field = IssueCustomField.find_by_name('Database')
139 field = IssueCustomField.find_by_name('Database')
140 field.update_attribute(:is_required, true)
140 field.update_attribute(:is_required, true)
141
141
142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
143 :status_id => 1, :subject => 'test_create',
143 :status_id => 1, :subject => 'test_create',
144 :description => 'IssueTest#test_create_with_required_custom_field')
144 :description => 'IssueTest#test_create_with_required_custom_field')
145 assert issue.available_custom_fields.include?(field)
145 assert issue.available_custom_fields.include?(field)
146 # No value for the custom field
146 # No value for the custom field
147 assert !issue.save
147 assert !issue.save
148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
149 # Blank value
149 # Blank value
150 issue.custom_field_values = { field.id => '' }
150 issue.custom_field_values = { field.id => '' }
151 assert !issue.save
151 assert !issue.save
152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
153 # Invalid value
153 # Invalid value
154 issue.custom_field_values = { field.id => 'SQLServer' }
154 issue.custom_field_values = { field.id => 'SQLServer' }
155 assert !issue.save
155 assert !issue.save
156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
157 # Valid value
157 # Valid value
158 issue.custom_field_values = { field.id => 'PostgreSQL' }
158 issue.custom_field_values = { field.id => 'PostgreSQL' }
159 assert issue.save
159 assert issue.save
160 issue.reload
160 issue.reload
161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
162 end
162 end
163
163
164 def test_create_with_group_assignment
164 def test_create_with_group_assignment
165 with_settings :issue_group_assignment => '1' do
165 with_settings :issue_group_assignment => '1' do
166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
167 :subject => 'Group assignment',
167 :subject => 'Group assignment',
168 :assigned_to_id => 11).save
168 :assigned_to_id => 11).save
169 issue = Issue.order('id DESC').first
169 issue = Issue.order('id DESC').first
170 assert_kind_of Group, issue.assigned_to
170 assert_kind_of Group, issue.assigned_to
171 assert_equal Group.find(11), issue.assigned_to
171 assert_equal Group.find(11), issue.assigned_to
172 end
172 end
173 end
173 end
174
174
175 def test_create_with_parent_issue_id
175 def test_create_with_parent_issue_id
176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
177 :author_id => 1, :subject => 'Group assignment',
177 :author_id => 1, :subject => 'Group assignment',
178 :parent_issue_id => 1)
178 :parent_issue_id => 1)
179 assert_save issue
179 assert_save issue
180 assert_equal 1, issue.parent_issue_id
180 assert_equal 1, issue.parent_issue_id
181 assert_equal Issue.find(1), issue.parent
181 assert_equal Issue.find(1), issue.parent
182 end
182 end
183
183
184 def test_create_with_sharp_parent_issue_id
184 def test_create_with_sharp_parent_issue_id
185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
186 :author_id => 1, :subject => 'Group assignment',
186 :author_id => 1, :subject => 'Group assignment',
187 :parent_issue_id => "#1")
187 :parent_issue_id => "#1")
188 assert_save issue
188 assert_save issue
189 assert_equal 1, issue.parent_issue_id
189 assert_equal 1, issue.parent_issue_id
190 assert_equal Issue.find(1), issue.parent
190 assert_equal Issue.find(1), issue.parent
191 end
191 end
192
192
193 def test_create_with_invalid_parent_issue_id
193 def test_create_with_invalid_parent_issue_id
194 set_language_if_valid 'en'
194 set_language_if_valid 'en'
195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
196 :author_id => 1, :subject => 'Group assignment',
196 :author_id => 1, :subject => 'Group assignment',
197 :parent_issue_id => '01ABC')
197 :parent_issue_id => '01ABC')
198 assert !issue.save
198 assert !issue.save
199 assert_equal '01ABC', issue.parent_issue_id
199 assert_equal '01ABC', issue.parent_issue_id
200 assert_include 'Parent task is invalid', issue.errors.full_messages
200 assert_include 'Parent task is invalid', issue.errors.full_messages
201 end
201 end
202
202
203 def test_create_with_invalid_sharp_parent_issue_id
203 def test_create_with_invalid_sharp_parent_issue_id
204 set_language_if_valid 'en'
204 set_language_if_valid 'en'
205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
206 :author_id => 1, :subject => 'Group assignment',
206 :author_id => 1, :subject => 'Group assignment',
207 :parent_issue_id => '#01ABC')
207 :parent_issue_id => '#01ABC')
208 assert !issue.save
208 assert !issue.save
209 assert_equal '#01ABC', issue.parent_issue_id
209 assert_equal '#01ABC', issue.parent_issue_id
210 assert_include 'Parent task is invalid', issue.errors.full_messages
210 assert_include 'Parent task is invalid', issue.errors.full_messages
211 end
211 end
212
212
213 def assert_visibility_match(user, issues)
213 def assert_visibility_match(user, issues)
214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
215 end
215 end
216
216
217 def test_visible_scope_for_anonymous
217 def test_visible_scope_for_anonymous
218 # Anonymous user should see issues of public projects only
218 # Anonymous user should see issues of public projects only
219 issues = Issue.visible(User.anonymous).to_a
219 issues = Issue.visible(User.anonymous).to_a
220 assert issues.any?
220 assert issues.any?
221 assert_nil issues.detect {|issue| !issue.project.is_public?}
221 assert_nil issues.detect {|issue| !issue.project.is_public?}
222 assert_nil issues.detect {|issue| issue.is_private?}
222 assert_nil issues.detect {|issue| issue.is_private?}
223 assert_visibility_match User.anonymous, issues
223 assert_visibility_match User.anonymous, issues
224 end
224 end
225
225
226 def test_visible_scope_for_anonymous_without_view_issues_permissions
226 def test_visible_scope_for_anonymous_without_view_issues_permissions
227 # Anonymous user should not see issues without permission
227 # Anonymous user should not see issues without permission
228 Role.anonymous.remove_permission!(:view_issues)
228 Role.anonymous.remove_permission!(:view_issues)
229 issues = Issue.visible(User.anonymous).to_a
229 issues = Issue.visible(User.anonymous).to_a
230 assert issues.empty?
230 assert issues.empty?
231 assert_visibility_match User.anonymous, issues
231 assert_visibility_match User.anonymous, issues
232 end
232 end
233
233
234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
235 Role.anonymous.remove_permission!(:view_issues)
235 Role.anonymous.remove_permission!(:view_issues)
236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
237
237
238 issues = Issue.visible(User.anonymous).all
238 issues = Issue.visible(User.anonymous).all
239 assert issues.any?
239 assert issues.any?
240 assert_equal [1], issues.map(&:project_id).uniq.sort
240 assert_equal [1], issues.map(&:project_id).uniq.sort
241 assert_visibility_match User.anonymous, issues
241 assert_visibility_match User.anonymous, issues
242 end
242 end
243
243
244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
245 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
245 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
246 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
246 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
248 assert !issue.visible?(User.anonymous)
248 assert !issue.visible?(User.anonymous)
249 end
249 end
250
250
251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
252 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
252 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
253 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
253 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
255 assert !issue.visible?(User.anonymous)
255 assert !issue.visible?(User.anonymous)
256 end
256 end
257
257
258 def test_visible_scope_for_non_member
258 def test_visible_scope_for_non_member
259 user = User.find(9)
259 user = User.find(9)
260 assert user.projects.empty?
260 assert user.projects.empty?
261 # Non member user should see issues of public projects only
261 # Non member user should see issues of public projects only
262 issues = Issue.visible(user).to_a
262 issues = Issue.visible(user).to_a
263 assert issues.any?
263 assert issues.any?
264 assert_nil issues.detect {|issue| !issue.project.is_public?}
264 assert_nil issues.detect {|issue| !issue.project.is_public?}
265 assert_nil issues.detect {|issue| issue.is_private?}
265 assert_nil issues.detect {|issue| issue.is_private?}
266 assert_visibility_match user, issues
266 assert_visibility_match user, issues
267 end
267 end
268
268
269 def test_visible_scope_for_non_member_with_own_issues_visibility
269 def test_visible_scope_for_non_member_with_own_issues_visibility
270 Role.non_member.update_attribute :issues_visibility, 'own'
270 Role.non_member.update_attribute :issues_visibility, 'own'
271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
272 user = User.find(9)
272 user = User.find(9)
273
273
274 issues = Issue.visible(user).to_a
274 issues = Issue.visible(user).to_a
275 assert issues.any?
275 assert issues.any?
276 assert_nil issues.detect {|issue| issue.author != user}
276 assert_nil issues.detect {|issue| issue.author != user}
277 assert_visibility_match user, issues
277 assert_visibility_match user, issues
278 end
278 end
279
279
280 def test_visible_scope_for_non_member_without_view_issues_permissions
280 def test_visible_scope_for_non_member_without_view_issues_permissions
281 # Non member user should not see issues without permission
281 # Non member user should not see issues without permission
282 Role.non_member.remove_permission!(:view_issues)
282 Role.non_member.remove_permission!(:view_issues)
283 user = User.find(9)
283 user = User.find(9)
284 assert user.projects.empty?
284 assert user.projects.empty?
285 issues = Issue.visible(user).to_a
285 issues = Issue.visible(user).to_a
286 assert issues.empty?
286 assert issues.empty?
287 assert_visibility_match user, issues
287 assert_visibility_match user, issues
288 end
288 end
289
289
290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
291 Role.non_member.remove_permission!(:view_issues)
291 Role.non_member.remove_permission!(:view_issues)
292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
293 user = User.find(9)
293 user = User.find(9)
294
294
295 issues = Issue.visible(user).all
295 issues = Issue.visible(user).all
296 assert issues.any?
296 assert issues.any?
297 assert_equal [1], issues.map(&:project_id).uniq.sort
297 assert_equal [1], issues.map(&:project_id).uniq.sort
298 assert_visibility_match user, issues
298 assert_visibility_match user, issues
299 end
299 end
300
300
301 def test_visible_scope_for_member
301 def test_visible_scope_for_member
302 user = User.find(9)
302 user = User.find(9)
303 # User should see issues of projects for which user has view_issues permissions only
303 # User should see issues of projects for which user has view_issues permissions only
304 Role.non_member.remove_permission!(:view_issues)
304 Role.non_member.remove_permission!(:view_issues)
305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
306 issues = Issue.visible(user).to_a
306 issues = Issue.visible(user).to_a
307 assert issues.any?
307 assert issues.any?
308 assert_nil issues.detect {|issue| issue.project_id != 3}
308 assert_nil issues.detect {|issue| issue.project_id != 3}
309 assert_nil issues.detect {|issue| issue.is_private?}
309 assert_nil issues.detect {|issue| issue.is_private?}
310 assert_visibility_match user, issues
310 assert_visibility_match user, issues
311 end
311 end
312
312
313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
314 Role.non_member.add_permission!(:view_issues)
314 Role.non_member.add_permission!(:view_issues)
315 Role.find(1).remove_permission!(:view_issues)
315 Role.find(1).remove_permission!(:view_issues)
316 user = User.find(2)
316 user = User.find(2)
317
317
318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
320 end
320 end
321
321
322 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
322 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
323 user = User.find(8)
323 user = User.find(8)
324 assert user.groups.any?
324 assert user.groups.any?
325 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
325 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
326 Role.non_member.remove_permission!(:view_issues)
326 Role.non_member.remove_permission!(:view_issues)
327
327
328 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
328 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
329 :status_id => 1, :priority => IssuePriority.all.first,
329 :status_id => 1, :priority => IssuePriority.all.first,
330 :subject => 'Assignment test',
330 :subject => 'Assignment test',
331 :assigned_to => user.groups.first,
331 :assigned_to => user.groups.first,
332 :is_private => true)
332 :is_private => true)
333
333
334 Role.find(2).update_attribute :issues_visibility, 'default'
334 Role.find(2).update_attribute :issues_visibility, 'default'
335 issues = Issue.visible(User.find(8)).to_a
335 issues = Issue.visible(User.find(8)).to_a
336 assert issues.any?
336 assert issues.any?
337 assert issues.include?(issue)
337 assert issues.include?(issue)
338
338
339 Role.find(2).update_attribute :issues_visibility, 'own'
339 Role.find(2).update_attribute :issues_visibility, 'own'
340 issues = Issue.visible(User.find(8)).to_a
340 issues = Issue.visible(User.find(8)).to_a
341 assert issues.any?
341 assert issues.any?
342 assert_include issue, issues
342 assert_include issue, issues
343 end
343 end
344
344
345 def test_visible_scope_for_admin
345 def test_visible_scope_for_admin
346 user = User.find(1)
346 user = User.find(1)
347 user.members.each(&:destroy)
347 user.members.each(&:destroy)
348 assert user.projects.empty?
348 assert user.projects.empty?
349 issues = Issue.visible(user).to_a
349 issues = Issue.visible(user).to_a
350 assert issues.any?
350 assert issues.any?
351 # Admin should see issues on private projects that admin does not belong to
351 # Admin should see issues on private projects that admin does not belong to
352 assert issues.detect {|issue| !issue.project.is_public?}
352 assert issues.detect {|issue| !issue.project.is_public?}
353 # Admin should see private issues of other users
353 # Admin should see private issues of other users
354 assert issues.detect {|issue| issue.is_private? && issue.author != user}
354 assert issues.detect {|issue| issue.is_private? && issue.author != user}
355 assert_visibility_match user, issues
355 assert_visibility_match user, issues
356 end
356 end
357
357
358 def test_visible_scope_with_project
358 def test_visible_scope_with_project
359 project = Project.find(1)
359 project = Project.find(1)
360 issues = Issue.visible(User.find(2), :project => project).to_a
360 issues = Issue.visible(User.find(2), :project => project).to_a
361 projects = issues.collect(&:project).uniq
361 projects = issues.collect(&:project).uniq
362 assert_equal 1, projects.size
362 assert_equal 1, projects.size
363 assert_equal project, projects.first
363 assert_equal project, projects.first
364 end
364 end
365
365
366 def test_visible_scope_with_project_and_subprojects
366 def test_visible_scope_with_project_and_subprojects
367 project = Project.find(1)
367 project = Project.find(1)
368 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
368 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
369 projects = issues.collect(&:project).uniq
369 projects = issues.collect(&:project).uniq
370 assert projects.size > 1
370 assert projects.size > 1
371 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
371 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
372 end
372 end
373
373
374 def test_visible_and_nested_set_scopes
374 def test_visible_and_nested_set_scopes
375 user = User.generate!
375 user = User.generate!
376 parent = Issue.generate!(:assigned_to => user)
376 parent = Issue.generate!(:assigned_to => user)
377 assert parent.visible?(user)
377 assert parent.visible?(user)
378 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
378 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
379 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
379 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
380 parent.reload
380 parent.reload
381 child1.reload
381 child1.reload
382 child2.reload
382 child2.reload
383 assert child1.visible?(user)
383 assert child1.visible?(user)
384 assert child2.visible?(user)
384 assert child2.visible?(user)
385 assert_equal 2, parent.descendants.count
385 assert_equal 2, parent.descendants.count
386 assert_equal 2, parent.descendants.visible(user).count
386 assert_equal 2, parent.descendants.visible(user).count
387 # awesome_nested_set 2-1-stable branch has regression.
387 # awesome_nested_set 2-1-stable branch has regression.
388 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
388 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
389 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
389 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
390 assert_equal 2, parent.descendants.collect{|i| i}.size
390 assert_equal 2, parent.descendants.collect{|i| i}.size
391 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
391 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
392 end
392 end
393
393
394 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
394 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
395 user = User.new
395 user = User.new
396 assert_nothing_raised do
396 assert_nothing_raised do
397 Issue.visible(user).to_a
397 Issue.visible(user).to_a
398 end
398 end
399 end
399 end
400
400
401 def test_open_scope
401 def test_open_scope
402 issues = Issue.open.to_a
402 issues = Issue.open.to_a
403 assert_nil issues.detect(&:closed?)
403 assert_nil issues.detect(&:closed?)
404 end
404 end
405
405
406 def test_open_scope_with_arg
406 def test_open_scope_with_arg
407 issues = Issue.open(false).to_a
407 issues = Issue.open(false).to_a
408 assert_equal issues, issues.select(&:closed?)
408 assert_equal issues, issues.select(&:closed?)
409 end
409 end
410
410
411 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
411 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
412 version = Version.find(2)
412 version = Version.find(2)
413 assert version.fixed_issues.any?
413 assert version.fixed_issues.any?
414 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
414 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
415 end
415 end
416
416
417 def test_fixed_version_scope_with_empty_array_should_return_no_result
417 def test_fixed_version_scope_with_empty_array_should_return_no_result
418 assert_equal 0, Issue.fixed_version([]).count
418 assert_equal 0, Issue.fixed_version([]).count
419 end
419 end
420
420
421 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
421 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
422 user = User.generate!
422 user = User.generate!
423 issue = Issue.generate!
423 issue = Issue.generate!
424 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
424 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
425 assert_equal [issue], Issue.assigned_to(user).to_a
425 assert_equal [issue], Issue.assigned_to(user).to_a
426 end
426 end
427
427
428 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
428 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
429 group = Group.generate!
429 group = Group.generate!
430 user = User.generate!
430 user = User.generate!
431 group.users << user
431 group.users << user
432 issue = Issue.generate!
432 issue = Issue.generate!
433 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
433 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
434 assert_equal [issue], Issue.assigned_to(user).to_a
434 assert_equal [issue], Issue.assigned_to(user).to_a
435 end
435 end
436
436
437 def test_errors_full_messages_should_include_custom_fields_errors
437 def test_errors_full_messages_should_include_custom_fields_errors
438 field = IssueCustomField.find_by_name('Database')
438 field = IssueCustomField.find_by_name('Database')
439
439
440 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
440 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
441 :status_id => 1, :subject => 'test_create',
441 :status_id => 1, :subject => 'test_create',
442 :description => 'IssueTest#test_create_with_required_custom_field')
442 :description => 'IssueTest#test_create_with_required_custom_field')
443 assert issue.available_custom_fields.include?(field)
443 assert issue.available_custom_fields.include?(field)
444 # Invalid value
444 # Invalid value
445 issue.custom_field_values = { field.id => 'SQLServer' }
445 issue.custom_field_values = { field.id => 'SQLServer' }
446
446
447 assert !issue.valid?
447 assert !issue.valid?
448 assert_equal 1, issue.errors.full_messages.size
448 assert_equal 1, issue.errors.full_messages.size
449 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
449 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
450 issue.errors.full_messages.first
450 issue.errors.full_messages.first
451 end
451 end
452
452
453 def test_update_issue_with_required_custom_field
453 def test_update_issue_with_required_custom_field
454 field = IssueCustomField.find_by_name('Database')
454 field = IssueCustomField.find_by_name('Database')
455 field.update_attribute(:is_required, true)
455 field.update_attribute(:is_required, true)
456
456
457 issue = Issue.find(1)
457 issue = Issue.find(1)
458 assert_nil issue.custom_value_for(field)
458 assert_nil issue.custom_value_for(field)
459 assert issue.available_custom_fields.include?(field)
459 assert issue.available_custom_fields.include?(field)
460 # No change to custom values, issue can be saved
460 # No change to custom values, issue can be saved
461 assert issue.save
461 assert issue.save
462 # Blank value
462 # Blank value
463 issue.custom_field_values = { field.id => '' }
463 issue.custom_field_values = { field.id => '' }
464 assert !issue.save
464 assert !issue.save
465 # Valid value
465 # Valid value
466 issue.custom_field_values = { field.id => 'PostgreSQL' }
466 issue.custom_field_values = { field.id => 'PostgreSQL' }
467 assert issue.save
467 assert issue.save
468 issue.reload
468 issue.reload
469 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
469 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
470 end
470 end
471
471
472 def test_should_not_update_attributes_if_custom_fields_validation_fails
472 def test_should_not_update_attributes_if_custom_fields_validation_fails
473 issue = Issue.find(1)
473 issue = Issue.find(1)
474 field = IssueCustomField.find_by_name('Database')
474 field = IssueCustomField.find_by_name('Database')
475 assert issue.available_custom_fields.include?(field)
475 assert issue.available_custom_fields.include?(field)
476
476
477 issue.custom_field_values = { field.id => 'Invalid' }
477 issue.custom_field_values = { field.id => 'Invalid' }
478 issue.subject = 'Should be not be saved'
478 issue.subject = 'Should be not be saved'
479 assert !issue.save
479 assert !issue.save
480
480
481 issue.reload
481 issue.reload
482 assert_equal "Cannot print recipes", issue.subject
482 assert_equal "Cannot print recipes", issue.subject
483 end
483 end
484
484
485 def test_should_not_recreate_custom_values_objects_on_update
485 def test_should_not_recreate_custom_values_objects_on_update
486 field = IssueCustomField.find_by_name('Database')
486 field = IssueCustomField.find_by_name('Database')
487
487
488 issue = Issue.find(1)
488 issue = Issue.find(1)
489 issue.custom_field_values = { field.id => 'PostgreSQL' }
489 issue.custom_field_values = { field.id => 'PostgreSQL' }
490 assert issue.save
490 assert issue.save
491 custom_value = issue.custom_value_for(field)
491 custom_value = issue.custom_value_for(field)
492 issue.reload
492 issue.reload
493 issue.custom_field_values = { field.id => 'MySQL' }
493 issue.custom_field_values = { field.id => 'MySQL' }
494 assert issue.save
494 assert issue.save
495 issue.reload
495 issue.reload
496 assert_equal custom_value.id, issue.custom_value_for(field).id
496 assert_equal custom_value.id, issue.custom_value_for(field).id
497 end
497 end
498
498
499 def test_setting_project_should_set_version_to_default_version
499 def test_setting_project_should_set_version_to_default_version
500 version = Version.generate!(:project_id => 1)
500 version = Version.generate!(:project_id => 1)
501 Project.find(1).update_attribute(:default_version_id, version.id)
501 Project.find(1).update_attribute(:default_version_id, version.id)
502
502
503 issue = Issue.new(:project_id => 1)
503 issue = Issue.new(:project_id => 1)
504 assert_equal version, issue.fixed_version
504 assert_equal version, issue.fixed_version
505 end
505 end
506
506
507 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
507 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
508 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
508 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
509 :status_id => 1, :subject => 'Test',
509 :status_id => 1, :subject => 'Test',
510 :custom_field_values => {'2' => 'Test'})
510 :custom_field_values => {'2' => 'Test'})
511 assert !Tracker.find(2).custom_field_ids.include?(2)
511 assert !Tracker.find(2).custom_field_ids.include?(2)
512
512
513 issue = Issue.find(issue.id)
513 issue = Issue.find(issue.id)
514 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
514 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
515
515
516 issue = Issue.find(issue.id)
516 issue = Issue.find(issue.id)
517 custom_value = issue.custom_value_for(2)
517 custom_value = issue.custom_value_for(2)
518 assert_not_nil custom_value
518 assert_not_nil custom_value
519 assert_equal 'Test', custom_value.value
519 assert_equal 'Test', custom_value.value
520 end
520 end
521
521
522 def test_assigning_tracker_id_should_reload_custom_fields_values
522 def test_assigning_tracker_id_should_reload_custom_fields_values
523 issue = Issue.new(:project => Project.find(1))
523 issue = Issue.new(:project => Project.find(1))
524 assert issue.custom_field_values.empty?
524 assert issue.custom_field_values.empty?
525 issue.tracker_id = 1
525 issue.tracker_id = 1
526 assert issue.custom_field_values.any?
526 assert issue.custom_field_values.any?
527 end
527 end
528
528
529 def test_assigning_attributes_should_assign_project_and_tracker_first
529 def test_assigning_attributes_should_assign_project_and_tracker_first
530 seq = sequence('seq')
530 seq = sequence('seq')
531 issue = Issue.new
531 issue = Issue.new
532 issue.expects(:project_id=).in_sequence(seq)
532 issue.expects(:project_id=).in_sequence(seq)
533 issue.expects(:tracker_id=).in_sequence(seq)
533 issue.expects(:tracker_id=).in_sequence(seq)
534 issue.expects(:subject=).in_sequence(seq)
534 issue.expects(:subject=).in_sequence(seq)
535 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
535 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
536 end
536 end
537
537
538 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
538 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
539 attributes = ActiveSupport::OrderedHash.new
539 attributes = ActiveSupport::OrderedHash.new
540 attributes['custom_field_values'] = { '1' => 'MySQL' }
540 attributes['custom_field_values'] = { '1' => 'MySQL' }
541 attributes['tracker_id'] = '1'
541 attributes['tracker_id'] = '1'
542 issue = Issue.new(:project => Project.find(1))
542 issue = Issue.new(:project => Project.find(1))
543 issue.attributes = attributes
543 issue.attributes = attributes
544 assert_equal 'MySQL', issue.custom_field_value(1)
544 assert_equal 'MySQL', issue.custom_field_value(1)
545 end
545 end
546
546
547 def test_changing_tracker_should_clear_disabled_core_fields
547 def test_changing_tracker_should_clear_disabled_core_fields
548 tracker = Tracker.find(2)
548 tracker = Tracker.find(2)
549 tracker.core_fields = tracker.core_fields - %w(due_date)
549 tracker.core_fields = tracker.core_fields - %w(due_date)
550 tracker.save!
550 tracker.save!
551
551
552 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
552 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
553 issue.save!
553 issue.save!
554
554
555 issue.tracker_id = 2
555 issue.tracker_id = 2
556 issue.save!
556 issue.save!
557 assert_not_nil issue.start_date
557 assert_not_nil issue.start_date
558 assert_nil issue.due_date
558 assert_nil issue.due_date
559 end
559 end
560
560
561 def test_changing_tracker_should_not_add_cleared_fields_to_journal
561 def test_changing_tracker_should_not_add_cleared_fields_to_journal
562 tracker = Tracker.find(2)
562 tracker = Tracker.find(2)
563 tracker.core_fields = tracker.core_fields - %w(due_date)
563 tracker.core_fields = tracker.core_fields - %w(due_date)
564 tracker.save!
564 tracker.save!
565
565
566 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
566 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
567 issue.save!
567 issue.save!
568
568
569 assert_difference 'Journal.count' do
569 assert_difference 'Journal.count' do
570 issue.init_journal User.find(1)
570 issue.init_journal User.find(1)
571 issue.tracker_id = 2
571 issue.tracker_id = 2
572 issue.save!
572 issue.save!
573 assert_nil issue.due_date
573 assert_nil issue.due_date
574 end
574 end
575 journal = Journal.order('id DESC').first
575 journal = Journal.order('id DESC').first
576 assert_equal 1, journal.details.count
576 assert_equal 1, journal.details.count
577 end
577 end
578
578
579 def test_reload_should_reload_custom_field_values
579 def test_reload_should_reload_custom_field_values
580 issue = Issue.generate!
580 issue = Issue.generate!
581 issue.custom_field_values = {'2' => 'Foo'}
581 issue.custom_field_values = {'2' => 'Foo'}
582 issue.save!
582 issue.save!
583
583
584 issue = Issue.order('id desc').first
584 issue = Issue.order('id desc').first
585 assert_equal 'Foo', issue.custom_field_value(2)
585 assert_equal 'Foo', issue.custom_field_value(2)
586
586
587 issue.custom_field_values = {'2' => 'Bar'}
587 issue.custom_field_values = {'2' => 'Bar'}
588 assert_equal 'Bar', issue.custom_field_value(2)
588 assert_equal 'Bar', issue.custom_field_value(2)
589
589
590 issue.reload
590 issue.reload
591 assert_equal 'Foo', issue.custom_field_value(2)
591 assert_equal 'Foo', issue.custom_field_value(2)
592 end
592 end
593
593
594 def test_should_update_issue_with_disabled_tracker
594 def test_should_update_issue_with_disabled_tracker
595 p = Project.find(1)
595 p = Project.find(1)
596 issue = Issue.find(1)
596 issue = Issue.find(1)
597
597
598 p.trackers.delete(issue.tracker)
598 p.trackers.delete(issue.tracker)
599 assert !p.trackers.include?(issue.tracker)
599 assert !p.trackers.include?(issue.tracker)
600
600
601 issue.reload
601 issue.reload
602 issue.subject = 'New subject'
602 issue.subject = 'New subject'
603 assert issue.save
603 assert issue.save
604 end
604 end
605
605
606 def test_should_not_set_a_disabled_tracker
606 def test_should_not_set_a_disabled_tracker
607 p = Project.find(1)
607 p = Project.find(1)
608 p.trackers.delete(Tracker.find(2))
608 p.trackers.delete(Tracker.find(2))
609
609
610 issue = Issue.find(1)
610 issue = Issue.find(1)
611 issue.tracker_id = 2
611 issue.tracker_id = 2
612 issue.subject = 'New subject'
612 issue.subject = 'New subject'
613 assert !issue.save
613 assert !issue.save
614 assert_not_equal [], issue.errors[:tracker_id]
614 assert_not_equal [], issue.errors[:tracker_id]
615 end
615 end
616
616
617 def test_category_based_assignment
617 def test_category_based_assignment
618 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
618 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
619 :status_id => 1, :priority => IssuePriority.all.first,
619 :status_id => 1, :priority => IssuePriority.all.first,
620 :subject => 'Assignment test',
620 :subject => 'Assignment test',
621 :description => 'Assignment test', :category_id => 1)
621 :description => 'Assignment test', :category_id => 1)
622 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
622 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
623 end
623 end
624
624
625 def test_new_statuses_allowed_to
625 def test_new_statuses_allowed_to
626 WorkflowTransition.delete_all
626 WorkflowTransition.delete_all
627 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
627 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
628 :old_status_id => 1, :new_status_id => 2,
628 :old_status_id => 1, :new_status_id => 2,
629 :author => false, :assignee => false)
629 :author => false, :assignee => false)
630 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
630 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
631 :old_status_id => 1, :new_status_id => 3,
631 :old_status_id => 1, :new_status_id => 3,
632 :author => true, :assignee => false)
632 :author => true, :assignee => false)
633 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
633 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
634 :old_status_id => 1, :new_status_id => 4,
634 :old_status_id => 1, :new_status_id => 4,
635 :author => false, :assignee => true)
635 :author => false, :assignee => true)
636 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
636 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
637 :old_status_id => 1, :new_status_id => 5,
637 :old_status_id => 1, :new_status_id => 5,
638 :author => true, :assignee => true)
638 :author => true, :assignee => true)
639 status = IssueStatus.find(1)
639 status = IssueStatus.find(1)
640 role = Role.find(1)
640 role = Role.find(1)
641 tracker = Tracker.find(1)
641 tracker = Tracker.find(1)
642 user = User.find(2)
642 user = User.find(2)
643
643
644 issue = Issue.generate!(:tracker => tracker, :status => status,
644 issue = Issue.generate!(:tracker => tracker, :status => status,
645 :project_id => 1, :author_id => 1)
645 :project_id => 1, :author_id => 1)
646 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
646 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
647
647
648 issue = Issue.generate!(:tracker => tracker, :status => status,
648 issue = Issue.generate!(:tracker => tracker, :status => status,
649 :project_id => 1, :author => user)
649 :project_id => 1, :author => user)
650 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
650 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
651
651
652 issue = Issue.generate!(:tracker => tracker, :status => status,
652 issue = Issue.generate!(:tracker => tracker, :status => status,
653 :project_id => 1, :author_id => 1,
653 :project_id => 1, :author_id => 1,
654 :assigned_to => user)
654 :assigned_to => user)
655 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
655 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
656
656
657 issue = Issue.generate!(:tracker => tracker, :status => status,
657 issue = Issue.generate!(:tracker => tracker, :status => status,
658 :project_id => 1, :author => user,
658 :project_id => 1, :author => user,
659 :assigned_to => user)
659 :assigned_to => user)
660 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
660 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
661
661
662 group = Group.generate!
662 group = Group.generate!
663 group.users << user
663 group.users << user
664 issue = Issue.generate!(:tracker => tracker, :status => status,
664 issue = Issue.generate!(:tracker => tracker, :status => status,
665 :project_id => 1, :author => user,
665 :project_id => 1, :author => user,
666 :assigned_to => group)
666 :assigned_to => group)
667 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
667 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
668 end
668 end
669
669
670 def test_new_statuses_allowed_to_should_consider_group_assignment
670 def test_new_statuses_allowed_to_should_consider_group_assignment
671 WorkflowTransition.delete_all
671 WorkflowTransition.delete_all
672 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
672 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
673 :old_status_id => 1, :new_status_id => 4,
673 :old_status_id => 1, :new_status_id => 4,
674 :author => false, :assignee => true)
674 :author => false, :assignee => true)
675 user = User.find(2)
675 user = User.find(2)
676 group = Group.generate!
676 group = Group.generate!
677 group.users << user
677 group.users << user
678
678
679 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
679 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
680 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
680 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
681 end
681 end
682
682
683 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
683 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
684 admin = User.find(1)
684 admin = User.find(1)
685 issue = Issue.find(1)
685 issue = Issue.find(1)
686 assert !admin.member_of?(issue.project)
686 assert !admin.member_of?(issue.project)
687 expected_statuses = [issue.status] +
687 expected_statuses = [issue.status] +
688 WorkflowTransition.where(:old_status_id => issue.status_id).
688 WorkflowTransition.where(:old_status_id => issue.status_id).
689 map(&:new_status).uniq.sort
689 map(&:new_status).uniq.sort
690 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
690 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
691 end
691 end
692
692
693 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
693 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
694 issue = Issue.find(1).copy
694 issue = Issue.find(1).copy
695 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
695 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
696
696
697 issue = Issue.find(2).copy
697 issue = Issue.find(2).copy
698 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
698 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
699 end
699 end
700
700
701 def test_safe_attributes_names_should_not_include_disabled_field
701 def test_safe_attributes_names_should_not_include_disabled_field
702 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
702 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
703
703
704 issue = Issue.new(:tracker => tracker)
704 issue = Issue.new(:tracker => tracker)
705 assert_include 'tracker_id', issue.safe_attribute_names
705 assert_include 'tracker_id', issue.safe_attribute_names
706 assert_include 'status_id', issue.safe_attribute_names
706 assert_include 'status_id', issue.safe_attribute_names
707 assert_include 'subject', issue.safe_attribute_names
707 assert_include 'subject', issue.safe_attribute_names
708 assert_include 'description', issue.safe_attribute_names
708 assert_include 'description', issue.safe_attribute_names
709 assert_include 'custom_field_values', issue.safe_attribute_names
709 assert_include 'custom_field_values', issue.safe_attribute_names
710 assert_include 'custom_fields', issue.safe_attribute_names
710 assert_include 'custom_fields', issue.safe_attribute_names
711 assert_include 'lock_version', issue.safe_attribute_names
711 assert_include 'lock_version', issue.safe_attribute_names
712
712
713 tracker.core_fields.each do |field|
713 tracker.core_fields.each do |field|
714 assert_include field, issue.safe_attribute_names
714 assert_include field, issue.safe_attribute_names
715 end
715 end
716
716
717 tracker.disabled_core_fields.each do |field|
717 tracker.disabled_core_fields.each do |field|
718 assert_not_include field, issue.safe_attribute_names
718 assert_not_include field, issue.safe_attribute_names
719 end
719 end
720 end
720 end
721
721
722 def test_safe_attributes_should_ignore_disabled_fields
722 def test_safe_attributes_should_ignore_disabled_fields
723 tracker = Tracker.find(1)
723 tracker = Tracker.find(1)
724 tracker.core_fields = %w(assigned_to_id due_date)
724 tracker.core_fields = %w(assigned_to_id due_date)
725 tracker.save!
725 tracker.save!
726
726
727 issue = Issue.new(:tracker => tracker)
727 issue = Issue.new(:tracker => tracker)
728 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
728 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
729 assert_nil issue.start_date
729 assert_nil issue.start_date
730 assert_equal Date.parse('2012-07-14'), issue.due_date
730 assert_equal Date.parse('2012-07-14'), issue.due_date
731 end
731 end
732
732
733 def test_safe_attributes_should_accept_target_tracker_enabled_fields
733 def test_safe_attributes_should_accept_target_tracker_enabled_fields
734 source = Tracker.find(1)
734 source = Tracker.find(1)
735 source.core_fields = []
735 source.core_fields = []
736 source.save!
736 source.save!
737 target = Tracker.find(2)
737 target = Tracker.find(2)
738 target.core_fields = %w(assigned_to_id due_date)
738 target.core_fields = %w(assigned_to_id due_date)
739 target.save!
739 target.save!
740
740
741 issue = Issue.new(:tracker => source)
741 issue = Issue.new(:tracker => source)
742 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
742 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
743 assert_equal target, issue.tracker
743 assert_equal target, issue.tracker
744 assert_equal Date.parse('2012-07-14'), issue.due_date
744 assert_equal Date.parse('2012-07-14'), issue.due_date
745 end
745 end
746
746
747 def test_safe_attributes_should_not_include_readonly_fields
747 def test_safe_attributes_should_not_include_readonly_fields
748 WorkflowPermission.delete_all
748 WorkflowPermission.delete_all
749 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
749 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
750 :role_id => 1, :field_name => 'due_date',
750 :role_id => 1, :field_name => 'due_date',
751 :rule => 'readonly')
751 :rule => 'readonly')
752 user = User.find(2)
752 user = User.find(2)
753
753
754 issue = Issue.new(:project_id => 1, :tracker_id => 1)
754 issue = Issue.new(:project_id => 1, :tracker_id => 1)
755 assert_equal %w(due_date), issue.read_only_attribute_names(user)
755 assert_equal %w(due_date), issue.read_only_attribute_names(user)
756 assert_not_include 'due_date', issue.safe_attribute_names(user)
756 assert_not_include 'due_date', issue.safe_attribute_names(user)
757
757
758 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
758 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
759 assert_equal Date.parse('2012-07-14'), issue.start_date
759 assert_equal Date.parse('2012-07-14'), issue.start_date
760 assert_nil issue.due_date
760 assert_nil issue.due_date
761 end
761 end
762
762
763 def test_safe_attributes_should_not_include_readonly_custom_fields
763 def test_safe_attributes_should_not_include_readonly_custom_fields
764 cf1 = IssueCustomField.create!(:name => 'Writable field',
764 cf1 = IssueCustomField.create!(:name => 'Writable field',
765 :field_format => 'string',
765 :field_format => 'string',
766 :is_for_all => true, :tracker_ids => [1])
766 :is_for_all => true, :tracker_ids => [1])
767 cf2 = IssueCustomField.create!(:name => 'Readonly field',
767 cf2 = IssueCustomField.create!(:name => 'Readonly field',
768 :field_format => 'string',
768 :field_format => 'string',
769 :is_for_all => true, :tracker_ids => [1])
769 :is_for_all => true, :tracker_ids => [1])
770 WorkflowPermission.delete_all
770 WorkflowPermission.delete_all
771 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
771 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
772 :role_id => 1, :field_name => cf2.id.to_s,
772 :role_id => 1, :field_name => cf2.id.to_s,
773 :rule => 'readonly')
773 :rule => 'readonly')
774 user = User.find(2)
774 user = User.find(2)
775 issue = Issue.new(:project_id => 1, :tracker_id => 1)
775 issue = Issue.new(:project_id => 1, :tracker_id => 1)
776 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
776 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
777 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
777 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
778
778
779 issue.send :safe_attributes=, {'custom_field_values' => {
779 issue.send :safe_attributes=, {'custom_field_values' => {
780 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
780 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
781 }}, user
781 }}, user
782 assert_equal 'value1', issue.custom_field_value(cf1)
782 assert_equal 'value1', issue.custom_field_value(cf1)
783 assert_nil issue.custom_field_value(cf2)
783 assert_nil issue.custom_field_value(cf2)
784
784
785 issue.send :safe_attributes=, {'custom_fields' => [
785 issue.send :safe_attributes=, {'custom_fields' => [
786 {'id' => cf1.id.to_s, 'value' => 'valuea'},
786 {'id' => cf1.id.to_s, 'value' => 'valuea'},
787 {'id' => cf2.id.to_s, 'value' => 'valueb'}
787 {'id' => cf2.id.to_s, 'value' => 'valueb'}
788 ]}, user
788 ]}, user
789 assert_equal 'valuea', issue.custom_field_value(cf1)
789 assert_equal 'valuea', issue.custom_field_value(cf1)
790 assert_nil issue.custom_field_value(cf2)
790 assert_nil issue.custom_field_value(cf2)
791 end
791 end
792
792
793 def test_safe_attributes_should_ignore_unassignable_assignee
793 def test_safe_attributes_should_ignore_unassignable_assignee
794 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
794 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
795 :status_id => 1, :priority => IssuePriority.all.first,
795 :status_id => 1, :priority => IssuePriority.all.first,
796 :subject => 'test_create')
796 :subject => 'test_create')
797 assert issue.valid?
797 assert issue.valid?
798
798
799 # locked user, not allowed
799 # locked user, not allowed
800 issue.safe_attributes=({'assigned_to_id' => '5'})
800 issue.safe_attributes=({'assigned_to_id' => '5'})
801 assert_nil issue.assigned_to_id
801 assert_nil issue.assigned_to_id
802 # no member
802 # no member
803 issue.safe_attributes=({'assigned_to_id' => '1'})
803 issue.safe_attributes=({'assigned_to_id' => '1'})
804 assert_nil issue.assigned_to_id
804 assert_nil issue.assigned_to_id
805 # user 2 is ok
805 # user 2 is ok
806 issue.safe_attributes=({'assigned_to_id' => '2'})
806 issue.safe_attributes=({'assigned_to_id' => '2'})
807 assert_equal 2, issue.assigned_to_id
807 assert_equal 2, issue.assigned_to_id
808 assert issue.save
808 assert issue.save
809
809
810 issue.reload
810 issue.reload
811 assert_equal 2, issue.assigned_to_id
811 assert_equal 2, issue.assigned_to_id
812 issue.safe_attributes=({'assigned_to_id' => '5'})
812 issue.safe_attributes=({'assigned_to_id' => '5'})
813 assert_equal 2, issue.assigned_to_id
813 assert_equal 2, issue.assigned_to_id
814 issue.safe_attributes=({'assigned_to_id' => '1'})
814 issue.safe_attributes=({'assigned_to_id' => '1'})
815 assert_equal 2, issue.assigned_to_id
815 assert_equal 2, issue.assigned_to_id
816 # user 3 is also ok
816 # user 3 is also ok
817 issue.safe_attributes=({'assigned_to_id' => '3'})
817 issue.safe_attributes=({'assigned_to_id' => '3'})
818 assert_equal 3, issue.assigned_to_id
818 assert_equal 3, issue.assigned_to_id
819 assert issue.save
819 assert issue.save
820
820
821 # removal of assignee
821 # removal of assignee
822 issue.safe_attributes=({'assigned_to_id' => ''})
822 issue.safe_attributes=({'assigned_to_id' => ''})
823 assert_nil issue.assigned_to_id
823 assert_nil issue.assigned_to_id
824 assert issue.save
824 assert issue.save
825 end
825 end
826
826
827 def test_editable_custom_field_values_should_return_non_readonly_custom_values
827 def test_editable_custom_field_values_should_return_non_readonly_custom_values
828 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
828 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
829 :is_for_all => true, :tracker_ids => [1, 2])
829 :is_for_all => true, :tracker_ids => [1, 2])
830 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
830 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
831 :is_for_all => true, :tracker_ids => [1, 2])
831 :is_for_all => true, :tracker_ids => [1, 2])
832 WorkflowPermission.delete_all
832 WorkflowPermission.delete_all
833 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
833 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
834 :field_name => cf2.id.to_s, :rule => 'readonly')
834 :field_name => cf2.id.to_s, :rule => 'readonly')
835 user = User.find(2)
835 user = User.find(2)
836
836
837 issue = Issue.new(:project_id => 1, :tracker_id => 1)
837 issue = Issue.new(:project_id => 1, :tracker_id => 1)
838 values = issue.editable_custom_field_values(user)
838 values = issue.editable_custom_field_values(user)
839 assert values.detect {|value| value.custom_field == cf1}
839 assert values.detect {|value| value.custom_field == cf1}
840 assert_nil values.detect {|value| value.custom_field == cf2}
840 assert_nil values.detect {|value| value.custom_field == cf2}
841
841
842 issue.tracker_id = 2
842 issue.tracker_id = 2
843 values = issue.editable_custom_field_values(user)
843 values = issue.editable_custom_field_values(user)
844 assert values.detect {|value| value.custom_field == cf1}
844 assert values.detect {|value| value.custom_field == cf1}
845 assert values.detect {|value| value.custom_field == cf2}
845 assert values.detect {|value| value.custom_field == cf2}
846 end
846 end
847
847
848 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
848 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
849 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
849 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
850 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
850 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
851 user = User.find(2)
851 user = User.find(2)
852 issue = Issue.new(:project_id => 1, :tracker_id => 1)
852 issue = Issue.new(:project_id => 1, :tracker_id => 1)
853
853
854 assert_include enabled_cf, issue.editable_custom_fields(user)
854 assert_include enabled_cf, issue.editable_custom_fields(user)
855 assert_not_include disabled_cf, issue.editable_custom_fields(user)
855 assert_not_include disabled_cf, issue.editable_custom_fields(user)
856 end
856 end
857
857
858 def test_safe_attributes_should_accept_target_tracker_writable_fields
858 def test_safe_attributes_should_accept_target_tracker_writable_fields
859 WorkflowPermission.delete_all
859 WorkflowPermission.delete_all
860 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
860 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
861 :role_id => 1, :field_name => 'due_date',
861 :role_id => 1, :field_name => 'due_date',
862 :rule => 'readonly')
862 :rule => 'readonly')
863 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
863 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
864 :role_id => 1, :field_name => 'start_date',
864 :role_id => 1, :field_name => 'start_date',
865 :rule => 'readonly')
865 :rule => 'readonly')
866 user = User.find(2)
866 user = User.find(2)
867
867
868 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
868 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
869
869
870 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
870 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
871 'due_date' => '2012-07-14'}, user
871 'due_date' => '2012-07-14'}, user
872 assert_equal Date.parse('2012-07-12'), issue.start_date
872 assert_equal Date.parse('2012-07-12'), issue.start_date
873 assert_nil issue.due_date
873 assert_nil issue.due_date
874
874
875 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
875 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
876 'due_date' => '2012-07-16',
876 'due_date' => '2012-07-16',
877 'tracker_id' => 2}, user
877 'tracker_id' => 2}, user
878 assert_equal Date.parse('2012-07-12'), issue.start_date
878 assert_equal Date.parse('2012-07-12'), issue.start_date
879 assert_equal Date.parse('2012-07-16'), issue.due_date
879 assert_equal Date.parse('2012-07-16'), issue.due_date
880 end
880 end
881
881
882 def test_safe_attributes_should_accept_target_status_writable_fields
882 def test_safe_attributes_should_accept_target_status_writable_fields
883 WorkflowPermission.delete_all
883 WorkflowPermission.delete_all
884 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
884 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
885 :role_id => 1, :field_name => 'due_date',
885 :role_id => 1, :field_name => 'due_date',
886 :rule => 'readonly')
886 :rule => 'readonly')
887 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
887 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
888 :role_id => 1, :field_name => 'start_date',
888 :role_id => 1, :field_name => 'start_date',
889 :rule => 'readonly')
889 :rule => 'readonly')
890 user = User.find(2)
890 user = User.find(2)
891
891
892 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
892 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
893
893
894 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
894 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
895 'due_date' => '2012-07-14'},
895 'due_date' => '2012-07-14'},
896 user
896 user
897 assert_equal Date.parse('2012-07-12'), issue.start_date
897 assert_equal Date.parse('2012-07-12'), issue.start_date
898 assert_nil issue.due_date
898 assert_nil issue.due_date
899
899
900 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
900 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
901 'due_date' => '2012-07-16',
901 'due_date' => '2012-07-16',
902 'status_id' => 2},
902 'status_id' => 2},
903 user
903 user
904 assert_equal Date.parse('2012-07-12'), issue.start_date
904 assert_equal Date.parse('2012-07-12'), issue.start_date
905 assert_equal Date.parse('2012-07-16'), issue.due_date
905 assert_equal Date.parse('2012-07-16'), issue.due_date
906 end
906 end
907
907
908 def test_required_attributes_should_be_validated
908 def test_required_attributes_should_be_validated
909 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
909 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
910 :is_for_all => true, :tracker_ids => [1, 2])
910 :is_for_all => true, :tracker_ids => [1, 2])
911
911
912 WorkflowPermission.delete_all
912 WorkflowPermission.delete_all
913 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
913 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
914 :role_id => 1, :field_name => 'due_date',
914 :role_id => 1, :field_name => 'due_date',
915 :rule => 'required')
915 :rule => 'required')
916 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
916 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
917 :role_id => 1, :field_name => 'category_id',
917 :role_id => 1, :field_name => 'category_id',
918 :rule => 'required')
918 :rule => 'required')
919 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
919 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
920 :role_id => 1, :field_name => cf.id.to_s,
920 :role_id => 1, :field_name => cf.id.to_s,
921 :rule => 'required')
921 :rule => 'required')
922
922
923 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
923 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
924 :role_id => 1, :field_name => 'start_date',
924 :role_id => 1, :field_name => 'start_date',
925 :rule => 'required')
925 :rule => 'required')
926 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
926 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
927 :role_id => 1, :field_name => cf.id.to_s,
927 :role_id => 1, :field_name => cf.id.to_s,
928 :rule => 'required')
928 :rule => 'required')
929 user = User.find(2)
929 user = User.find(2)
930
930
931 issue = Issue.new(:project_id => 1, :tracker_id => 1,
931 issue = Issue.new(:project_id => 1, :tracker_id => 1,
932 :status_id => 1, :subject => 'Required fields',
932 :status_id => 1, :subject => 'Required fields',
933 :author => user)
933 :author => user)
934 assert_equal [cf.id.to_s, "category_id", "due_date"],
934 assert_equal [cf.id.to_s, "category_id", "due_date"],
935 issue.required_attribute_names(user).sort
935 issue.required_attribute_names(user).sort
936 assert !issue.save, "Issue was saved"
936 assert !issue.save, "Issue was saved"
937 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
937 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
938 issue.errors.full_messages.sort
938 issue.errors.full_messages.sort
939
939
940 issue.tracker_id = 2
940 issue.tracker_id = 2
941 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
941 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
942 assert !issue.save, "Issue was saved"
942 assert !issue.save, "Issue was saved"
943 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
943 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
944 issue.errors.full_messages.sort
944 issue.errors.full_messages.sort
945
945
946 issue.start_date = Date.today
946 issue.start_date = Date.today
947 issue.custom_field_values = {cf.id.to_s => 'bar'}
947 issue.custom_field_values = {cf.id.to_s => 'bar'}
948 assert issue.save
948 assert issue.save
949 end
949 end
950
950
951 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
951 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
952 WorkflowPermission.delete_all
952 WorkflowPermission.delete_all
953 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
953 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
954 :role_id => 1, :field_name => 'start_date',
954 :role_id => 1, :field_name => 'start_date',
955 :rule => 'required')
955 :rule => 'required')
956 user = User.find(2)
956 user = User.find(2)
957
957
958 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
958 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
959 :subject => 'Required fields', :author => user)
959 :subject => 'Required fields', :author => user)
960 assert !issue.save
960 assert !issue.save
961 assert_include "Start date cannot be blank", issue.errors.full_messages
961 assert_include "Start date cannot be blank", issue.errors.full_messages
962
962
963 tracker = Tracker.find(1)
963 tracker = Tracker.find(1)
964 tracker.core_fields -= %w(start_date)
964 tracker.core_fields -= %w(start_date)
965 tracker.save!
965 tracker.save!
966 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
966 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
967 :subject => 'Required fields', :author => user)
967 :subject => 'Required fields', :author => user)
968 assert issue.save
968 assert issue.save
969 end
969 end
970
970
971 def test_category_should_not_be_required_if_project_has_no_categories
971 def test_category_should_not_be_required_if_project_has_no_categories
972 Project.find(1).issue_categories.delete_all
972 Project.find(1).issue_categories.delete_all
973 WorkflowPermission.delete_all
973 WorkflowPermission.delete_all
974 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
974 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
975 :role_id => 1, :field_name => 'category_id',:rule => 'required')
975 :role_id => 1, :field_name => 'category_id',:rule => 'required')
976 user = User.find(2)
976 user = User.find(2)
977
977
978 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
978 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
979 :subject => 'Required fields', :author => user)
979 :subject => 'Required fields', :author => user)
980 assert_save issue
980 assert_save issue
981 end
981 end
982
982
983 def test_fixed_version_should_not_be_required_no_assignable_versions
983 def test_fixed_version_should_not_be_required_no_assignable_versions
984 Version.delete_all
984 Version.delete_all
985 WorkflowPermission.delete_all
985 WorkflowPermission.delete_all
986 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
986 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
987 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
987 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
988 user = User.find(2)
988 user = User.find(2)
989
989
990 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
990 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
991 :subject => 'Required fields', :author => user)
991 :subject => 'Required fields', :author => user)
992 assert_save issue
992 assert_save issue
993 end
993 end
994
994
995 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
995 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
996 CustomField.delete_all
996 CustomField.delete_all
997 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
997 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
998 user = User.generate!
998 user = User.generate!
999 User.add_to_project(user, Project.find(1), Role.find(2))
999 User.add_to_project(user, Project.find(1), Role.find(2))
1000
1000
1001 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1001 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1002 :subject => 'Required fields', :author => user)
1002 :subject => 'Required fields', :author => user)
1003 assert_save issue
1003 assert_save issue
1004 end
1004 end
1005
1005
1006 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1006 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1007 CustomField.delete_all
1007 CustomField.delete_all
1008 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1008 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1009 user = User.generate!
1009 user = User.generate!
1010 User.add_to_project(user, Project.find(1), Role.find(1))
1010 User.add_to_project(user, Project.find(1), Role.find(1))
1011
1011
1012 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1012 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1013 :subject => 'Required fields', :author => user)
1013 :subject => 'Required fields', :author => user)
1014 assert !issue.save
1014 assert !issue.save
1015 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1015 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1016 end
1016 end
1017
1017
1018 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1018 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1019 WorkflowPermission.delete_all
1019 WorkflowPermission.delete_all
1020 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1020 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1021 :role_id => 1, :field_name => 'due_date',
1021 :role_id => 1, :field_name => 'due_date',
1022 :rule => 'required')
1022 :rule => 'required')
1023 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1023 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1024 :role_id => 1, :field_name => 'start_date',
1024 :role_id => 1, :field_name => 'start_date',
1025 :rule => 'required')
1025 :rule => 'required')
1026 user = User.find(2)
1026 user = User.find(2)
1027 member = Member.find(1)
1027 member = Member.find(1)
1028 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1028 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1029
1029
1030 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1030 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1031
1031
1032 member.role_ids = [1, 2]
1032 member.role_ids = [1, 2]
1033 member.save!
1033 member.save!
1034 assert_equal [], issue.required_attribute_names(user.reload)
1034 assert_equal [], issue.required_attribute_names(user.reload)
1035
1035
1036 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1036 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1037 :role_id => 2, :field_name => 'due_date',
1037 :role_id => 2, :field_name => 'due_date',
1038 :rule => 'required')
1038 :rule => 'required')
1039 assert_equal %w(due_date), issue.required_attribute_names(user)
1039 assert_equal %w(due_date), issue.required_attribute_names(user)
1040
1040
1041 member.role_ids = [1, 2, 3]
1041 member.role_ids = [1, 2, 3]
1042 member.save!
1042 member.save!
1043 assert_equal [], issue.required_attribute_names(user.reload)
1043 assert_equal [], issue.required_attribute_names(user.reload)
1044
1044
1045 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1045 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1046 :role_id => 3, :field_name => 'due_date',
1046 :role_id => 3, :field_name => 'due_date',
1047 :rule => 'readonly')
1047 :rule => 'readonly')
1048 # required + readonly => required
1048 # required + readonly => required
1049 assert_equal %w(due_date), issue.required_attribute_names(user)
1049 assert_equal %w(due_date), issue.required_attribute_names(user)
1050 end
1050 end
1051
1051
1052 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1052 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1053 WorkflowPermission.delete_all
1053 WorkflowPermission.delete_all
1054 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1054 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1055 :role_id => 1, :field_name => 'due_date',
1055 :role_id => 1, :field_name => 'due_date',
1056 :rule => 'readonly')
1056 :rule => 'readonly')
1057 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1057 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1058 :role_id => 1, :field_name => 'start_date',
1058 :role_id => 1, :field_name => 'start_date',
1059 :rule => 'readonly')
1059 :rule => 'readonly')
1060 user = User.find(2)
1060 user = User.find(2)
1061 member = Member.find(1)
1061 member = Member.find(1)
1062 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1062 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1063
1063
1064 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1064 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1065
1065
1066 member.role_ids = [1, 2]
1066 member.role_ids = [1, 2]
1067 member.save!
1067 member.save!
1068 assert_equal [], issue.read_only_attribute_names(user.reload)
1068 assert_equal [], issue.read_only_attribute_names(user.reload)
1069
1069
1070 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1070 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1071 :role_id => 2, :field_name => 'due_date',
1071 :role_id => 2, :field_name => 'due_date',
1072 :rule => 'readonly')
1072 :rule => 'readonly')
1073 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1073 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1074 end
1074 end
1075
1075
1076 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1076 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1077 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1077 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1078 field = IssueCustomField.generate!(
1078 field = IssueCustomField.generate!(
1079 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1079 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1080 )
1080 )
1081 WorkflowPermission.delete_all
1081 WorkflowPermission.delete_all
1082 WorkflowPermission.create!(
1082 WorkflowPermission.create!(
1083 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1083 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1084 )
1084 )
1085 user = User.generate!
1085 user = User.generate!
1086 project = Project.find(1)
1086 project = Project.find(1)
1087 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1087 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1088
1088
1089 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1089 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1090 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1090 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1091 end
1091 end
1092
1092
1093 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1093 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1094 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1094 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1095 ignored_role = Role.generate! :permissions => [:view_issues]
1095 ignored_role = Role.generate! :permissions => [:view_issues]
1096
1096
1097 WorkflowPermission.delete_all
1097 WorkflowPermission.delete_all
1098 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1098 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1099 :role => role, :field_name => 'due_date',
1099 :role => role, :field_name => 'due_date',
1100 :rule => 'required')
1100 :rule => 'required')
1101 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1101 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1102 :role => role, :field_name => 'start_date',
1102 :role => role, :field_name => 'start_date',
1103 :rule => 'readonly')
1103 :rule => 'readonly')
1104 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1104 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1105 :role => role, :field_name => 'done_ratio',
1105 :role => role, :field_name => 'done_ratio',
1106 :rule => 'readonly')
1106 :rule => 'readonly')
1107 user = User.generate!
1107 user = User.generate!
1108 User.add_to_project user, Project.find(1), [role, ignored_role]
1108 User.add_to_project user, Project.find(1), [role, ignored_role]
1109
1109
1110 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1110 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1111
1111
1112 assert_equal %w(due_date), issue.required_attribute_names(user)
1112 assert_equal %w(due_date), issue.required_attribute_names(user)
1113 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1113 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1114 end
1114 end
1115
1115
1116 def test_workflow_rules_should_work_for_member_with_duplicate_role
1116 def test_workflow_rules_should_work_for_member_with_duplicate_role
1117 WorkflowPermission.delete_all
1117 WorkflowPermission.delete_all
1118 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1118 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1119 :role_id => 1, :field_name => 'due_date',
1119 :role_id => 1, :field_name => 'due_date',
1120 :rule => 'required')
1120 :rule => 'required')
1121 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1121 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1122 :role_id => 1, :field_name => 'start_date',
1122 :role_id => 1, :field_name => 'start_date',
1123 :rule => 'readonly')
1123 :rule => 'readonly')
1124
1124
1125 user = User.generate!
1125 user = User.generate!
1126 m = Member.new(:user_id => user.id, :project_id => 1)
1126 m = Member.new(:user_id => user.id, :project_id => 1)
1127 m.member_roles.build(:role_id => 1)
1127 m.member_roles.build(:role_id => 1)
1128 m.member_roles.build(:role_id => 1)
1128 m.member_roles.build(:role_id => 1)
1129 m.save!
1129 m.save!
1130
1130
1131 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1131 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1132
1132
1133 assert_equal %w(due_date), issue.required_attribute_names(user)
1133 assert_equal %w(due_date), issue.required_attribute_names(user)
1134 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1134 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1135 end
1135 end
1136
1136
1137 def test_copy
1137 def test_copy
1138 issue = Issue.new.copy_from(1)
1138 issue = Issue.new.copy_from(1)
1139 assert issue.copy?
1139 assert issue.copy?
1140 assert issue.save
1140 assert issue.save
1141 issue.reload
1141 issue.reload
1142 orig = Issue.find(1)
1142 orig = Issue.find(1)
1143 assert_equal orig.subject, issue.subject
1143 assert_equal orig.subject, issue.subject
1144 assert_equal orig.tracker, issue.tracker
1144 assert_equal orig.tracker, issue.tracker
1145 assert_equal "125", issue.custom_value_for(2).value
1145 assert_equal "125", issue.custom_value_for(2).value
1146 end
1146 end
1147
1147
1148 def test_copy_should_copy_status
1148 def test_copy_should_copy_status
1149 orig = Issue.find(8)
1149 orig = Issue.find(8)
1150 assert orig.status != orig.default_status
1150 assert orig.status != orig.default_status
1151
1151
1152 issue = Issue.new.copy_from(orig)
1152 issue = Issue.new.copy_from(orig)
1153 assert issue.save
1153 assert issue.save
1154 issue.reload
1154 issue.reload
1155 assert_equal orig.status, issue.status
1155 assert_equal orig.status, issue.status
1156 end
1156 end
1157
1157
1158 def test_copy_should_add_relation_with_copied_issue
1158 def test_copy_should_add_relation_with_copied_issue
1159 copied = Issue.find(1)
1159 copied = Issue.find(1)
1160 issue = Issue.new.copy_from(copied)
1160 issue = Issue.new.copy_from(copied)
1161 assert issue.save
1161 assert issue.save
1162 issue.reload
1162 issue.reload
1163
1163
1164 assert_equal 1, issue.relations.size
1164 assert_equal 1, issue.relations.size
1165 relation = issue.relations.first
1165 relation = issue.relations.first
1166 assert_equal 'copied_to', relation.relation_type
1166 assert_equal 'copied_to', relation.relation_type
1167 assert_equal copied, relation.issue_from
1167 assert_equal copied, relation.issue_from
1168 assert_equal issue, relation.issue_to
1168 assert_equal issue, relation.issue_to
1169 end
1169 end
1170
1170
1171 def test_copy_should_copy_subtasks
1171 def test_copy_should_copy_subtasks
1172 issue = Issue.generate_with_descendants!
1172 issue = Issue.generate_with_descendants!
1173
1173
1174 copy = issue.reload.copy
1174 copy = issue.reload.copy
1175 copy.author = User.find(7)
1175 copy.author = User.find(7)
1176 assert_difference 'Issue.count', 1+issue.descendants.count do
1176 assert_difference 'Issue.count', 1+issue.descendants.count do
1177 assert copy.save
1177 assert copy.save
1178 end
1178 end
1179 copy.reload
1179 copy.reload
1180 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1180 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1181 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1181 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1182 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1182 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1183 assert_equal copy.author, child_copy.author
1183 assert_equal copy.author, child_copy.author
1184 end
1184 end
1185
1185
1186 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1186 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1187 parent = Issue.generate!
1187 parent = Issue.generate!
1188 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1188 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1189 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1189 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1190
1190
1191 copy = parent.reload.copy
1191 copy = parent.reload.copy
1192 copy.parent_issue_id = parent.id
1192 copy.parent_issue_id = parent.id
1193 copy.author = User.find(7)
1193 copy.author = User.find(7)
1194 assert_difference 'Issue.count', 3 do
1194 assert_difference 'Issue.count', 3 do
1195 assert copy.save
1195 assert copy.save
1196 end
1196 end
1197 parent.reload
1197 parent.reload
1198 copy.reload
1198 copy.reload
1199 assert_equal parent, copy.parent
1199 assert_equal parent, copy.parent
1200 assert_equal 3, parent.children.count
1200 assert_equal 3, parent.children.count
1201 assert_equal 5, parent.descendants.count
1201 assert_equal 5, parent.descendants.count
1202 assert_equal 2, copy.children.count
1202 assert_equal 2, copy.children.count
1203 assert_equal 2, copy.descendants.count
1203 assert_equal 2, copy.descendants.count
1204 end
1204 end
1205
1205
1206 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1206 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1207 parent = Issue.generate!
1207 parent = Issue.generate!
1208 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1208 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1209 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1209 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1210
1210
1211 copy = parent.reload.copy
1211 copy = parent.reload.copy
1212 copy.parent_issue_id = child1.id
1212 copy.parent_issue_id = child1.id
1213 copy.author = User.find(7)
1213 copy.author = User.find(7)
1214 assert_difference 'Issue.count', 3 do
1214 assert_difference 'Issue.count', 3 do
1215 assert copy.save
1215 assert copy.save
1216 end
1216 end
1217 parent.reload
1217 parent.reload
1218 child1.reload
1218 child1.reload
1219 copy.reload
1219 copy.reload
1220 assert_equal child1, copy.parent
1220 assert_equal child1, copy.parent
1221 assert_equal 2, parent.children.count
1221 assert_equal 2, parent.children.count
1222 assert_equal 5, parent.descendants.count
1222 assert_equal 5, parent.descendants.count
1223 assert_equal 1, child1.children.count
1223 assert_equal 1, child1.children.count
1224 assert_equal 3, child1.descendants.count
1224 assert_equal 3, child1.descendants.count
1225 assert_equal 2, copy.children.count
1225 assert_equal 2, copy.children.count
1226 assert_equal 2, copy.descendants.count
1226 assert_equal 2, copy.descendants.count
1227 end
1227 end
1228
1228
1229 def test_copy_should_copy_subtasks_to_target_project
1229 def test_copy_should_copy_subtasks_to_target_project
1230 issue = Issue.generate_with_descendants!
1230 issue = Issue.generate_with_descendants!
1231
1231
1232 copy = issue.copy(:project_id => 3)
1232 copy = issue.copy(:project_id => 3)
1233 assert_difference 'Issue.count', 1+issue.descendants.count do
1233 assert_difference 'Issue.count', 1+issue.descendants.count do
1234 assert copy.save
1234 assert copy.save
1235 end
1235 end
1236 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1236 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1237 end
1237 end
1238
1238
1239 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1239 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1240 issue = Issue.generate_with_descendants!
1240 issue = Issue.generate_with_descendants!
1241
1241
1242 copy = issue.reload.copy
1242 copy = issue.reload.copy
1243 assert_difference 'Issue.count', 1+issue.descendants.count do
1243 assert_difference 'Issue.count', 1+issue.descendants.count do
1244 assert copy.save
1244 assert copy.save
1245 assert copy.save
1245 assert copy.save
1246 end
1246 end
1247 end
1247 end
1248
1248
1249 def test_should_not_call_after_project_change_on_creation
1249 def test_should_not_call_after_project_change_on_creation
1250 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1250 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1251 :subject => 'Test', :author_id => 1)
1251 :subject => 'Test', :author_id => 1)
1252 issue.expects(:after_project_change).never
1252 issue.expects(:after_project_change).never
1253 issue.save!
1253 issue.save!
1254 end
1254 end
1255
1255
1256 def test_should_not_call_after_project_change_on_update
1256 def test_should_not_call_after_project_change_on_update
1257 issue = Issue.find(1)
1257 issue = Issue.find(1)
1258 issue.project = Project.find(1)
1258 issue.project = Project.find(1)
1259 issue.subject = 'No project change'
1259 issue.subject = 'No project change'
1260 issue.expects(:after_project_change).never
1260 issue.expects(:after_project_change).never
1261 issue.save!
1261 issue.save!
1262 end
1262 end
1263
1263
1264 def test_should_call_after_project_change_on_project_change
1264 def test_should_call_after_project_change_on_project_change
1265 issue = Issue.find(1)
1265 issue = Issue.find(1)
1266 issue.project = Project.find(2)
1266 issue.project = Project.find(2)
1267 issue.expects(:after_project_change).once
1267 issue.expects(:after_project_change).once
1268 issue.save!
1268 issue.save!
1269 end
1269 end
1270
1270
1271 def test_adding_journal_should_update_timestamp
1271 def test_adding_journal_should_update_timestamp
1272 issue = Issue.find(1)
1272 issue = Issue.find(1)
1273 updated_on_was = issue.updated_on
1273 updated_on_was = issue.updated_on
1274
1274
1275 issue.init_journal(User.first, "Adding notes")
1275 issue.init_journal(User.first, "Adding notes")
1276 assert_difference 'Journal.count' do
1276 assert_difference 'Journal.count' do
1277 assert issue.save
1277 assert issue.save
1278 end
1278 end
1279 issue.reload
1279 issue.reload
1280
1280
1281 assert_not_equal updated_on_was, issue.updated_on
1281 assert_not_equal updated_on_was, issue.updated_on
1282 end
1282 end
1283
1283
1284 def test_should_close_duplicates
1284 def test_should_close_duplicates
1285 # Create 3 issues
1285 # Create 3 issues
1286 issue1 = Issue.generate!
1286 issue1 = Issue.generate!
1287 issue2 = Issue.generate!
1287 issue2 = Issue.generate!
1288 issue3 = Issue.generate!
1288 issue3 = Issue.generate!
1289
1289
1290 # 2 is a dupe of 1
1290 # 2 is a dupe of 1
1291 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1291 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1292 :relation_type => IssueRelation::TYPE_DUPLICATES)
1292 :relation_type => IssueRelation::TYPE_DUPLICATES)
1293 # And 3 is a dupe of 2
1293 # And 3 is a dupe of 2
1294 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1294 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1295 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1295 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1296 # And 3 is a dupe of 1 (circular duplicates)
1296 # And 3 is a dupe of 1 (circular duplicates)
1297 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1297 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1298 :relation_type => IssueRelation::TYPE_DUPLICATES)
1298 :relation_type => IssueRelation::TYPE_DUPLICATES)
1299
1299
1300 assert issue1.reload.duplicates.include?(issue2)
1300 assert issue1.reload.duplicates.include?(issue2)
1301
1301
1302 # Closing issue 1
1302 # Closing issue 1
1303 issue1.init_journal(User.first, "Closing issue1")
1303 issue1.init_journal(User.first, "Closing issue1")
1304 issue1.status = IssueStatus.where(:is_closed => true).first
1304 issue1.status = IssueStatus.where(:is_closed => true).first
1305 assert issue1.save
1305 assert issue1.save
1306 # 2 and 3 should be also closed
1306 # 2 and 3 should be also closed
1307 assert issue2.reload.closed?
1307 assert issue2.reload.closed?
1308 assert issue3.reload.closed?
1308 assert issue3.reload.closed?
1309 end
1309 end
1310
1310
1311 def test_should_close_duplicates_with_private_notes
1311 def test_should_close_duplicates_with_private_notes
1312 issue = Issue.generate!
1312 issue = Issue.generate!
1313 duplicate = Issue.generate!
1313 duplicate = Issue.generate!
1314 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1314 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1315 :relation_type => IssueRelation::TYPE_DUPLICATES)
1315 :relation_type => IssueRelation::TYPE_DUPLICATES)
1316 assert issue.reload.duplicates.include?(duplicate)
1316 assert issue.reload.duplicates.include?(duplicate)
1317
1317
1318 # Closing issue with private notes
1318 # Closing issue with private notes
1319 issue.init_journal(User.first, "Private notes")
1319 issue.init_journal(User.first, "Private notes")
1320 issue.private_notes = true
1320 issue.private_notes = true
1321 issue.status = IssueStatus.where(:is_closed => true).first
1321 issue.status = IssueStatus.where(:is_closed => true).first
1322 assert_save issue
1322 assert_save issue
1323
1323
1324 duplicate.reload
1324 duplicate.reload
1325 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1325 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1326 assert_equal true, journal.private_notes
1326 assert_equal true, journal.private_notes
1327 end
1327 end
1328
1328
1329 def test_should_not_close_duplicated_issue
1329 def test_should_not_close_duplicated_issue
1330 issue1 = Issue.generate!
1330 issue1 = Issue.generate!
1331 issue2 = Issue.generate!
1331 issue2 = Issue.generate!
1332
1332
1333 # 2 is a dupe of 1
1333 # 2 is a dupe of 1
1334 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1334 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1335 :relation_type => IssueRelation::TYPE_DUPLICATES)
1335 :relation_type => IssueRelation::TYPE_DUPLICATES)
1336 # 2 is a dup of 1 but 1 is not a duplicate of 2
1336 # 2 is a dup of 1 but 1 is not a duplicate of 2
1337 assert !issue2.reload.duplicates.include?(issue1)
1337 assert !issue2.reload.duplicates.include?(issue1)
1338
1338
1339 # Closing issue 2
1339 # Closing issue 2
1340 issue2.init_journal(User.first, "Closing issue2")
1340 issue2.init_journal(User.first, "Closing issue2")
1341 issue2.status = IssueStatus.where(:is_closed => true).first
1341 issue2.status = IssueStatus.where(:is_closed => true).first
1342 assert issue2.save
1342 assert issue2.save
1343 # 1 should not be also closed
1343 # 1 should not be also closed
1344 assert !issue1.reload.closed?
1344 assert !issue1.reload.closed?
1345 end
1345 end
1346
1346
1347 def test_assignable_versions
1347 def test_assignable_versions
1348 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1348 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1349 :status_id => 1, :fixed_version_id => 1,
1349 :status_id => 1, :fixed_version_id => 1,
1350 :subject => 'New issue')
1350 :subject => 'New issue')
1351 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1351 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1352 end
1352 end
1353
1353
1354 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1354 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1355 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1355 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1356 :status_id => 1, :fixed_version_id => 1,
1356 :status_id => 1, :fixed_version_id => 1,
1357 :subject => 'New issue')
1357 :subject => 'New issue')
1358 assert !issue.save
1358 assert !issue.save
1359 assert_not_equal [], issue.errors[:fixed_version_id]
1359 assert_not_equal [], issue.errors[:fixed_version_id]
1360 end
1360 end
1361
1361
1362 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1362 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1363 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1363 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1364 :status_id => 1, :fixed_version_id => 2,
1364 :status_id => 1, :fixed_version_id => 2,
1365 :subject => 'New issue')
1365 :subject => 'New issue')
1366 assert !issue.save
1366 assert !issue.save
1367 assert_not_equal [], issue.errors[:fixed_version_id]
1367 assert_not_equal [], issue.errors[:fixed_version_id]
1368 end
1368 end
1369
1369
1370 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1370 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1371 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1371 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1372 :status_id => 1, :fixed_version_id => 3,
1372 :status_id => 1, :fixed_version_id => 3,
1373 :subject => 'New issue')
1373 :subject => 'New issue')
1374 assert issue.save
1374 assert issue.save
1375 end
1375 end
1376
1376
1377 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1377 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1378 issue = Issue.find(11)
1378 issue = Issue.find(11)
1379 assert_equal 'closed', issue.fixed_version.status
1379 assert_equal 'closed', issue.fixed_version.status
1380 issue.subject = 'Subject changed'
1380 issue.subject = 'Subject changed'
1381 assert issue.save
1381 assert issue.save
1382 end
1382 end
1383
1383
1384 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1384 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1385 issue = Issue.find(11)
1385 issue = Issue.find(11)
1386 issue.status_id = 1
1386 issue.status_id = 1
1387 assert !issue.save
1387 assert !issue.save
1388 assert_not_equal [], issue.errors[:base]
1388 assert_not_equal [], issue.errors[:base]
1389 end
1389 end
1390
1390
1391 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1391 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1392 issue = Issue.find(11)
1392 issue = Issue.find(11)
1393 issue.status_id = 1
1393 issue.status_id = 1
1394 issue.fixed_version_id = 3
1394 issue.fixed_version_id = 3
1395 assert issue.save
1395 assert issue.save
1396 end
1396 end
1397
1397
1398 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1398 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1399 issue = Issue.find(12)
1399 issue = Issue.find(12)
1400 assert_equal 'locked', issue.fixed_version.status
1400 assert_equal 'locked', issue.fixed_version.status
1401 issue.status_id = 1
1401 issue.status_id = 1
1402 assert issue.save
1402 assert issue.save
1403 end
1403 end
1404
1404
1405 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1405 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1406 issue = Issue.find(2)
1406 issue = Issue.find(2)
1407 assert_equal 2, issue.fixed_version_id
1407 assert_equal 2, issue.fixed_version_id
1408 issue.project_id = 3
1408 issue.project_id = 3
1409 assert_nil issue.fixed_version_id
1409 assert_nil issue.fixed_version_id
1410 issue.fixed_version_id = 2
1410 issue.fixed_version_id = 2
1411 assert !issue.save
1411 assert !issue.save
1412 assert_include 'Target version is not included in the list', issue.errors.full_messages
1412 assert_include 'Target version is not included in the list', issue.errors.full_messages
1413 end
1413 end
1414
1414
1415 def test_should_keep_shared_version_when_changing_project
1415 def test_should_keep_shared_version_when_changing_project
1416 Version.find(2).update_attribute :sharing, 'tree'
1416 Version.find(2).update_attribute :sharing, 'tree'
1417
1417
1418 issue = Issue.find(2)
1418 issue = Issue.find(2)
1419 assert_equal 2, issue.fixed_version_id
1419 assert_equal 2, issue.fixed_version_id
1420 issue.project_id = 3
1420 issue.project_id = 3
1421 assert_equal 2, issue.fixed_version_id
1421 assert_equal 2, issue.fixed_version_id
1422 assert issue.save
1422 assert issue.save
1423 end
1423 end
1424
1424
1425 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1425 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1426 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1426 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1427 end
1427 end
1428
1428
1429 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1429 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1430 Project.find(2).disable_module! :issue_tracking
1430 Project.find(2).disable_module! :issue_tracking
1431 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1431 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1432 end
1432 end
1433
1433
1434 def test_allowed_target_projects_should_not_include_projects_without_trackers
1434 def test_allowed_target_projects_should_not_include_projects_without_trackers
1435 project = Project.generate!(:tracker_ids => [])
1435 project = Project.generate!(:tracker_ids => [])
1436 assert project.trackers.empty?
1436 assert project.trackers.empty?
1437 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1437 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1438 end
1438 end
1439
1439
1440 def test_move_to_another_project_with_same_category
1440 def test_move_to_another_project_with_same_category
1441 issue = Issue.find(1)
1441 issue = Issue.find(1)
1442 issue.project = Project.find(2)
1442 issue.project = Project.find(2)
1443 assert issue.save
1443 assert issue.save
1444 issue.reload
1444 issue.reload
1445 assert_equal 2, issue.project_id
1445 assert_equal 2, issue.project_id
1446 # Category changes
1446 # Category changes
1447 assert_equal 4, issue.category_id
1447 assert_equal 4, issue.category_id
1448 # Make sure time entries were move to the target project
1448 # Make sure time entries were move to the target project
1449 assert_equal 2, issue.time_entries.first.project_id
1449 assert_equal 2, issue.time_entries.first.project_id
1450 end
1450 end
1451
1451
1452 def test_move_to_another_project_without_same_category
1452 def test_move_to_another_project_without_same_category
1453 issue = Issue.find(2)
1453 issue = Issue.find(2)
1454 issue.project = Project.find(2)
1454 issue.project = Project.find(2)
1455 assert issue.save
1455 assert issue.save
1456 issue.reload
1456 issue.reload
1457 assert_equal 2, issue.project_id
1457 assert_equal 2, issue.project_id
1458 # Category cleared
1458 # Category cleared
1459 assert_nil issue.category_id
1459 assert_nil issue.category_id
1460 end
1460 end
1461
1461
1462 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1462 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1463 issue = Issue.find(1)
1463 issue = Issue.find(1)
1464 issue.update_attribute(:fixed_version_id, 1)
1464 issue.update_attribute(:fixed_version_id, 1)
1465 issue.project = Project.find(2)
1465 issue.project = Project.find(2)
1466 assert issue.save
1466 assert issue.save
1467 issue.reload
1467 issue.reload
1468 assert_equal 2, issue.project_id
1468 assert_equal 2, issue.project_id
1469 # Cleared fixed_version
1469 # Cleared fixed_version
1470 assert_equal nil, issue.fixed_version
1470 assert_equal nil, issue.fixed_version
1471 end
1471 end
1472
1472
1473 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1473 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1474 issue = Issue.find(1)
1474 issue = Issue.find(1)
1475 issue.update_attribute(:fixed_version_id, 4)
1475 issue.update_attribute(:fixed_version_id, 4)
1476 issue.project = Project.find(5)
1476 issue.project = Project.find(5)
1477 assert issue.save
1477 assert issue.save
1478 issue.reload
1478 issue.reload
1479 assert_equal 5, issue.project_id
1479 assert_equal 5, issue.project_id
1480 # Keep fixed_version
1480 # Keep fixed_version
1481 assert_equal 4, issue.fixed_version_id
1481 assert_equal 4, issue.fixed_version_id
1482 end
1482 end
1483
1483
1484 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1484 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1485 issue = Issue.find(1)
1485 issue = Issue.find(1)
1486 issue.update_attribute(:fixed_version_id, 1)
1486 issue.update_attribute(:fixed_version_id, 1)
1487 issue.project = Project.find(5)
1487 issue.project = Project.find(5)
1488 assert issue.save
1488 assert issue.save
1489 issue.reload
1489 issue.reload
1490 assert_equal 5, issue.project_id
1490 assert_equal 5, issue.project_id
1491 # Cleared fixed_version
1491 # Cleared fixed_version
1492 assert_equal nil, issue.fixed_version
1492 assert_equal nil, issue.fixed_version
1493 end
1493 end
1494
1494
1495 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1495 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1496 issue = Issue.find(1)
1496 issue = Issue.find(1)
1497 issue.update_attribute(:fixed_version_id, 7)
1497 issue.update_attribute(:fixed_version_id, 7)
1498 issue.project = Project.find(2)
1498 issue.project = Project.find(2)
1499 assert issue.save
1499 assert issue.save
1500 issue.reload
1500 issue.reload
1501 assert_equal 2, issue.project_id
1501 assert_equal 2, issue.project_id
1502 # Keep fixed_version
1502 # Keep fixed_version
1503 assert_equal 7, issue.fixed_version_id
1503 assert_equal 7, issue.fixed_version_id
1504 end
1504 end
1505
1505
1506 def test_move_to_another_project_should_keep_parent_if_valid
1506 def test_move_to_another_project_should_keep_parent_if_valid
1507 issue = Issue.find(1)
1507 issue = Issue.find(1)
1508 issue.update_attribute(:parent_issue_id, 2)
1508 issue.update_attribute(:parent_issue_id, 2)
1509 issue.project = Project.find(3)
1509 issue.project = Project.find(3)
1510 assert issue.save
1510 assert issue.save
1511 issue.reload
1511 issue.reload
1512 assert_equal 2, issue.parent_id
1512 assert_equal 2, issue.parent_id
1513 end
1513 end
1514
1514
1515 def test_move_to_another_project_should_clear_parent_if_not_valid
1515 def test_move_to_another_project_should_clear_parent_if_not_valid
1516 issue = Issue.find(1)
1516 issue = Issue.find(1)
1517 issue.update_attribute(:parent_issue_id, 2)
1517 issue.update_attribute(:parent_issue_id, 2)
1518 issue.project = Project.find(2)
1518 issue.project = Project.find(2)
1519 assert issue.save
1519 assert issue.save
1520 issue.reload
1520 issue.reload
1521 assert_nil issue.parent_id
1521 assert_nil issue.parent_id
1522 end
1522 end
1523
1523
1524 def test_move_to_another_project_with_disabled_tracker
1524 def test_move_to_another_project_with_disabled_tracker
1525 issue = Issue.find(1)
1525 issue = Issue.find(1)
1526 target = Project.find(2)
1526 target = Project.find(2)
1527 target.tracker_ids = [3]
1527 target.tracker_ids = [3]
1528 target.save
1528 target.save
1529 issue.project = target
1529 issue.project = target
1530 assert issue.save
1530 assert issue.save
1531 issue.reload
1531 issue.reload
1532 assert_equal 2, issue.project_id
1532 assert_equal 2, issue.project_id
1533 assert_equal 3, issue.tracker_id
1533 assert_equal 3, issue.tracker_id
1534 end
1534 end
1535
1535
1536 def test_copy_to_the_same_project
1536 def test_copy_to_the_same_project
1537 issue = Issue.find(1)
1537 issue = Issue.find(1)
1538 copy = issue.copy
1538 copy = issue.copy
1539 assert_difference 'Issue.count' do
1539 assert_difference 'Issue.count' do
1540 copy.save!
1540 copy.save!
1541 end
1541 end
1542 assert_kind_of Issue, copy
1542 assert_kind_of Issue, copy
1543 assert_equal issue.project, copy.project
1543 assert_equal issue.project, copy.project
1544 assert_equal "125", copy.custom_value_for(2).value
1544 assert_equal "125", copy.custom_value_for(2).value
1545 end
1545 end
1546
1546
1547 def test_copy_to_another_project_and_tracker
1547 def test_copy_to_another_project_and_tracker
1548 issue = Issue.find(1)
1548 issue = Issue.find(1)
1549 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1549 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1550 assert_difference 'Issue.count' do
1550 assert_difference 'Issue.count' do
1551 copy.save!
1551 copy.save!
1552 end
1552 end
1553 copy.reload
1553 copy.reload
1554 assert_kind_of Issue, copy
1554 assert_kind_of Issue, copy
1555 assert_equal Project.find(3), copy.project
1555 assert_equal Project.find(3), copy.project
1556 assert_equal Tracker.find(2), copy.tracker
1556 assert_equal Tracker.find(2), copy.tracker
1557 # Custom field #2 is not associated with target tracker
1557 # Custom field #2 is not associated with target tracker
1558 assert_nil copy.custom_value_for(2)
1558 assert_nil copy.custom_value_for(2)
1559 end
1559 end
1560
1560
1561 test "#copy should not create a journal" do
1561 test "#copy should not create a journal" do
1562 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :assigned_to_id => 3}, :link => false)
1562 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :assigned_to_id => 3}, :link => false)
1563 copy.save!
1563 copy.save!
1564 assert_equal 0, copy.reload.journals.size
1564 assert_equal 0, copy.reload.journals.size
1565 end
1565 end
1566
1566
1567 test "#copy should allow assigned_to changes" do
1567 test "#copy should allow assigned_to changes" do
1568 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1568 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1569 assert_equal 3, copy.assigned_to_id
1569 assert_equal 3, copy.assigned_to_id
1570 end
1570 end
1571
1571
1572 test "#copy should allow status changes" do
1572 test "#copy should allow status changes" do
1573 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1573 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1574 assert_equal 2, copy.status_id
1574 assert_equal 2, copy.status_id
1575 end
1575 end
1576
1576
1577 test "#copy should allow start date changes" do
1577 test "#copy should allow start date changes" do
1578 date = Date.today
1578 date = Date.today
1579 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1579 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1580 assert_equal date, copy.start_date
1580 assert_equal date, copy.start_date
1581 end
1581 end
1582
1582
1583 test "#copy should allow due date changes" do
1583 test "#copy should allow due date changes" do
1584 date = Date.today
1584 date = Date.today
1585 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1585 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1586 assert_equal date, copy.due_date
1586 assert_equal date, copy.due_date
1587 end
1587 end
1588
1588
1589 test "#copy should set current user as author" do
1589 test "#copy should set current user as author" do
1590 User.current = User.find(9)
1590 User.current = User.find(9)
1591 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1591 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1592 assert_equal User.current, copy.author
1592 assert_equal User.current, copy.author
1593 end
1593 end
1594
1594
1595 test "#copy should create a journal with notes" do
1595 test "#copy should create a journal with notes" do
1596 date = Date.today
1596 date = Date.today
1597 notes = "Notes added when copying"
1597 notes = "Notes added when copying"
1598 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1598 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1599 copy.init_journal(User.current, notes)
1599 copy.init_journal(User.current, notes)
1600 copy.save!
1600 copy.save!
1601
1601
1602 assert_equal 1, copy.journals.size
1602 assert_equal 1, copy.journals.size
1603 journal = copy.journals.first
1603 journal = copy.journals.first
1604 assert_equal 0, journal.details.size
1604 assert_equal 0, journal.details.size
1605 assert_equal notes, journal.notes
1605 assert_equal notes, journal.notes
1606 end
1606 end
1607
1607
1608 def test_valid_parent_project
1608 def test_valid_parent_project
1609 issue = Issue.find(1)
1609 issue = Issue.find(1)
1610 issue_in_same_project = Issue.find(2)
1610 issue_in_same_project = Issue.find(2)
1611 issue_in_child_project = Issue.find(5)
1611 issue_in_child_project = Issue.find(5)
1612 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1612 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1613 issue_in_other_child_project = Issue.find(6)
1613 issue_in_other_child_project = Issue.find(6)
1614 issue_in_different_tree = Issue.find(4)
1614 issue_in_different_tree = Issue.find(4)
1615
1615
1616 with_settings :cross_project_subtasks => '' do
1616 with_settings :cross_project_subtasks => '' do
1617 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1617 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1618 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1618 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1619 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1619 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1620 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1620 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1621 end
1621 end
1622
1622
1623 with_settings :cross_project_subtasks => 'system' do
1623 with_settings :cross_project_subtasks => 'system' do
1624 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1624 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1625 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1625 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1626 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1626 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1627 end
1627 end
1628
1628
1629 with_settings :cross_project_subtasks => 'tree' do
1629 with_settings :cross_project_subtasks => 'tree' do
1630 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1630 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1631 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1631 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1632 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1632 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1633 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1633 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1634
1634
1635 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1635 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1636 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1636 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1637 end
1637 end
1638
1638
1639 with_settings :cross_project_subtasks => 'descendants' do
1639 with_settings :cross_project_subtasks => 'descendants' do
1640 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1640 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1641 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1641 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1642 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1642 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1643 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1643 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1644
1644
1645 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1645 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1646 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1646 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1647 end
1647 end
1648 end
1648 end
1649
1649
1650 def test_recipients_should_include_previous_assignee
1650 def test_recipients_should_include_previous_assignee
1651 user = User.find(3)
1651 user = User.find(3)
1652 user.members.update_all ["mail_notification = ?", false]
1652 user.members.update_all ["mail_notification = ?", false]
1653 user.update_attribute :mail_notification, 'only_assigned'
1653 user.update_attribute :mail_notification, 'only_assigned'
1654
1654
1655 issue = Issue.find(2)
1655 issue = Issue.find(2)
1656 issue.assigned_to = nil
1656 issue.assigned_to = nil
1657 assert_include user.mail, issue.recipients
1657 assert_include user.mail, issue.recipients
1658 issue.save!
1658 issue.save!
1659 assert !issue.recipients.include?(user.mail)
1659 assert !issue.recipients.include?(user.mail)
1660 end
1660 end
1661
1661
1662 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1662 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1663 issue = Issue.find(12)
1663 issue = Issue.find(12)
1664 assert issue.recipients.include?(issue.author.mail)
1664 assert issue.recipients.include?(issue.author.mail)
1665 # copy the issue to a private project
1665 # copy the issue to a private project
1666 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1666 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1667 # author is not a member of project anymore
1667 # author is not a member of project anymore
1668 assert !copy.recipients.include?(copy.author.mail)
1668 assert !copy.recipients.include?(copy.author.mail)
1669 end
1669 end
1670
1670
1671 def test_recipients_should_include_the_assigned_group_members
1671 def test_recipients_should_include_the_assigned_group_members
1672 group_member = User.generate!
1672 group_member = User.generate!
1673 group = Group.generate!
1673 group = Group.generate!
1674 group.users << group_member
1674 group.users << group_member
1675
1675
1676 issue = Issue.find(12)
1676 issue = Issue.find(12)
1677 issue.assigned_to = group
1677 issue.assigned_to = group
1678 assert issue.recipients.include?(group_member.mail)
1678 assert issue.recipients.include?(group_member.mail)
1679 end
1679 end
1680
1680
1681 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1681 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1682 user = User.find(3)
1682 user = User.find(3)
1683 issue = Issue.find(9)
1683 issue = Issue.find(9)
1684 Watcher.create!(:user => user, :watchable => issue)
1684 Watcher.create!(:user => user, :watchable => issue)
1685 assert issue.watched_by?(user)
1685 assert issue.watched_by?(user)
1686 assert !issue.watcher_recipients.include?(user.mail)
1686 assert !issue.watcher_recipients.include?(user.mail)
1687 end
1687 end
1688
1688
1689 def test_issue_destroy
1689 def test_issue_destroy
1690 Issue.find(1).destroy
1690 Issue.find(1).destroy
1691 assert_nil Issue.find_by_id(1)
1691 assert_nil Issue.find_by_id(1)
1692 assert_nil TimeEntry.find_by_issue_id(1)
1692 assert_nil TimeEntry.find_by_issue_id(1)
1693 end
1693 end
1694
1694
1695 def test_destroy_should_delete_time_entries_custom_values
1695 def test_destroy_should_delete_time_entries_custom_values
1696 issue = Issue.generate!
1696 issue = Issue.generate!
1697 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1697 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1698
1698
1699 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1699 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1700 assert issue.destroy
1700 assert issue.destroy
1701 end
1701 end
1702 end
1702 end
1703
1703
1704 def test_destroying_a_deleted_issue_should_not_raise_an_error
1704 def test_destroying_a_deleted_issue_should_not_raise_an_error
1705 issue = Issue.find(1)
1705 issue = Issue.find(1)
1706 Issue.find(1).destroy
1706 Issue.find(1).destroy
1707
1707
1708 assert_nothing_raised do
1708 assert_nothing_raised do
1709 assert_no_difference 'Issue.count' do
1709 assert_no_difference 'Issue.count' do
1710 issue.destroy
1710 issue.destroy
1711 end
1711 end
1712 assert issue.destroyed?
1712 assert issue.destroyed?
1713 end
1713 end
1714 end
1714 end
1715
1715
1716 def test_destroying_a_stale_issue_should_not_raise_an_error
1716 def test_destroying_a_stale_issue_should_not_raise_an_error
1717 issue = Issue.find(1)
1717 issue = Issue.find(1)
1718 Issue.find(1).update_attribute :subject, "Updated"
1718 Issue.find(1).update_attribute :subject, "Updated"
1719
1719
1720 assert_nothing_raised do
1720 assert_nothing_raised do
1721 assert_difference 'Issue.count', -1 do
1721 assert_difference 'Issue.count', -1 do
1722 issue.destroy
1722 issue.destroy
1723 end
1723 end
1724 assert issue.destroyed?
1724 assert issue.destroyed?
1725 end
1725 end
1726 end
1726 end
1727
1727
1728 def test_blocked
1728 def test_blocked
1729 blocked_issue = Issue.find(9)
1729 blocked_issue = Issue.find(9)
1730 blocking_issue = Issue.find(10)
1730 blocking_issue = Issue.find(10)
1731
1731
1732 assert blocked_issue.blocked?
1732 assert blocked_issue.blocked?
1733 assert !blocking_issue.blocked?
1733 assert !blocking_issue.blocked?
1734 end
1734 end
1735
1735
1736 def test_blocked_issues_dont_allow_closed_statuses
1736 def test_blocked_issues_dont_allow_closed_statuses
1737 blocked_issue = Issue.find(9)
1737 blocked_issue = Issue.find(9)
1738
1738
1739 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1739 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1740 assert !allowed_statuses.empty?
1740 assert !allowed_statuses.empty?
1741 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1741 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1742 assert closed_statuses.empty?
1742 assert closed_statuses.empty?
1743 end
1743 end
1744
1744
1745 def test_unblocked_issues_allow_closed_statuses
1745 def test_unblocked_issues_allow_closed_statuses
1746 blocking_issue = Issue.find(10)
1746 blocking_issue = Issue.find(10)
1747
1747
1748 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1748 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1749 assert !allowed_statuses.empty?
1749 assert !allowed_statuses.empty?
1750 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1750 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1751 assert !closed_statuses.empty?
1751 assert !closed_statuses.empty?
1752 end
1752 end
1753
1753
1754 def test_reschedule_an_issue_without_dates
1754 def test_reschedule_an_issue_without_dates
1755 with_settings :non_working_week_days => [] do
1755 with_settings :non_working_week_days => [] do
1756 issue = Issue.new(:start_date => nil, :due_date => nil)
1756 issue = Issue.new(:start_date => nil, :due_date => nil)
1757 issue.reschedule_on '2012-10-09'.to_date
1757 issue.reschedule_on '2012-10-09'.to_date
1758 assert_equal '2012-10-09'.to_date, issue.start_date
1758 assert_equal '2012-10-09'.to_date, issue.start_date
1759 assert_equal '2012-10-09'.to_date, issue.due_date
1759 assert_equal '2012-10-09'.to_date, issue.due_date
1760 end
1760 end
1761
1761
1762 with_settings :non_working_week_days => %w(6 7) do
1762 with_settings :non_working_week_days => %w(6 7) do
1763 issue = Issue.new(:start_date => nil, :due_date => nil)
1763 issue = Issue.new(:start_date => nil, :due_date => nil)
1764 issue.reschedule_on '2012-10-09'.to_date
1764 issue.reschedule_on '2012-10-09'.to_date
1765 assert_equal '2012-10-09'.to_date, issue.start_date
1765 assert_equal '2012-10-09'.to_date, issue.start_date
1766 assert_equal '2012-10-09'.to_date, issue.due_date
1766 assert_equal '2012-10-09'.to_date, issue.due_date
1767
1767
1768 issue = Issue.new(:start_date => nil, :due_date => nil)
1768 issue = Issue.new(:start_date => nil, :due_date => nil)
1769 issue.reschedule_on '2012-10-13'.to_date
1769 issue.reschedule_on '2012-10-13'.to_date
1770 assert_equal '2012-10-15'.to_date, issue.start_date
1770 assert_equal '2012-10-15'.to_date, issue.start_date
1771 assert_equal '2012-10-15'.to_date, issue.due_date
1771 assert_equal '2012-10-15'.to_date, issue.due_date
1772 end
1772 end
1773 end
1773 end
1774
1774
1775 def test_reschedule_an_issue_with_start_date
1775 def test_reschedule_an_issue_with_start_date
1776 with_settings :non_working_week_days => [] do
1776 with_settings :non_working_week_days => [] do
1777 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1777 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1778 issue.reschedule_on '2012-10-13'.to_date
1778 issue.reschedule_on '2012-10-13'.to_date
1779 assert_equal '2012-10-13'.to_date, issue.start_date
1779 assert_equal '2012-10-13'.to_date, issue.start_date
1780 assert_equal '2012-10-13'.to_date, issue.due_date
1780 assert_equal '2012-10-13'.to_date, issue.due_date
1781 end
1781 end
1782
1782
1783 with_settings :non_working_week_days => %w(6 7) do
1783 with_settings :non_working_week_days => %w(6 7) do
1784 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1784 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1785 issue.reschedule_on '2012-10-11'.to_date
1785 issue.reschedule_on '2012-10-11'.to_date
1786 assert_equal '2012-10-11'.to_date, issue.start_date
1786 assert_equal '2012-10-11'.to_date, issue.start_date
1787 assert_equal '2012-10-11'.to_date, issue.due_date
1787 assert_equal '2012-10-11'.to_date, issue.due_date
1788
1788
1789 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1789 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1790 issue.reschedule_on '2012-10-13'.to_date
1790 issue.reschedule_on '2012-10-13'.to_date
1791 assert_equal '2012-10-15'.to_date, issue.start_date
1791 assert_equal '2012-10-15'.to_date, issue.start_date
1792 assert_equal '2012-10-15'.to_date, issue.due_date
1792 assert_equal '2012-10-15'.to_date, issue.due_date
1793 end
1793 end
1794 end
1794 end
1795
1795
1796 def test_reschedule_an_issue_with_start_and_due_dates
1796 def test_reschedule_an_issue_with_start_and_due_dates
1797 with_settings :non_working_week_days => [] do
1797 with_settings :non_working_week_days => [] do
1798 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1798 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1799 issue.reschedule_on '2012-10-13'.to_date
1799 issue.reschedule_on '2012-10-13'.to_date
1800 assert_equal '2012-10-13'.to_date, issue.start_date
1800 assert_equal '2012-10-13'.to_date, issue.start_date
1801 assert_equal '2012-10-19'.to_date, issue.due_date
1801 assert_equal '2012-10-19'.to_date, issue.due_date
1802 end
1802 end
1803
1803
1804 with_settings :non_working_week_days => %w(6 7) do
1804 with_settings :non_working_week_days => %w(6 7) do
1805 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1805 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1806 issue.reschedule_on '2012-10-11'.to_date
1806 issue.reschedule_on '2012-10-11'.to_date
1807 assert_equal '2012-10-11'.to_date, issue.start_date
1807 assert_equal '2012-10-11'.to_date, issue.start_date
1808 assert_equal '2012-10-23'.to_date, issue.due_date
1808 assert_equal '2012-10-23'.to_date, issue.due_date
1809
1809
1810 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1810 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1811 issue.reschedule_on '2012-10-13'.to_date
1811 issue.reschedule_on '2012-10-13'.to_date
1812 assert_equal '2012-10-15'.to_date, issue.start_date
1812 assert_equal '2012-10-15'.to_date, issue.start_date
1813 assert_equal '2012-10-25'.to_date, issue.due_date
1813 assert_equal '2012-10-25'.to_date, issue.due_date
1814 end
1814 end
1815 end
1815 end
1816
1816
1817 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1817 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1818 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1818 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1819 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1819 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1820 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1820 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1821 :relation_type => IssueRelation::TYPE_PRECEDES)
1821 :relation_type => IssueRelation::TYPE_PRECEDES)
1822 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1822 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1823
1823
1824 issue1.reload
1824 issue1.reload
1825 issue1.due_date = '2012-10-23'
1825 issue1.due_date = '2012-10-23'
1826 issue1.save!
1826 issue1.save!
1827 issue2.reload
1827 issue2.reload
1828 assert_equal Date.parse('2012-10-24'), issue2.start_date
1828 assert_equal Date.parse('2012-10-24'), issue2.start_date
1829 assert_equal Date.parse('2012-10-26'), issue2.due_date
1829 assert_equal Date.parse('2012-10-26'), issue2.due_date
1830 end
1830 end
1831
1831
1832 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1832 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1833 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1833 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1834 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1834 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1835 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1835 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1836 :relation_type => IssueRelation::TYPE_PRECEDES)
1836 :relation_type => IssueRelation::TYPE_PRECEDES)
1837 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1837 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1838
1838
1839 issue1.reload
1839 issue1.reload
1840 issue1.start_date = '2012-09-17'
1840 issue1.start_date = '2012-09-17'
1841 issue1.due_date = '2012-09-18'
1841 issue1.due_date = '2012-09-18'
1842 issue1.save!
1842 issue1.save!
1843 issue2.reload
1843 issue2.reload
1844 assert_equal Date.parse('2012-09-19'), issue2.start_date
1844 assert_equal Date.parse('2012-09-19'), issue2.start_date
1845 assert_equal Date.parse('2012-09-21'), issue2.due_date
1845 assert_equal Date.parse('2012-09-21'), issue2.due_date
1846 end
1846 end
1847
1847
1848 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1848 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1849 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1849 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1850 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1850 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1851 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1851 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1852 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1852 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1853 :relation_type => IssueRelation::TYPE_PRECEDES)
1853 :relation_type => IssueRelation::TYPE_PRECEDES)
1854 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1854 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1855 :relation_type => IssueRelation::TYPE_PRECEDES)
1855 :relation_type => IssueRelation::TYPE_PRECEDES)
1856 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1856 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1857
1857
1858 issue1.reload
1858 issue1.reload
1859 issue1.start_date = '2012-09-17'
1859 issue1.start_date = '2012-09-17'
1860 issue1.due_date = '2012-09-18'
1860 issue1.due_date = '2012-09-18'
1861 issue1.save!
1861 issue1.save!
1862 issue2.reload
1862 issue2.reload
1863 # Issue 2 must start after Issue 3
1863 # Issue 2 must start after Issue 3
1864 assert_equal Date.parse('2012-10-03'), issue2.start_date
1864 assert_equal Date.parse('2012-10-03'), issue2.start_date
1865 assert_equal Date.parse('2012-10-05'), issue2.due_date
1865 assert_equal Date.parse('2012-10-05'), issue2.due_date
1866 end
1866 end
1867
1867
1868 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1868 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1869 with_settings :non_working_week_days => [] do
1869 with_settings :non_working_week_days => [] do
1870 stale = Issue.find(1)
1870 stale = Issue.find(1)
1871 issue = Issue.find(1)
1871 issue = Issue.find(1)
1872 issue.subject = "Updated"
1872 issue.subject = "Updated"
1873 issue.save!
1873 issue.save!
1874 date = 10.days.from_now.to_date
1874 date = 10.days.from_now.to_date
1875 assert_nothing_raised do
1875 assert_nothing_raised do
1876 stale.reschedule_on!(date)
1876 stale.reschedule_on!(date)
1877 end
1877 end
1878 assert_equal date, stale.reload.start_date
1878 assert_equal date, stale.reload.start_date
1879 end
1879 end
1880 end
1880 end
1881
1881
1882 def test_child_issue_should_consider_parent_soonest_start_on_create
1882 def test_child_issue_should_consider_parent_soonest_start_on_create
1883 set_language_if_valid 'en'
1883 set_language_if_valid 'en'
1884 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1884 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1885 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
1885 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
1886 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1886 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1887 :relation_type => IssueRelation::TYPE_PRECEDES)
1887 :relation_type => IssueRelation::TYPE_PRECEDES)
1888 issue1.reload
1888 issue1.reload
1889 issue2.reload
1889 issue2.reload
1890 assert_equal Date.parse('2012-10-18'), issue2.start_date
1890 assert_equal Date.parse('2012-10-18'), issue2.start_date
1891
1891
1892 with_settings :date_format => '%m/%d/%Y' do
1892 with_settings :date_format => '%m/%d/%Y' do
1893 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1893 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1894 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1894 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1895 assert !child.valid?
1895 assert !child.valid?
1896 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1896 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1897 assert_equal Date.parse('2012-10-18'), child.soonest_start
1897 assert_equal Date.parse('2012-10-18'), child.soonest_start
1898 child.start_date = '2012-10-18'
1898 child.start_date = '2012-10-18'
1899 assert child.save
1899 assert child.save
1900 end
1900 end
1901 end
1901 end
1902
1902
1903 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
1903 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
1904 # tests that 3 cannot have 1 as parent:
1904 # tests that 3 cannot have 1 as parent:
1905 #
1905 #
1906 # 1 -> 2 -> 3
1906 # 1 -> 2 -> 3
1907 #
1907 #
1908 set_language_if_valid 'en'
1908 set_language_if_valid 'en'
1909 issue1 = Issue.generate!
1909 issue1 = Issue.generate!
1910 issue2 = Issue.generate!
1910 issue2 = Issue.generate!
1911 issue3 = Issue.generate!
1911 issue3 = Issue.generate!
1912 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1912 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1913 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
1913 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
1914 issue3.reload
1914 issue3.reload
1915 issue3.parent_issue_id = issue1.id
1915 issue3.parent_issue_id = issue1.id
1916 assert !issue3.valid?
1916 assert !issue3.valid?
1917 assert_include 'Parent task is invalid', issue3.errors.full_messages
1917 assert_include 'Parent task is invalid', issue3.errors.full_messages
1918 end
1918 end
1919
1919
1920 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
1920 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
1921 # tests that 1 cannot have 3 as parent:
1921 # tests that 1 cannot have 3 as parent:
1922 #
1922 #
1923 # 1 -> 2 -> 3
1923 # 1 -> 2 -> 3
1924 #
1924 #
1925 set_language_if_valid 'en'
1925 set_language_if_valid 'en'
1926 issue1 = Issue.generate!
1926 issue1 = Issue.generate!
1927 issue2 = Issue.generate!
1927 issue2 = Issue.generate!
1928 issue3 = Issue.generate!
1928 issue3 = Issue.generate!
1929 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1929 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1930 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
1930 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
1931 issue1.reload
1931 issue1.reload
1932 issue1.parent_issue_id = issue3.id
1932 issue1.parent_issue_id = issue3.id
1933 assert !issue1.valid?
1933 assert !issue1.valid?
1934 assert_include 'Parent task is invalid', issue1.errors.full_messages
1934 assert_include 'Parent task is invalid', issue1.errors.full_messages
1935 end
1935 end
1936
1936
1937 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
1937 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
1938 # tests that 4 cannot have 1 as parent:
1938 # tests that 4 cannot have 1 as parent:
1939 # changing the due date of 4 would update the end date of 1 which would reschedule 2
1939 # changing the due date of 4 would update the end date of 1 which would reschedule 2
1940 # which would change the end date of 3 which would reschedule 4 and so on...
1940 # which would change the end date of 3 which would reschedule 4 and so on...
1941 #
1941 #
1942 # 3 -> 4
1942 # 3 -> 4
1943 # ^
1943 # ^
1944 # 1 -> 2
1944 # 1 -> 2
1945 #
1945 #
1946 set_language_if_valid 'en'
1946 set_language_if_valid 'en'
1947 issue1 = Issue.generate!
1947 issue1 = Issue.generate!
1948 issue2 = Issue.generate!
1948 issue2 = Issue.generate!
1949 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1949 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1950 issue3 = Issue.generate!
1950 issue3 = Issue.generate!
1951 issue2.reload
1951 issue2.reload
1952 issue2.parent_issue_id = issue3.id
1952 issue2.parent_issue_id = issue3.id
1953 issue2.save!
1953 issue2.save!
1954 issue4 = Issue.generate!
1954 issue4 = Issue.generate!
1955 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
1955 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
1956 issue4.reload
1956 issue4.reload
1957 issue4.parent_issue_id = issue1.id
1957 issue4.parent_issue_id = issue1.id
1958 assert !issue4.valid?
1958 assert !issue4.valid?
1959 assert_include 'Parent task is invalid', issue4.errors.full_messages
1959 assert_include 'Parent task is invalid', issue4.errors.full_messages
1960 end
1960 end
1961
1961
1962 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
1962 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
1963 set_language_if_valid 'en'
1963 set_language_if_valid 'en'
1964 issue1 = Issue.generate!
1964 issue1 = Issue.generate!
1965 issue2 = Issue.generate!
1965 issue2 = Issue.generate!
1966 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
1966 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
1967 parent = Issue.generate!
1967 parent = Issue.generate!
1968 issue1.reload.parent_issue_id = parent.id
1968 issue1.reload.parent_issue_id = parent.id
1969 assert_save issue1
1969 assert_save issue1
1970 parent.reload
1970 parent.reload
1971 issue2.reload.parent_issue_id = parent.id
1971 issue2.reload.parent_issue_id = parent.id
1972 assert_save issue2
1972 assert_save issue2
1973 assert IssueRelation.exists?(relation.id)
1973 assert IssueRelation.exists?(relation.id)
1974 end
1974 end
1975
1975
1976 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
1976 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
1977 set_language_if_valid 'en'
1977 set_language_if_valid 'en'
1978 issue1 = Issue.generate!
1978 issue1 = Issue.generate!
1979 issue2 = Issue.generate!
1979 issue2 = Issue.generate!
1980 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
1980 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
1981 parent = Issue.generate!
1981 parent = Issue.generate!
1982 issue1.reload.parent_issue_id = parent.id
1982 issue1.reload.parent_issue_id = parent.id
1983 assert_save issue1
1983 assert_save issue1
1984 parent.reload
1984 parent.reload
1985 issue2.reload.parent_issue_id = parent.id
1985 issue2.reload.parent_issue_id = parent.id
1986 assert_save issue2
1986 assert_save issue2
1987 assert IssueRelation.exists?(relation.id)
1987 assert IssueRelation.exists?(relation.id)
1988 end
1988 end
1989
1989
1990 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
1990 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
1991 set_language_if_valid 'en'
1991 set_language_if_valid 'en'
1992 issue1 = Issue.generate!
1992 issue1 = Issue.generate!
1993 issue2 = Issue.generate!
1993 issue2 = Issue.generate!
1994 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
1994 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
1995 parent = Issue.generate!
1995 parent = Issue.generate!
1996 issue1.reload.parent_issue_id = parent.id
1996 issue1.reload.parent_issue_id = parent.id
1997 assert_save issue1
1997 assert_save issue1
1998 parent.reload
1998 parent.reload
1999 issue2.reload.parent_issue_id = parent.id
1999 issue2.reload.parent_issue_id = parent.id
2000 assert_save issue2
2000 assert_save issue2
2001 assert IssueRelation.exists?(relation.id)
2001 assert IssueRelation.exists?(relation.id)
2002 end
2002 end
2003
2003
2004 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2004 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2005 set_language_if_valid 'en'
2005 set_language_if_valid 'en'
2006 issue1 = Issue.generate!
2006 issue1 = Issue.generate!
2007 issue2 = Issue.generate!
2007 issue2 = Issue.generate!
2008 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2008 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2009 parent = Issue.generate!
2009 parent = Issue.generate!
2010 issue1.reload.parent_issue_id = parent.id
2010 issue1.reload.parent_issue_id = parent.id
2011 assert_save issue1
2011 assert_save issue1
2012 parent.reload
2012 parent.reload
2013 issue2.reload.parent_issue_id = parent.id
2013 issue2.reload.parent_issue_id = parent.id
2014 assert_save issue2
2014 assert_save issue2
2015 assert IssueRelation.exists?(relation.id)
2015 assert IssueRelation.exists?(relation.id)
2016 end
2016 end
2017
2017
2018 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2018 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2019 issue = Issue.generate!
2019 issue = Issue.generate!
2020 parent = Issue.generate!
2020 parent = Issue.generate!
2021 issue.parent_issue_id = parent.id
2021 issue.parent_issue_id = parent.id
2022 issue.save!
2022 issue.save!
2023 issue.reload
2023 issue.reload
2024
2024
2025 copy = Issue.new.copy_from(issue, :link => true)
2025 copy = Issue.new.copy_from(issue, :link => true)
2026 relation = new_record(IssueRelation) do
2026 relation = new_record(IssueRelation) do
2027 copy.save!
2027 copy.save!
2028 end
2028 end
2029
2029
2030 copy.parent_issue_id = parent.id
2030 copy.parent_issue_id = parent.id
2031 assert_save copy
2031 assert_save copy
2032 assert IssueRelation.exists?(relation.id)
2032 assert IssueRelation.exists?(relation.id)
2033 end
2033 end
2034
2034
2035 def test_overdue
2035 def test_overdue
2036 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2036 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2037 assert !Issue.new(:due_date => Date.today).overdue?
2037 assert !Issue.new(:due_date => Date.today).overdue?
2038 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2038 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2039 assert !Issue.new(:due_date => nil).overdue?
2039 assert !Issue.new(:due_date => nil).overdue?
2040 assert !Issue.new(:due_date => 1.day.ago.to_date,
2040 assert !Issue.new(:due_date => 1.day.ago.to_date,
2041 :status => IssueStatus.where(:is_closed => true).first
2041 :status => IssueStatus.where(:is_closed => true).first
2042 ).overdue?
2042 ).overdue?
2043 end
2043 end
2044
2044
2045 test "#behind_schedule? should be false if the issue has no start_date" do
2045 test "#behind_schedule? should be false if the issue has no start_date" do
2046 assert !Issue.new(:start_date => nil,
2046 assert !Issue.new(:start_date => nil,
2047 :due_date => 1.day.from_now.to_date,
2047 :due_date => 1.day.from_now.to_date,
2048 :done_ratio => 0).behind_schedule?
2048 :done_ratio => 0).behind_schedule?
2049 end
2049 end
2050
2050
2051 test "#behind_schedule? should be false if the issue has no end_date" do
2051 test "#behind_schedule? should be false if the issue has no end_date" do
2052 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2052 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2053 :due_date => nil,
2053 :due_date => nil,
2054 :done_ratio => 0).behind_schedule?
2054 :done_ratio => 0).behind_schedule?
2055 end
2055 end
2056
2056
2057 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2057 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2058 assert !Issue.new(:start_date => 50.days.ago.to_date,
2058 assert !Issue.new(:start_date => 50.days.ago.to_date,
2059 :due_date => 50.days.from_now.to_date,
2059 :due_date => 50.days.from_now.to_date,
2060 :done_ratio => 90).behind_schedule?
2060 :done_ratio => 90).behind_schedule?
2061 end
2061 end
2062
2062
2063 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2063 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2064 assert Issue.new(:start_date => 1.day.ago.to_date,
2064 assert Issue.new(:start_date => 1.day.ago.to_date,
2065 :due_date => 1.day.from_now.to_date,
2065 :due_date => 1.day.from_now.to_date,
2066 :done_ratio => 0).behind_schedule?
2066 :done_ratio => 0).behind_schedule?
2067 end
2067 end
2068
2068
2069 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2069 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2070 assert Issue.new(:start_date => 100.days.ago.to_date,
2070 assert Issue.new(:start_date => 100.days.ago.to_date,
2071 :due_date => Date.today,
2071 :due_date => Date.today,
2072 :done_ratio => 90).behind_schedule?
2072 :done_ratio => 90).behind_schedule?
2073 end
2073 end
2074
2074
2075 test "#assignable_users should be Users" do
2075 test "#assignable_users should be Users" do
2076 assert_kind_of User, Issue.find(1).assignable_users.first
2076 assert_kind_of User, Issue.find(1).assignable_users.first
2077 end
2077 end
2078
2078
2079 test "#assignable_users should include the issue author" do
2079 test "#assignable_users should include the issue author" do
2080 non_project_member = User.generate!
2080 non_project_member = User.generate!
2081 issue = Issue.generate!(:author => non_project_member)
2081 issue = Issue.generate!(:author => non_project_member)
2082
2082
2083 assert issue.assignable_users.include?(non_project_member)
2083 assert issue.assignable_users.include?(non_project_member)
2084 end
2084 end
2085
2085
2086 def test_assignable_users_should_not_include_anonymous_user
2086 def test_assignable_users_should_not_include_anonymous_user
2087 issue = Issue.generate!(:author => User.anonymous)
2087 issue = Issue.generate!(:author => User.anonymous)
2088
2088
2089 assert !issue.assignable_users.include?(User.anonymous)
2089 assert !issue.assignable_users.include?(User.anonymous)
2090 end
2090 end
2091
2091
2092 def test_assignable_users_should_not_include_locked_user
2092 def test_assignable_users_should_not_include_locked_user
2093 user = User.generate!
2093 user = User.generate!
2094 issue = Issue.generate!(:author => user)
2094 issue = Issue.generate!(:author => user)
2095 user.lock!
2095 user.lock!
2096
2096
2097 assert !issue.assignable_users.include?(user)
2097 assert !issue.assignable_users.include?(user)
2098 end
2098 end
2099
2099
2100 test "#assignable_users should include the current assignee" do
2100 test "#assignable_users should include the current assignee" do
2101 user = User.generate!
2101 user = User.generate!
2102 issue = Issue.generate!(:assigned_to => user)
2102 issue = Issue.generate!(:assigned_to => user)
2103 user.lock!
2103 user.lock!
2104
2104
2105 assert Issue.find(issue.id).assignable_users.include?(user)
2105 assert Issue.find(issue.id).assignable_users.include?(user)
2106 end
2106 end
2107
2107
2108 test "#assignable_users should not show the issue author twice" do
2108 test "#assignable_users should not show the issue author twice" do
2109 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2109 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2110 assert_equal 2, assignable_user_ids.length
2110 assert_equal 2, assignable_user_ids.length
2111
2111
2112 assignable_user_ids.each do |user_id|
2112 assignable_user_ids.each do |user_id|
2113 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2113 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2114 "User #{user_id} appears more or less than once"
2114 "User #{user_id} appears more or less than once"
2115 end
2115 end
2116 end
2116 end
2117
2117
2118 test "#assignable_users with issue_group_assignment should include groups" do
2118 test "#assignable_users with issue_group_assignment should include groups" do
2119 issue = Issue.new(:project => Project.find(2))
2119 issue = Issue.new(:project => Project.find(2))
2120
2120
2121 with_settings :issue_group_assignment => '1' do
2121 with_settings :issue_group_assignment => '1' do
2122 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2122 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2123 assert issue.assignable_users.include?(Group.find(11))
2123 assert issue.assignable_users.include?(Group.find(11))
2124 end
2124 end
2125 end
2125 end
2126
2126
2127 test "#assignable_users without issue_group_assignment should not include groups" do
2127 test "#assignable_users without issue_group_assignment should not include groups" do
2128 issue = Issue.new(:project => Project.find(2))
2128 issue = Issue.new(:project => Project.find(2))
2129
2129
2130 with_settings :issue_group_assignment => '0' do
2130 with_settings :issue_group_assignment => '0' do
2131 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2131 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2132 assert !issue.assignable_users.include?(Group.find(11))
2132 assert !issue.assignable_users.include?(Group.find(11))
2133 end
2133 end
2134 end
2134 end
2135
2135
2136 def test_assignable_users_should_not_include_builtin_groups
2136 def test_assignable_users_should_not_include_builtin_groups
2137 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2137 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2138 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2138 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2139 issue = Issue.new(:project => Project.find(1))
2139 issue = Issue.new(:project => Project.find(1))
2140
2140
2141 with_settings :issue_group_assignment => '1' do
2141 with_settings :issue_group_assignment => '1' do
2142 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2142 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2143 end
2143 end
2144 end
2144 end
2145
2145
2146 def test_create_should_send_email_notification
2146 def test_create_should_send_email_notification
2147 ActionMailer::Base.deliveries.clear
2147 ActionMailer::Base.deliveries.clear
2148 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2148 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2149 :author_id => 3, :status_id => 1,
2149 :author_id => 3, :status_id => 1,
2150 :priority => IssuePriority.all.first,
2150 :priority => IssuePriority.all.first,
2151 :subject => 'test_create', :estimated_hours => '1:30')
2151 :subject => 'test_create', :estimated_hours => '1:30')
2152 with_settings :notified_events => %w(issue_added) do
2152 with_settings :notified_events => %w(issue_added) do
2153 assert issue.save
2153 assert issue.save
2154 assert_equal 1, ActionMailer::Base.deliveries.size
2154 assert_equal 1, ActionMailer::Base.deliveries.size
2155 end
2155 end
2156 end
2156 end
2157
2157
2158 def test_create_should_send_one_email_notification_with_both_settings
2158 def test_create_should_send_one_email_notification_with_both_settings
2159 ActionMailer::Base.deliveries.clear
2159 ActionMailer::Base.deliveries.clear
2160 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2160 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2161 :author_id => 3, :status_id => 1,
2161 :author_id => 3, :status_id => 1,
2162 :priority => IssuePriority.all.first,
2162 :priority => IssuePriority.all.first,
2163 :subject => 'test_create', :estimated_hours => '1:30')
2163 :subject => 'test_create', :estimated_hours => '1:30')
2164 with_settings :notified_events => %w(issue_added issue_updated) do
2164 with_settings :notified_events => %w(issue_added issue_updated) do
2165 assert issue.save
2165 assert issue.save
2166 assert_equal 1, ActionMailer::Base.deliveries.size
2166 assert_equal 1, ActionMailer::Base.deliveries.size
2167 end
2167 end
2168 end
2168 end
2169
2169
2170 def test_create_should_not_send_email_notification_with_no_setting
2170 def test_create_should_not_send_email_notification_with_no_setting
2171 ActionMailer::Base.deliveries.clear
2171 ActionMailer::Base.deliveries.clear
2172 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2172 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2173 :author_id => 3, :status_id => 1,
2173 :author_id => 3, :status_id => 1,
2174 :priority => IssuePriority.all.first,
2174 :priority => IssuePriority.all.first,
2175 :subject => 'test_create', :estimated_hours => '1:30')
2175 :subject => 'test_create', :estimated_hours => '1:30')
2176 with_settings :notified_events => [] do
2176 with_settings :notified_events => [] do
2177 assert issue.save
2177 assert issue.save
2178 assert_equal 0, ActionMailer::Base.deliveries.size
2178 assert_equal 0, ActionMailer::Base.deliveries.size
2179 end
2179 end
2180 end
2180 end
2181
2181
2182 def test_update_should_notify_previous_assignee
2182 def test_update_should_notify_previous_assignee
2183 ActionMailer::Base.deliveries.clear
2183 ActionMailer::Base.deliveries.clear
2184 user = User.find(3)
2184 user = User.find(3)
2185 user.members.update_all ["mail_notification = ?", false]
2185 user.members.update_all ["mail_notification = ?", false]
2186 user.update_attribute :mail_notification, 'only_assigned'
2186 user.update_attribute :mail_notification, 'only_assigned'
2187
2187
2188 with_settings :notified_events => %w(issue_updated) do
2188 with_settings :notified_events => %w(issue_updated) do
2189 issue = Issue.find(2)
2189 issue = Issue.find(2)
2190 issue.init_journal User.find(1)
2190 issue.init_journal User.find(1)
2191 issue.assigned_to = nil
2191 issue.assigned_to = nil
2192 issue.save!
2192 issue.save!
2193 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2193 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2194 end
2194 end
2195 end
2195 end
2196
2196
2197 def test_stale_issue_should_not_send_email_notification
2197 def test_stale_issue_should_not_send_email_notification
2198 ActionMailer::Base.deliveries.clear
2198 ActionMailer::Base.deliveries.clear
2199 issue = Issue.find(1)
2199 issue = Issue.find(1)
2200 stale = Issue.find(1)
2200 stale = Issue.find(1)
2201
2201
2202 issue.init_journal(User.find(1))
2202 issue.init_journal(User.find(1))
2203 issue.subject = 'Subjet update'
2203 issue.subject = 'Subjet update'
2204 with_settings :notified_events => %w(issue_updated) do
2204 with_settings :notified_events => %w(issue_updated) do
2205 assert issue.save
2205 assert issue.save
2206 assert_equal 1, ActionMailer::Base.deliveries.size
2206 assert_equal 1, ActionMailer::Base.deliveries.size
2207 ActionMailer::Base.deliveries.clear
2207 ActionMailer::Base.deliveries.clear
2208
2208
2209 stale.init_journal(User.find(1))
2209 stale.init_journal(User.find(1))
2210 stale.subject = 'Another subjet update'
2210 stale.subject = 'Another subjet update'
2211 assert_raise ActiveRecord::StaleObjectError do
2211 assert_raise ActiveRecord::StaleObjectError do
2212 stale.save
2212 stale.save
2213 end
2213 end
2214 assert ActionMailer::Base.deliveries.empty?
2214 assert ActionMailer::Base.deliveries.empty?
2215 end
2215 end
2216 end
2216 end
2217
2217
2218 def test_journalized_description
2218 def test_journalized_description
2219 IssueCustomField.delete_all
2219 IssueCustomField.delete_all
2220
2220
2221 i = Issue.first
2221 i = Issue.first
2222 old_description = i.description
2222 old_description = i.description
2223 new_description = "This is the new description"
2223 new_description = "This is the new description"
2224
2224
2225 i.init_journal(User.find(2))
2225 i.init_journal(User.find(2))
2226 i.description = new_description
2226 i.description = new_description
2227 assert_difference 'Journal.count', 1 do
2227 assert_difference 'Journal.count', 1 do
2228 assert_difference 'JournalDetail.count', 1 do
2228 assert_difference 'JournalDetail.count', 1 do
2229 i.save!
2229 i.save!
2230 end
2230 end
2231 end
2231 end
2232
2232
2233 detail = JournalDetail.order('id DESC').first
2233 detail = JournalDetail.order('id DESC').first
2234 assert_equal i, detail.journal.journalized
2234 assert_equal i, detail.journal.journalized
2235 assert_equal 'attr', detail.property
2235 assert_equal 'attr', detail.property
2236 assert_equal 'description', detail.prop_key
2236 assert_equal 'description', detail.prop_key
2237 assert_equal old_description, detail.old_value
2237 assert_equal old_description, detail.old_value
2238 assert_equal new_description, detail.value
2238 assert_equal new_description, detail.value
2239 end
2239 end
2240
2240
2241 def test_blank_descriptions_should_not_be_journalized
2241 def test_blank_descriptions_should_not_be_journalized
2242 IssueCustomField.delete_all
2242 IssueCustomField.delete_all
2243 Issue.where(:id => 1).update_all("description = NULL")
2243 Issue.where(:id => 1).update_all("description = NULL")
2244
2244
2245 i = Issue.find(1)
2245 i = Issue.find(1)
2246 i.init_journal(User.find(2))
2246 i.init_journal(User.find(2))
2247 i.subject = "blank description"
2247 i.subject = "blank description"
2248 i.description = "\r\n"
2248 i.description = "\r\n"
2249
2249
2250 assert_difference 'Journal.count', 1 do
2250 assert_difference 'Journal.count', 1 do
2251 assert_difference 'JournalDetail.count', 1 do
2251 assert_difference 'JournalDetail.count', 1 do
2252 i.save!
2252 i.save!
2253 end
2253 end
2254 end
2254 end
2255 end
2255 end
2256
2256
2257 def test_journalized_multi_custom_field
2257 def test_journalized_multi_custom_field
2258 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2258 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2259 :is_filter => true, :is_for_all => true,
2259 :is_filter => true, :is_for_all => true,
2260 :tracker_ids => [1],
2260 :tracker_ids => [1],
2261 :possible_values => ['value1', 'value2', 'value3'],
2261 :possible_values => ['value1', 'value2', 'value3'],
2262 :multiple => true)
2262 :multiple => true)
2263
2263
2264 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2264 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2265 :subject => 'Test', :author_id => 1)
2265 :subject => 'Test', :author_id => 1)
2266
2266
2267 assert_difference 'Journal.count' do
2267 assert_difference 'Journal.count' do
2268 assert_difference 'JournalDetail.count' do
2268 assert_difference 'JournalDetail.count' do
2269 issue.init_journal(User.first)
2269 issue.init_journal(User.first)
2270 issue.custom_field_values = {field.id => ['value1']}
2270 issue.custom_field_values = {field.id => ['value1']}
2271 issue.save!
2271 issue.save!
2272 end
2272 end
2273 assert_difference 'JournalDetail.count' do
2273 assert_difference 'JournalDetail.count' do
2274 issue.init_journal(User.first)
2274 issue.init_journal(User.first)
2275 issue.custom_field_values = {field.id => ['value1', 'value2']}
2275 issue.custom_field_values = {field.id => ['value1', 'value2']}
2276 issue.save!
2276 issue.save!
2277 end
2277 end
2278 assert_difference 'JournalDetail.count', 2 do
2278 assert_difference 'JournalDetail.count', 2 do
2279 issue.init_journal(User.first)
2279 issue.init_journal(User.first)
2280 issue.custom_field_values = {field.id => ['value3', 'value2']}
2280 issue.custom_field_values = {field.id => ['value3', 'value2']}
2281 issue.save!
2281 issue.save!
2282 end
2282 end
2283 assert_difference 'JournalDetail.count', 2 do
2283 assert_difference 'JournalDetail.count', 2 do
2284 issue.init_journal(User.first)
2284 issue.init_journal(User.first)
2285 issue.custom_field_values = {field.id => nil}
2285 issue.custom_field_values = {field.id => nil}
2286 issue.save!
2286 issue.save!
2287 end
2287 end
2288 end
2288 end
2289 end
2289 end
2290
2290
2291 def test_description_eol_should_be_normalized
2291 def test_description_eol_should_be_normalized
2292 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2292 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2293 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2293 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2294 end
2294 end
2295
2295
2296 def test_saving_twice_should_not_duplicate_journal_details
2296 def test_saving_twice_should_not_duplicate_journal_details
2297 i = Issue.first
2297 i = Issue.first
2298 i.init_journal(User.find(2), 'Some notes')
2298 i.init_journal(User.find(2), 'Some notes')
2299 # initial changes
2299 # initial changes
2300 i.subject = 'New subject'
2300 i.subject = 'New subject'
2301 i.done_ratio = i.done_ratio + 10
2301 i.done_ratio = i.done_ratio + 10
2302 assert_difference 'Journal.count' do
2302 assert_difference 'Journal.count' do
2303 assert i.save
2303 assert i.save
2304 end
2304 end
2305 # 1 more change
2305 # 1 more change
2306 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2306 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2307 assert_no_difference 'Journal.count' do
2307 assert_no_difference 'Journal.count' do
2308 assert_difference 'JournalDetail.count', 1 do
2308 assert_difference 'JournalDetail.count', 1 do
2309 i.save
2309 i.save
2310 end
2310 end
2311 end
2311 end
2312 # no more change
2312 # no more change
2313 assert_no_difference 'Journal.count' do
2313 assert_no_difference 'Journal.count' do
2314 assert_no_difference 'JournalDetail.count' do
2314 assert_no_difference 'JournalDetail.count' do
2315 i.save
2315 i.save
2316 end
2316 end
2317 end
2317 end
2318 end
2318 end
2319
2319
2320 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2320 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2321 @issue = Issue.find(1)
2321 @issue = Issue.find(1)
2322 @issue_status = IssueStatus.find(1)
2322 @issue_status = IssueStatus.find(1)
2323 @issue_status.update_attribute(:default_done_ratio, 50)
2323 @issue_status.update_attribute(:default_done_ratio, 50)
2324 @issue2 = Issue.find(2)
2324 @issue2 = Issue.find(2)
2325 @issue_status2 = IssueStatus.find(2)
2325 @issue_status2 = IssueStatus.find(2)
2326 @issue_status2.update_attribute(:default_done_ratio, 0)
2326 @issue_status2.update_attribute(:default_done_ratio, 0)
2327
2327
2328 with_settings :issue_done_ratio => 'issue_field' do
2328 with_settings :issue_done_ratio => 'issue_field' do
2329 assert_equal 0, @issue.done_ratio
2329 assert_equal 0, @issue.done_ratio
2330 assert_equal 30, @issue2.done_ratio
2330 assert_equal 30, @issue2.done_ratio
2331 end
2331 end
2332
2332
2333 with_settings :issue_done_ratio => 'issue_status' do
2333 with_settings :issue_done_ratio => 'issue_status' do
2334 assert_equal 50, @issue.done_ratio
2334 assert_equal 50, @issue.done_ratio
2335 assert_equal 0, @issue2.done_ratio
2335 assert_equal 0, @issue2.done_ratio
2336 end
2336 end
2337 end
2337 end
2338
2338
2339 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2339 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2340 @issue = Issue.find(1)
2340 @issue = Issue.find(1)
2341 @issue_status = IssueStatus.find(1)
2341 @issue_status = IssueStatus.find(1)
2342 @issue_status.update_attribute(:default_done_ratio, 50)
2342 @issue_status.update_attribute(:default_done_ratio, 50)
2343 @issue2 = Issue.find(2)
2343 @issue2 = Issue.find(2)
2344 @issue_status2 = IssueStatus.find(2)
2344 @issue_status2 = IssueStatus.find(2)
2345 @issue_status2.update_attribute(:default_done_ratio, 0)
2345 @issue_status2.update_attribute(:default_done_ratio, 0)
2346
2346
2347 with_settings :issue_done_ratio => 'issue_field' do
2347 with_settings :issue_done_ratio => 'issue_field' do
2348 @issue.update_done_ratio_from_issue_status
2348 @issue.update_done_ratio_from_issue_status
2349 @issue2.update_done_ratio_from_issue_status
2349 @issue2.update_done_ratio_from_issue_status
2350
2350
2351 assert_equal 0, @issue.read_attribute(:done_ratio)
2351 assert_equal 0, @issue.read_attribute(:done_ratio)
2352 assert_equal 30, @issue2.read_attribute(:done_ratio)
2352 assert_equal 30, @issue2.read_attribute(:done_ratio)
2353 end
2353 end
2354
2354
2355 with_settings :issue_done_ratio => 'issue_status' do
2355 with_settings :issue_done_ratio => 'issue_status' do
2356 @issue.update_done_ratio_from_issue_status
2356 @issue.update_done_ratio_from_issue_status
2357 @issue2.update_done_ratio_from_issue_status
2357 @issue2.update_done_ratio_from_issue_status
2358
2358
2359 assert_equal 50, @issue.read_attribute(:done_ratio)
2359 assert_equal 50, @issue.read_attribute(:done_ratio)
2360 assert_equal 0, @issue2.read_attribute(:done_ratio)
2360 assert_equal 0, @issue2.read_attribute(:done_ratio)
2361 end
2361 end
2362 end
2362 end
2363
2363
2364 test "#by_tracker" do
2364 test "#by_tracker" do
2365 User.current = User.anonymous
2365 User.current = User.anonymous
2366 groups = Issue.by_tracker(Project.find(1))
2366 groups = Issue.by_tracker(Project.find(1))
2367 assert_equal 3, groups.count
2367 assert_equal 3, groups.count
2368 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2368 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2369 end
2369 end
2370
2370
2371 test "#by_version" do
2371 test "#by_version" do
2372 User.current = User.anonymous
2372 User.current = User.anonymous
2373 groups = Issue.by_version(Project.find(1))
2373 groups = Issue.by_version(Project.find(1))
2374 assert_equal 3, groups.count
2374 assert_equal 3, groups.count
2375 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2375 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2376 end
2376 end
2377
2377
2378 test "#by_priority" do
2378 test "#by_priority" do
2379 User.current = User.anonymous
2379 User.current = User.anonymous
2380 groups = Issue.by_priority(Project.find(1))
2380 groups = Issue.by_priority(Project.find(1))
2381 assert_equal 4, groups.count
2381 assert_equal 4, groups.count
2382 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2382 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2383 end
2383 end
2384
2384
2385 test "#by_category" do
2385 test "#by_category" do
2386 User.current = User.anonymous
2386 User.current = User.anonymous
2387 groups = Issue.by_category(Project.find(1))
2387 groups = Issue.by_category(Project.find(1))
2388 assert_equal 2, groups.count
2388 assert_equal 2, groups.count
2389 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2389 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2390 end
2390 end
2391
2391
2392 test "#by_assigned_to" do
2392 test "#by_assigned_to" do
2393 User.current = User.anonymous
2393 User.current = User.anonymous
2394 groups = Issue.by_assigned_to(Project.find(1))
2394 groups = Issue.by_assigned_to(Project.find(1))
2395 assert_equal 2, groups.count
2395 assert_equal 2, groups.count
2396 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2396 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2397 end
2397 end
2398
2398
2399 test "#by_author" do
2399 test "#by_author" do
2400 User.current = User.anonymous
2400 User.current = User.anonymous
2401 groups = Issue.by_author(Project.find(1))
2401 groups = Issue.by_author(Project.find(1))
2402 assert_equal 4, groups.count
2402 assert_equal 4, groups.count
2403 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2403 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2404 end
2404 end
2405
2405
2406 test "#by_subproject" do
2406 test "#by_subproject" do
2407 User.current = User.anonymous
2407 User.current = User.anonymous
2408 groups = Issue.by_subproject(Project.find(1))
2408 groups = Issue.by_subproject(Project.find(1))
2409 # Private descendant not visible
2409 # Private descendant not visible
2410 assert_equal 1, groups.count
2410 assert_equal 1, groups.count
2411 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2411 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2412 end
2412 end
2413
2413
2414 def test_recently_updated_scope
2414 def test_recently_updated_scope
2415 #should return the last updated issue
2415 #should return the last updated issue
2416 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2416 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2417 end
2417 end
2418
2418
2419 def test_on_active_projects_scope
2419 def test_on_active_projects_scope
2420 assert Project.find(2).archive
2420 assert Project.find(2).archive
2421
2421
2422 before = Issue.on_active_project.length
2422 before = Issue.on_active_project.length
2423 # test inclusion to results
2423 # test inclusion to results
2424 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2424 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2425 assert_equal before + 1, Issue.on_active_project.length
2425 assert_equal before + 1, Issue.on_active_project.length
2426
2426
2427 # Move to an archived project
2427 # Move to an archived project
2428 issue.project = Project.find(2)
2428 issue.project = Project.find(2)
2429 assert issue.save
2429 assert issue.save
2430 assert_equal before, Issue.on_active_project.length
2430 assert_equal before, Issue.on_active_project.length
2431 end
2431 end
2432
2432
2433 test "Issue#recipients should include project recipients" do
2433 test "Issue#recipients should include project recipients" do
2434 issue = Issue.generate!
2434 issue = Issue.generate!
2435 assert issue.project.recipients.present?
2435 assert issue.project.recipients.present?
2436 issue.project.recipients.each do |project_recipient|
2436 issue.project.recipients.each do |project_recipient|
2437 assert issue.recipients.include?(project_recipient)
2437 assert issue.recipients.include?(project_recipient)
2438 end
2438 end
2439 end
2439 end
2440
2440
2441 test "Issue#recipients should include the author if the author is active" do
2441 test "Issue#recipients should include the author if the author is active" do
2442 issue = Issue.generate!(:author => User.generate!)
2442 issue = Issue.generate!(:author => User.generate!)
2443 assert issue.author, "No author set for Issue"
2443 assert issue.author, "No author set for Issue"
2444 assert issue.recipients.include?(issue.author.mail)
2444 assert issue.recipients.include?(issue.author.mail)
2445 end
2445 end
2446
2446
2447 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2447 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2448 issue = Issue.generate!(:assigned_to => User.generate!)
2448 issue = Issue.generate!(:assigned_to => User.generate!)
2449 assert issue.assigned_to, "No assigned_to set for Issue"
2449 assert issue.assigned_to, "No assigned_to set for Issue"
2450 assert issue.recipients.include?(issue.assigned_to.mail)
2450 assert issue.recipients.include?(issue.assigned_to.mail)
2451 end
2451 end
2452
2452
2453 test "Issue#recipients should not include users who opt out of all email" do
2453 test "Issue#recipients should not include users who opt out of all email" do
2454 issue = Issue.generate!(:author => User.generate!)
2454 issue = Issue.generate!(:author => User.generate!)
2455 issue.author.update_attribute(:mail_notification, :none)
2455 issue.author.update_attribute(:mail_notification, :none)
2456 assert !issue.recipients.include?(issue.author.mail)
2456 assert !issue.recipients.include?(issue.author.mail)
2457 end
2457 end
2458
2458
2459 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2459 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2460 issue = Issue.generate!(:author => User.generate!)
2460 issue = Issue.generate!(:author => User.generate!)
2461 issue.author.update_attribute(:mail_notification, :only_assigned)
2461 issue.author.update_attribute(:mail_notification, :only_assigned)
2462 assert !issue.recipients.include?(issue.author.mail)
2462 assert !issue.recipients.include?(issue.author.mail)
2463 end
2463 end
2464
2464
2465 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2465 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2466 issue = Issue.generate!(:assigned_to => User.generate!)
2466 issue = Issue.generate!(:assigned_to => User.generate!)
2467 issue.assigned_to.update_attribute(:mail_notification, :only_owner)
2467 issue.assigned_to.update_attribute(:mail_notification, :only_owner)
2468 assert !issue.recipients.include?(issue.assigned_to.mail)
2468 assert !issue.recipients.include?(issue.assigned_to.mail)
2469 end
2469 end
2470
2470
2471 def test_last_journal_id_with_journals_should_return_the_journal_id
2471 def test_last_journal_id_with_journals_should_return_the_journal_id
2472 assert_equal 2, Issue.find(1).last_journal_id
2472 assert_equal 2, Issue.find(1).last_journal_id
2473 end
2473 end
2474
2474
2475 def test_last_journal_id_without_journals_should_return_nil
2475 def test_last_journal_id_without_journals_should_return_nil
2476 assert_nil Issue.find(3).last_journal_id
2476 assert_nil Issue.find(3).last_journal_id
2477 end
2477 end
2478
2478
2479 def test_journals_after_should_return_journals_with_greater_id
2479 def test_journals_after_should_return_journals_with_greater_id
2480 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2480 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2481 assert_equal [], Issue.find(1).journals_after('2')
2481 assert_equal [], Issue.find(1).journals_after('2')
2482 end
2482 end
2483
2483
2484 def test_journals_after_with_blank_arg_should_return_all_journals
2484 def test_journals_after_with_blank_arg_should_return_all_journals
2485 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2485 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2486 end
2486 end
2487
2487
2488 def test_css_classes_should_include_tracker
2488 def test_css_classes_should_include_tracker
2489 issue = Issue.new(:tracker => Tracker.find(2))
2489 issue = Issue.new(:tracker => Tracker.find(2))
2490 classes = issue.css_classes.split(' ')
2490 classes = issue.css_classes.split(' ')
2491 assert_include 'tracker-2', classes
2491 assert_include 'tracker-2', classes
2492 end
2492 end
2493
2493
2494 def test_css_classes_should_include_priority
2494 def test_css_classes_should_include_priority
2495 issue = Issue.new(:priority => IssuePriority.find(8))
2495 issue = Issue.new(:priority => IssuePriority.find(8))
2496 classes = issue.css_classes.split(' ')
2496 classes = issue.css_classes.split(' ')
2497 assert_include 'priority-8', classes
2497 assert_include 'priority-8', classes
2498 assert_include 'priority-highest', classes
2498 assert_include 'priority-highest', classes
2499 end
2499 end
2500
2500
2501 def test_css_classes_should_include_user_and_group_assignment
2501 def test_css_classes_should_include_user_and_group_assignment
2502 project = Project.first
2502 project = Project.first
2503 user = User.generate!
2503 user = User.generate!
2504 group = Group.generate!
2504 group = Group.generate!
2505 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2505 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2506 group.users << user
2506 group.users << user
2507 assert user.member_of?(project)
2507 assert user.member_of?(project)
2508 issue1 = Issue.generate(:assigned_to_id => group.id)
2508 issue1 = Issue.generate(:assigned_to_id => group.id)
2509 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2509 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2510 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2510 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2511 issue2 = Issue.generate(:assigned_to_id => user.id)
2511 issue2 = Issue.generate(:assigned_to_id => user.id)
2512 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2512 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2513 assert_include 'assigned-to-me', issue2.css_classes(user)
2513 assert_include 'assigned-to-me', issue2.css_classes(user)
2514 end
2514 end
2515
2515
2516 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2516 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2517 set_tmp_attachments_directory
2517 set_tmp_attachments_directory
2518 issue = Issue.generate!
2518 issue = Issue.generate!
2519 issue.save_attachments({
2519 issue.save_attachments({
2520 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2520 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2521 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2521 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2522 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2522 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2523 })
2523 })
2524 issue.attach_saved_attachments
2524 issue.attach_saved_attachments
2525
2525
2526 assert_equal 3, issue.reload.attachments.count
2526 assert_equal 3, issue.reload.attachments.count
2527 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2527 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2528 end
2528 end
2529
2529
2530 def test_closed_on_should_be_nil_when_creating_an_open_issue
2530 def test_closed_on_should_be_nil_when_creating_an_open_issue
2531 issue = Issue.generate!(:status_id => 1).reload
2531 issue = Issue.generate!(:status_id => 1).reload
2532 assert !issue.closed?
2532 assert !issue.closed?
2533 assert_nil issue.closed_on
2533 assert_nil issue.closed_on
2534 end
2534 end
2535
2535
2536 def test_closed_on_should_be_set_when_creating_a_closed_issue
2536 def test_closed_on_should_be_set_when_creating_a_closed_issue
2537 issue = Issue.generate!(:status_id => 5).reload
2537 issue = Issue.generate!(:status_id => 5).reload
2538 assert issue.closed?
2538 assert issue.closed?
2539 assert_not_nil issue.closed_on
2539 assert_not_nil issue.closed_on
2540 assert_equal issue.updated_on, issue.closed_on
2540 assert_equal issue.updated_on, issue.closed_on
2541 assert_equal issue.created_on, issue.closed_on
2541 assert_equal issue.created_on, issue.closed_on
2542 end
2542 end
2543
2543
2544 def test_closed_on_should_be_nil_when_updating_an_open_issue
2544 def test_closed_on_should_be_nil_when_updating_an_open_issue
2545 issue = Issue.find(1)
2545 issue = Issue.find(1)
2546 issue.subject = 'Not closed yet'
2546 issue.subject = 'Not closed yet'
2547 issue.save!
2547 issue.save!
2548 issue.reload
2548 issue.reload
2549 assert_nil issue.closed_on
2549 assert_nil issue.closed_on
2550 end
2550 end
2551
2551
2552 def test_closed_on_should_be_set_when_closing_an_open_issue
2552 def test_closed_on_should_be_set_when_closing_an_open_issue
2553 issue = Issue.find(1)
2553 issue = Issue.find(1)
2554 issue.subject = 'Now closed'
2554 issue.subject = 'Now closed'
2555 issue.status_id = 5
2555 issue.status_id = 5
2556 issue.save!
2556 issue.save!
2557 issue.reload
2557 issue.reload
2558 assert_not_nil issue.closed_on
2558 assert_not_nil issue.closed_on
2559 assert_equal issue.updated_on, issue.closed_on
2559 assert_equal issue.updated_on, issue.closed_on
2560 end
2560 end
2561
2561
2562 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2562 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2563 issue = Issue.open(false).first
2563 issue = Issue.open(false).first
2564 was_closed_on = issue.closed_on
2564 was_closed_on = issue.closed_on
2565 assert_not_nil was_closed_on
2565 assert_not_nil was_closed_on
2566 issue.subject = 'Updating a closed issue'
2566 issue.subject = 'Updating a closed issue'
2567 issue.save!
2567 issue.save!
2568 issue.reload
2568 issue.reload
2569 assert_equal was_closed_on, issue.closed_on
2569 assert_equal was_closed_on, issue.closed_on
2570 end
2570 end
2571
2571
2572 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2572 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2573 issue = Issue.open(false).first
2573 issue = Issue.open(false).first
2574 was_closed_on = issue.closed_on
2574 was_closed_on = issue.closed_on
2575 assert_not_nil was_closed_on
2575 assert_not_nil was_closed_on
2576 issue.subject = 'Reopening a closed issue'
2576 issue.subject = 'Reopening a closed issue'
2577 issue.status_id = 1
2577 issue.status_id = 1
2578 issue.save!
2578 issue.save!
2579 issue.reload
2579 issue.reload
2580 assert !issue.closed?
2580 assert !issue.closed?
2581 assert_equal was_closed_on, issue.closed_on
2581 assert_equal was_closed_on, issue.closed_on
2582 end
2582 end
2583
2583
2584 def test_status_was_should_return_nil_for_new_issue
2584 def test_status_was_should_return_nil_for_new_issue
2585 issue = Issue.new
2585 issue = Issue.new
2586 assert_nil issue.status_was
2586 assert_nil issue.status_was
2587 end
2587 end
2588
2588
2589 def test_status_was_should_return_status_before_change
2589 def test_status_was_should_return_status_before_change
2590 issue = Issue.find(1)
2590 issue = Issue.find(1)
2591 issue.status = IssueStatus.find(2)
2591 issue.status = IssueStatus.find(2)
2592 assert_equal IssueStatus.find(1), issue.status_was
2592 assert_equal IssueStatus.find(1), issue.status_was
2593 end
2593 end
2594
2594
2595 def test_status_was_should_return_status_before_change_with_status_id
2595 def test_status_was_should_return_status_before_change_with_status_id
2596 issue = Issue.find(1)
2596 issue = Issue.find(1)
2597 assert_equal IssueStatus.find(1), issue.status
2597 assert_equal IssueStatus.find(1), issue.status
2598 issue.status_id = 2
2598 issue.status_id = 2
2599 assert_equal IssueStatus.find(1), issue.status_was
2599 assert_equal IssueStatus.find(1), issue.status_was
2600 end
2600 end
2601
2601
2602 def test_status_was_should_be_reset_on_save
2602 def test_status_was_should_be_reset_on_save
2603 issue = Issue.find(1)
2603 issue = Issue.find(1)
2604 issue.status = IssueStatus.find(2)
2604 issue.status = IssueStatus.find(2)
2605 assert_equal IssueStatus.find(1), issue.status_was
2605 assert_equal IssueStatus.find(1), issue.status_was
2606 assert issue.save!
2606 assert issue.save!
2607 assert_equal IssueStatus.find(2), issue.status_was
2607 assert_equal IssueStatus.find(2), issue.status_was
2608 end
2608 end
2609
2609
2610 def test_closing_should_return_true_when_closing_an_issue
2610 def test_closing_should_return_true_when_closing_an_issue
2611 issue = Issue.find(1)
2611 issue = Issue.find(1)
2612 issue.status = IssueStatus.find(2)
2612 issue.status = IssueStatus.find(2)
2613 assert_equal false, issue.closing?
2613 assert_equal false, issue.closing?
2614 issue.status = IssueStatus.find(5)
2614 issue.status = IssueStatus.find(5)
2615 assert_equal true, issue.closing?
2615 assert_equal true, issue.closing?
2616 end
2616 end
2617
2617
2618 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2618 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2619 issue = Issue.find(1)
2619 issue = Issue.find(1)
2620 issue.status_id = 2
2620 issue.status_id = 2
2621 assert_equal false, issue.closing?
2621 assert_equal false, issue.closing?
2622 issue.status_id = 5
2622 issue.status_id = 5
2623 assert_equal true, issue.closing?
2623 assert_equal true, issue.closing?
2624 end
2624 end
2625
2625
2626 def test_closing_should_return_true_for_new_closed_issue
2626 def test_closing_should_return_true_for_new_closed_issue
2627 issue = Issue.new
2627 issue = Issue.new
2628 assert_equal false, issue.closing?
2628 assert_equal false, issue.closing?
2629 issue.status = IssueStatus.find(5)
2629 issue.status = IssueStatus.find(5)
2630 assert_equal true, issue.closing?
2630 assert_equal true, issue.closing?
2631 end
2631 end
2632
2632
2633 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2633 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2634 issue = Issue.new
2634 issue = Issue.new
2635 assert_equal false, issue.closing?
2635 assert_equal false, issue.closing?
2636 issue.status_id = 5
2636 issue.status_id = 5
2637 assert_equal true, issue.closing?
2637 assert_equal true, issue.closing?
2638 end
2638 end
2639
2639
2640 def test_closing_should_be_reset_after_save
2640 def test_closing_should_be_reset_after_save
2641 issue = Issue.find(1)
2641 issue = Issue.find(1)
2642 issue.status_id = 5
2642 issue.status_id = 5
2643 assert_equal true, issue.closing?
2643 assert_equal true, issue.closing?
2644 issue.save!
2644 issue.save!
2645 assert_equal false, issue.closing?
2645 assert_equal false, issue.closing?
2646 end
2646 end
2647
2647
2648 def test_reopening_should_return_true_when_reopening_an_issue
2648 def test_reopening_should_return_true_when_reopening_an_issue
2649 issue = Issue.find(8)
2649 issue = Issue.find(8)
2650 issue.status = IssueStatus.find(6)
2650 issue.status = IssueStatus.find(6)
2651 assert_equal false, issue.reopening?
2651 assert_equal false, issue.reopening?
2652 issue.status = IssueStatus.find(2)
2652 issue.status = IssueStatus.find(2)
2653 assert_equal true, issue.reopening?
2653 assert_equal true, issue.reopening?
2654 end
2654 end
2655
2655
2656 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2656 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2657 issue = Issue.find(8)
2657 issue = Issue.find(8)
2658 issue.status_id = 6
2658 issue.status_id = 6
2659 assert_equal false, issue.reopening?
2659 assert_equal false, issue.reopening?
2660 issue.status_id = 2
2660 issue.status_id = 2
2661 assert_equal true, issue.reopening?
2661 assert_equal true, issue.reopening?
2662 end
2662 end
2663
2663
2664 def test_reopening_should_return_false_for_new_open_issue
2664 def test_reopening_should_return_false_for_new_open_issue
2665 issue = Issue.new
2665 issue = Issue.new
2666 issue.status = IssueStatus.find(1)
2666 issue.status = IssueStatus.find(1)
2667 assert_equal false, issue.reopening?
2667 assert_equal false, issue.reopening?
2668 end
2668 end
2669
2669
2670 def test_reopening_should_be_reset_after_save
2670 def test_reopening_should_be_reset_after_save
2671 issue = Issue.find(8)
2671 issue = Issue.find(8)
2672 issue.status_id = 2
2672 issue.status_id = 2
2673 assert_equal true, issue.reopening?
2673 assert_equal true, issue.reopening?
2674 issue.save!
2674 issue.save!
2675 assert_equal false, issue.reopening?
2675 assert_equal false, issue.reopening?
2676 end
2676 end
2677
2677
2678 def test_default_status_without_tracker_should_be_nil
2678 def test_default_status_without_tracker_should_be_nil
2679 issue = Issue.new
2679 issue = Issue.new
2680 assert_nil issue.tracker
2680 assert_nil issue.tracker
2681 assert_nil issue.default_status
2681 assert_nil issue.default_status
2682 end
2682 end
2683
2683
2684 def test_default_status_should_be_tracker_default_status
2684 def test_default_status_should_be_tracker_default_status
2685 issue = Issue.new(:tracker_id => 1)
2685 issue = Issue.new(:tracker_id => 1)
2686 assert_not_nil issue.status
2686 assert_not_nil issue.status
2687 assert_equal issue.tracker.default_status, issue.default_status
2687 assert_equal issue.tracker.default_status, issue.default_status
2688 end
2688 end
2689
2689
2690 def test_initializing_with_tracker_should_set_default_status
2690 def test_initializing_with_tracker_should_set_default_status
2691 issue = Issue.new(:tracker => Tracker.find(1))
2691 issue = Issue.new(:tracker => Tracker.find(1))
2692 assert_not_nil issue.status
2692 assert_not_nil issue.status
2693 assert_equal issue.default_status, issue.status
2693 assert_equal issue.default_status, issue.status
2694 end
2694 end
2695
2695
2696 def test_initializing_with_tracker_id_should_set_default_status
2696 def test_initializing_with_tracker_id_should_set_default_status
2697 issue = Issue.new(:tracker_id => 1)
2697 issue = Issue.new(:tracker_id => 1)
2698 assert_not_nil issue.status
2698 assert_not_nil issue.status
2699 assert_equal issue.default_status, issue.status
2699 assert_equal issue.default_status, issue.status
2700 end
2700 end
2701
2701
2702 def test_setting_tracker_should_set_default_status
2702 def test_setting_tracker_should_set_default_status
2703 issue = Issue.new
2703 issue = Issue.new
2704 issue.tracker = Tracker.find(1)
2704 issue.tracker = Tracker.find(1)
2705 assert_not_nil issue.status
2705 assert_not_nil issue.status
2706 assert_equal issue.default_status, issue.status
2706 assert_equal issue.default_status, issue.status
2707 end
2707 end
2708
2708
2709 def test_changing_tracker_should_set_default_status_if_status_was_default
2709 def test_changing_tracker_should_set_default_status_if_status_was_default
2710 WorkflowTransition.delete_all
2710 WorkflowTransition.delete_all
2711 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2711 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2712 Tracker.find(2).update! :default_status_id => 2
2712 Tracker.find(2).update! :default_status_id => 2
2713
2713
2714 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2714 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2715 assert_equal IssueStatus.find(1), issue.status
2715 assert_equal IssueStatus.find(1), issue.status
2716 issue.tracker = Tracker.find(2)
2716 issue.tracker = Tracker.find(2)
2717 assert_equal IssueStatus.find(2), issue.status
2717 assert_equal IssueStatus.find(2), issue.status
2718 end
2718 end
2719
2719
2720 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2720 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2721 WorkflowTransition.delete_all
2721 WorkflowTransition.delete_all
2722 Tracker.find(2).update! :default_status_id => 2
2722 Tracker.find(2).update! :default_status_id => 2
2723
2723
2724 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2724 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2725 assert_equal IssueStatus.find(3), issue.status
2725 assert_equal IssueStatus.find(3), issue.status
2726 issue.tracker = Tracker.find(2)
2726 issue.tracker = Tracker.find(2)
2727 assert_equal IssueStatus.find(2), issue.status
2727 assert_equal IssueStatus.find(2), issue.status
2728 end
2728 end
2729
2729
2730 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2730 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2731 WorkflowTransition.delete_all
2731 WorkflowTransition.delete_all
2732 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2732 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2733 Tracker.find(2).update! :default_status_id => 2
2733 Tracker.find(2).update! :default_status_id => 2
2734
2734
2735 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2735 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2736 assert_equal IssueStatus.find(3), issue.status
2736 assert_equal IssueStatus.find(3), issue.status
2737 issue.tracker = Tracker.find(2)
2737 issue.tracker = Tracker.find(2)
2738 assert_equal IssueStatus.find(3), issue.status
2738 assert_equal IssueStatus.find(3), issue.status
2739 end
2739 end
2740
2740
2741 def test_assigned_to_was_with_a_group
2741 def test_assigned_to_was_with_a_group
2742 group = Group.find(10)
2742 group = Group.find(10)
2743
2743
2744 issue = Issue.generate!(:assigned_to => group)
2744 issue = Issue.generate!(:assigned_to => group)
2745 issue.reload.assigned_to = nil
2745 issue.reload.assigned_to = nil
2746 assert_equal group, issue.assigned_to_was
2746 assert_equal group, issue.assigned_to_was
2747 end
2747 end
2748 end
2748 end
@@ -1,1084 +1,1084
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class MailHandlerTest < ActiveSupport::TestCase
22 class MailHandlerTest < ActiveSupport::TestCase
23 fixtures :users, :projects, :enabled_modules, :roles,
23 fixtures :users, :projects, :enabled_modules, :roles,
24 :members, :member_roles, :users,
24 :members, :member_roles, :users,
25 :email_addresses,
25 :email_addresses, :user_preferences,
26 :issues, :issue_statuses,
26 :issues, :issue_statuses,
27 :workflows, :trackers, :projects_trackers,
27 :workflows, :trackers, :projects_trackers,
28 :versions, :enumerations, :issue_categories,
28 :versions, :enumerations, :issue_categories,
29 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
29 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
30 :boards, :messages
30 :boards, :messages
31
31
32 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
32 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
33
33
34 def setup
34 def setup
35 ActionMailer::Base.deliveries.clear
35 ActionMailer::Base.deliveries.clear
36 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
36 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
37 end
37 end
38
38
39 def teardown
39 def teardown
40 Setting.clear_cache
40 Setting.clear_cache
41 end
41 end
42
42
43 def test_add_issue_with_specific_overrides
43 def test_add_issue_with_specific_overrides
44 issue = submit_email('ticket_on_given_project.eml',
44 issue = submit_email('ticket_on_given_project.eml',
45 :allow_override => ['status', 'start_date', 'due_date', 'assigned_to', 'fixed_version', 'estimated_hours', 'done_ratio']
45 :allow_override => ['status', 'start_date', 'due_date', 'assigned_to', 'fixed_version', 'estimated_hours', 'done_ratio']
46 )
46 )
47 assert issue.is_a?(Issue)
47 assert issue.is_a?(Issue)
48 assert !issue.new_record?
48 assert !issue.new_record?
49 issue.reload
49 issue.reload
50 assert_equal Project.find(2), issue.project
50 assert_equal Project.find(2), issue.project
51 assert_equal issue.project.trackers.first, issue.tracker
51 assert_equal issue.project.trackers.first, issue.tracker
52 assert_equal 'New ticket on a given project', issue.subject
52 assert_equal 'New ticket on a given project', issue.subject
53 assert_equal User.find_by_login('jsmith'), issue.author
53 assert_equal User.find_by_login('jsmith'), issue.author
54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
56 assert_equal '2010-01-01', issue.start_date.to_s
56 assert_equal '2010-01-01', issue.start_date.to_s
57 assert_equal '2010-12-31', issue.due_date.to_s
57 assert_equal '2010-12-31', issue.due_date.to_s
58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
60 assert_equal 2.5, issue.estimated_hours
60 assert_equal 2.5, issue.estimated_hours
61 assert_equal 30, issue.done_ratio
61 assert_equal 30, issue.done_ratio
62 # keywords should be removed from the email body
62 # keywords should be removed from the email body
63 assert !issue.description.match(/^Project:/i)
63 assert !issue.description.match(/^Project:/i)
64 assert !issue.description.match(/^Status:/i)
64 assert !issue.description.match(/^Status:/i)
65 assert !issue.description.match(/^Start Date:/i)
65 assert !issue.description.match(/^Start Date:/i)
66 end
66 end
67
67
68 def test_add_issue_with_all_overrides
68 def test_add_issue_with_all_overrides
69 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
69 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
70 assert issue.is_a?(Issue)
70 assert issue.is_a?(Issue)
71 assert !issue.new_record?
71 assert !issue.new_record?
72 issue.reload
72 issue.reload
73 assert_equal Project.find(2), issue.project
73 assert_equal Project.find(2), issue.project
74 assert_equal issue.project.trackers.first, issue.tracker
74 assert_equal issue.project.trackers.first, issue.tracker
75 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
75 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
76 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
76 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
77 assert_equal '2010-01-01', issue.start_date.to_s
77 assert_equal '2010-01-01', issue.start_date.to_s
78 assert_equal '2010-12-31', issue.due_date.to_s
78 assert_equal '2010-12-31', issue.due_date.to_s
79 assert_equal User.find_by_login('jsmith'), issue.assigned_to
79 assert_equal User.find_by_login('jsmith'), issue.assigned_to
80 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
80 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
81 assert_equal 2.5, issue.estimated_hours
81 assert_equal 2.5, issue.estimated_hours
82 assert_equal 30, issue.done_ratio
82 assert_equal 30, issue.done_ratio
83 end
83 end
84
84
85 def test_add_issue_without_overrides_should_ignore_attributes
85 def test_add_issue_without_overrides_should_ignore_attributes
86 WorkflowRule.delete_all
86 WorkflowRule.delete_all
87 issue = submit_email('ticket_on_given_project.eml')
87 issue = submit_email('ticket_on_given_project.eml')
88 assert issue.is_a?(Issue)
88 assert issue.is_a?(Issue)
89 assert !issue.new_record?
89 assert !issue.new_record?
90 issue.reload
90 issue.reload
91 assert_equal Project.find(2), issue.project
91 assert_equal Project.find(2), issue.project
92 assert_equal 'New ticket on a given project', issue.subject
92 assert_equal 'New ticket on a given project', issue.subject
93 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
93 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
94 assert_equal User.find_by_login('jsmith'), issue.author
94 assert_equal User.find_by_login('jsmith'), issue.author
95
95
96 assert_equal issue.project.trackers.first, issue.tracker
96 assert_equal issue.project.trackers.first, issue.tracker
97 assert_equal 'New', issue.status.name
97 assert_equal 'New', issue.status.name
98 assert_not_equal '2010-01-01', issue.start_date.to_s
98 assert_not_equal '2010-01-01', issue.start_date.to_s
99 assert_nil issue.due_date
99 assert_nil issue.due_date
100 assert_nil issue.assigned_to
100 assert_nil issue.assigned_to
101 assert_nil issue.fixed_version
101 assert_nil issue.fixed_version
102 assert_nil issue.estimated_hours
102 assert_nil issue.estimated_hours
103 assert_equal 0, issue.done_ratio
103 assert_equal 0, issue.done_ratio
104 end
104 end
105
105
106 def test_add_issue_to_project_specified_by_subaddress
106 def test_add_issue_to_project_specified_by_subaddress
107 # This email has redmine+onlinestore@somenet.foo as 'To' header
107 # This email has redmine+onlinestore@somenet.foo as 'To' header
108 issue = submit_email(
108 issue = submit_email(
109 'ticket_on_project_given_by_to_header.eml',
109 'ticket_on_project_given_by_to_header.eml',
110 :issue => {:tracker => 'Support request'},
110 :issue => {:tracker => 'Support request'},
111 :project_from_subaddress => 'redmine@somenet.foo'
111 :project_from_subaddress => 'redmine@somenet.foo'
112 )
112 )
113 assert issue.is_a?(Issue)
113 assert issue.is_a?(Issue)
114 assert !issue.new_record?
114 assert !issue.new_record?
115 issue.reload
115 issue.reload
116 assert_equal 'onlinestore', issue.project.identifier
116 assert_equal 'onlinestore', issue.project.identifier
117 assert_equal 'Support request', issue.tracker.name
117 assert_equal 'Support request', issue.tracker.name
118 end
118 end
119
119
120 def test_add_issue_with_default_tracker
120 def test_add_issue_with_default_tracker
121 # This email contains: 'Project: onlinestore'
121 # This email contains: 'Project: onlinestore'
122 issue = submit_email(
122 issue = submit_email(
123 'ticket_on_given_project.eml',
123 'ticket_on_given_project.eml',
124 :issue => {:tracker => 'Support request'}
124 :issue => {:tracker => 'Support request'}
125 )
125 )
126 assert issue.is_a?(Issue)
126 assert issue.is_a?(Issue)
127 assert !issue.new_record?
127 assert !issue.new_record?
128 issue.reload
128 issue.reload
129 assert_equal 'Support request', issue.tracker.name
129 assert_equal 'Support request', issue.tracker.name
130 end
130 end
131
131
132 def test_add_issue_with_default_version
132 def test_add_issue_with_default_version
133 # This email contains: 'Project: onlinestore'
133 # This email contains: 'Project: onlinestore'
134 issue = submit_email(
134 issue = submit_email(
135 'ticket_on_given_project.eml',
135 'ticket_on_given_project.eml',
136 :issue => {:fixed_version => 'Alpha'}
136 :issue => {:fixed_version => 'Alpha'}
137 )
137 )
138 assert issue.is_a?(Issue)
138 assert issue.is_a?(Issue)
139 assert !issue.new_record?
139 assert !issue.new_record?
140 assert_equal 'Alpha', issue.reload.fixed_version.name
140 assert_equal 'Alpha', issue.reload.fixed_version.name
141 end
141 end
142
142
143 def test_add_issue_with_status_override
143 def test_add_issue_with_status_override
144 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
144 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
145 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['status'])
145 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['status'])
146 assert issue.is_a?(Issue)
146 assert issue.is_a?(Issue)
147 assert !issue.new_record?
147 assert !issue.new_record?
148 issue.reload
148 issue.reload
149 assert_equal Project.find(2), issue.project
149 assert_equal Project.find(2), issue.project
150 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
150 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
151 end
151 end
152
152
153 def test_add_issue_should_accept_is_private_attribute
153 def test_add_issue_should_accept_is_private_attribute
154 issue = submit_email('ticket_on_given_project.eml', :issue => {:is_private => '1'})
154 issue = submit_email('ticket_on_given_project.eml', :issue => {:is_private => '1'})
155 assert issue.is_a?(Issue)
155 assert issue.is_a?(Issue)
156 assert !issue.new_record?
156 assert !issue.new_record?
157 assert_equal true, issue.reload.is_private
157 assert_equal true, issue.reload.is_private
158 end
158 end
159
159
160 def test_add_issue_with_group_assignment
160 def test_add_issue_with_group_assignment
161 with_settings :issue_group_assignment => '1' do
161 with_settings :issue_group_assignment => '1' do
162 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
162 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
163 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
163 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
164 end
164 end
165 assert issue.is_a?(Issue)
165 assert issue.is_a?(Issue)
166 assert !issue.new_record?
166 assert !issue.new_record?
167 issue.reload
167 issue.reload
168 assert_equal Group.find(11), issue.assigned_to
168 assert_equal Group.find(11), issue.assigned_to
169 end
169 end
170 end
170 end
171
171
172 def test_add_issue_with_partial_attributes_override
172 def test_add_issue_with_partial_attributes_override
173 issue = submit_email(
173 issue = submit_email(
174 'ticket_with_attributes.eml',
174 'ticket_with_attributes.eml',
175 :issue => {:priority => 'High'},
175 :issue => {:priority => 'High'},
176 :allow_override => ['tracker']
176 :allow_override => ['tracker']
177 )
177 )
178 assert issue.is_a?(Issue)
178 assert issue.is_a?(Issue)
179 assert !issue.new_record?
179 assert !issue.new_record?
180 issue.reload
180 issue.reload
181 assert_equal 'New ticket on a given project', issue.subject
181 assert_equal 'New ticket on a given project', issue.subject
182 assert_equal User.find_by_login('jsmith'), issue.author
182 assert_equal User.find_by_login('jsmith'), issue.author
183 assert_equal Project.find(2), issue.project
183 assert_equal Project.find(2), issue.project
184 assert_equal 'Feature request', issue.tracker.to_s
184 assert_equal 'Feature request', issue.tracker.to_s
185 assert_nil issue.category
185 assert_nil issue.category
186 assert_equal 'High', issue.priority.to_s
186 assert_equal 'High', issue.priority.to_s
187 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
187 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
188 end
188 end
189
189
190 def test_add_issue_with_spaces_between_attribute_and_separator
190 def test_add_issue_with_spaces_between_attribute_and_separator
191 issue = submit_email(
191 issue = submit_email(
192 'ticket_with_spaces_between_attribute_and_separator.eml',
192 'ticket_with_spaces_between_attribute_and_separator.eml',
193 :allow_override => 'tracker,category,priority'
193 :allow_override => 'tracker,category,priority'
194 )
194 )
195 assert issue.is_a?(Issue)
195 assert issue.is_a?(Issue)
196 assert !issue.new_record?
196 assert !issue.new_record?
197 issue.reload
197 issue.reload
198 assert_equal 'New ticket on a given project', issue.subject
198 assert_equal 'New ticket on a given project', issue.subject
199 assert_equal User.find_by_login('jsmith'), issue.author
199 assert_equal User.find_by_login('jsmith'), issue.author
200 assert_equal Project.find(2), issue.project
200 assert_equal Project.find(2), issue.project
201 assert_equal 'Feature request', issue.tracker.to_s
201 assert_equal 'Feature request', issue.tracker.to_s
202 assert_equal 'Stock management', issue.category.to_s
202 assert_equal 'Stock management', issue.category.to_s
203 assert_equal 'Urgent', issue.priority.to_s
203 assert_equal 'Urgent', issue.priority.to_s
204 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
204 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
205 end
205 end
206
206
207 def test_add_issue_with_attachment_to_specific_project
207 def test_add_issue_with_attachment_to_specific_project
208 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
208 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
209 assert issue.is_a?(Issue)
209 assert issue.is_a?(Issue)
210 assert !issue.new_record?
210 assert !issue.new_record?
211 issue.reload
211 issue.reload
212 assert_equal 'Ticket created by email with attachment', issue.subject
212 assert_equal 'Ticket created by email with attachment', issue.subject
213 assert_equal User.find_by_login('jsmith'), issue.author
213 assert_equal User.find_by_login('jsmith'), issue.author
214 assert_equal Project.find(2), issue.project
214 assert_equal Project.find(2), issue.project
215 assert_equal 'This is a new ticket with attachments', issue.description
215 assert_equal 'This is a new ticket with attachments', issue.description
216 # Attachment properties
216 # Attachment properties
217 assert_equal 1, issue.attachments.size
217 assert_equal 1, issue.attachments.size
218 assert_equal 'Paella.jpg', issue.attachments.first.filename
218 assert_equal 'Paella.jpg', issue.attachments.first.filename
219 assert_equal 'image/jpeg', issue.attachments.first.content_type
219 assert_equal 'image/jpeg', issue.attachments.first.content_type
220 assert_equal 10790, issue.attachments.first.filesize
220 assert_equal 10790, issue.attachments.first.filesize
221 end
221 end
222
222
223 def test_add_issue_with_custom_fields
223 def test_add_issue_with_custom_fields
224 issue = submit_email('ticket_with_custom_fields.eml',
224 issue = submit_email('ticket_with_custom_fields.eml',
225 :issue => {:project => 'onlinestore'}, :allow_override => ['database', 'Searchable_field']
225 :issue => {:project => 'onlinestore'}, :allow_override => ['database', 'Searchable_field']
226 )
226 )
227 assert issue.is_a?(Issue)
227 assert issue.is_a?(Issue)
228 assert !issue.new_record?
228 assert !issue.new_record?
229 issue.reload
229 issue.reload
230 assert_equal 'New ticket with custom field values', issue.subject
230 assert_equal 'New ticket with custom field values', issue.subject
231 assert_equal 'PostgreSQL', issue.custom_field_value(1)
231 assert_equal 'PostgreSQL', issue.custom_field_value(1)
232 assert_equal 'Value for a custom field', issue.custom_field_value(2)
232 assert_equal 'Value for a custom field', issue.custom_field_value(2)
233 assert !issue.description.match(/^searchable field:/i)
233 assert !issue.description.match(/^searchable field:/i)
234 end
234 end
235
235
236 def test_add_issue_with_version_custom_fields
236 def test_add_issue_with_version_custom_fields
237 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
237 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
238
238
239 issue = submit_email('ticket_with_custom_fields.eml',
239 issue = submit_email('ticket_with_custom_fields.eml',
240 :issue => {:project => 'ecookbook'}, :allow_override => ['affected version']
240 :issue => {:project => 'ecookbook'}, :allow_override => ['affected version']
241 ) do |email|
241 ) do |email|
242 email << "Affected version: 1.0\n"
242 email << "Affected version: 1.0\n"
243 end
243 end
244 assert issue.is_a?(Issue)
244 assert issue.is_a?(Issue)
245 assert !issue.new_record?
245 assert !issue.new_record?
246 issue.reload
246 issue.reload
247 assert_equal '2', issue.custom_field_value(field)
247 assert_equal '2', issue.custom_field_value(field)
248 end
248 end
249
249
250 def test_add_issue_should_match_assignee_on_display_name
250 def test_add_issue_should_match_assignee_on_display_name
251 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
251 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
252 User.add_to_project(user, Project.find(2))
252 User.add_to_project(user, Project.find(2))
253 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
253 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
254 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
254 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
255 end
255 end
256 assert issue.is_a?(Issue)
256 assert issue.is_a?(Issue)
257 assert_equal user, issue.assigned_to
257 assert_equal user, issue.assigned_to
258 end
258 end
259
259
260 def test_add_issue_should_set_default_start_date
260 def test_add_issue_should_set_default_start_date
261 with_settings :default_issue_start_date_to_creation_date => '1' do
261 with_settings :default_issue_start_date_to_creation_date => '1' do
262 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
262 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
263 assert issue.is_a?(Issue)
263 assert issue.is_a?(Issue)
264 assert_equal Date.today, issue.start_date
264 assert_equal Date.today, issue.start_date
265 end
265 end
266 end
266 end
267
267
268 def test_add_issue_should_add_cc_as_watchers
268 def test_add_issue_should_add_cc_as_watchers
269 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
269 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
270 assert issue.is_a?(Issue)
270 assert issue.is_a?(Issue)
271 assert !issue.new_record?
271 assert !issue.new_record?
272 issue.reload
272 issue.reload
273 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
273 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
274 assert_equal 1, issue.watcher_user_ids.size
274 assert_equal 1, issue.watcher_user_ids.size
275 end
275 end
276
276
277 def test_add_issue_from_additional_email_address
277 def test_add_issue_from_additional_email_address
278 user = User.find(2)
278 user = User.find(2)
279 user.mail = 'mainaddress@somenet.foo'
279 user.mail = 'mainaddress@somenet.foo'
280 user.save!
280 user.save!
281 EmailAddress.create!(:user => user, :address => 'jsmith@somenet.foo')
281 EmailAddress.create!(:user => user, :address => 'jsmith@somenet.foo')
282
282
283 issue = submit_email('ticket_on_given_project.eml')
283 issue = submit_email('ticket_on_given_project.eml')
284 assert issue
284 assert issue
285 assert_equal user, issue.author
285 assert_equal user, issue.author
286 end
286 end
287
287
288 def test_add_issue_by_unknown_user
288 def test_add_issue_by_unknown_user
289 assert_no_difference 'User.count' do
289 assert_no_difference 'User.count' do
290 assert_equal false,
290 assert_equal false,
291 submit_email(
291 submit_email(
292 'ticket_by_unknown_user.eml',
292 'ticket_by_unknown_user.eml',
293 :issue => {:project => 'ecookbook'}
293 :issue => {:project => 'ecookbook'}
294 )
294 )
295 end
295 end
296 end
296 end
297
297
298 def test_add_issue_by_anonymous_user
298 def test_add_issue_by_anonymous_user
299 Role.anonymous.add_permission!(:add_issues)
299 Role.anonymous.add_permission!(:add_issues)
300 assert_no_difference 'User.count' do
300 assert_no_difference 'User.count' do
301 issue = submit_email(
301 issue = submit_email(
302 'ticket_by_unknown_user.eml',
302 'ticket_by_unknown_user.eml',
303 :issue => {:project => 'ecookbook'},
303 :issue => {:project => 'ecookbook'},
304 :unknown_user => 'accept'
304 :unknown_user => 'accept'
305 )
305 )
306 assert issue.is_a?(Issue)
306 assert issue.is_a?(Issue)
307 assert issue.author.anonymous?
307 assert issue.author.anonymous?
308 end
308 end
309 end
309 end
310
310
311 def test_add_issue_by_anonymous_user_with_no_from_address
311 def test_add_issue_by_anonymous_user_with_no_from_address
312 Role.anonymous.add_permission!(:add_issues)
312 Role.anonymous.add_permission!(:add_issues)
313 assert_no_difference 'User.count' do
313 assert_no_difference 'User.count' do
314 issue = submit_email(
314 issue = submit_email(
315 'ticket_by_empty_user.eml',
315 'ticket_by_empty_user.eml',
316 :issue => {:project => 'ecookbook'},
316 :issue => {:project => 'ecookbook'},
317 :unknown_user => 'accept'
317 :unknown_user => 'accept'
318 )
318 )
319 assert issue.is_a?(Issue)
319 assert issue.is_a?(Issue)
320 assert issue.author.anonymous?
320 assert issue.author.anonymous?
321 end
321 end
322 end
322 end
323
323
324 def test_add_issue_by_anonymous_user_on_private_project
324 def test_add_issue_by_anonymous_user_on_private_project
325 Role.anonymous.add_permission!(:add_issues)
325 Role.anonymous.add_permission!(:add_issues)
326 assert_no_difference 'User.count' do
326 assert_no_difference 'User.count' do
327 assert_no_difference 'Issue.count' do
327 assert_no_difference 'Issue.count' do
328 assert_equal false,
328 assert_equal false,
329 submit_email(
329 submit_email(
330 'ticket_by_unknown_user.eml',
330 'ticket_by_unknown_user.eml',
331 :issue => {:project => 'onlinestore'},
331 :issue => {:project => 'onlinestore'},
332 :unknown_user => 'accept'
332 :unknown_user => 'accept'
333 )
333 )
334 end
334 end
335 end
335 end
336 end
336 end
337
337
338 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
338 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
339 assert_no_difference 'User.count' do
339 assert_no_difference 'User.count' do
340 assert_difference 'Issue.count' do
340 assert_difference 'Issue.count' do
341 issue = submit_email(
341 issue = submit_email(
342 'ticket_by_unknown_user.eml',
342 'ticket_by_unknown_user.eml',
343 :issue => {:project => 'onlinestore'},
343 :issue => {:project => 'onlinestore'},
344 :no_permission_check => '1',
344 :no_permission_check => '1',
345 :unknown_user => 'accept'
345 :unknown_user => 'accept'
346 )
346 )
347 assert issue.is_a?(Issue)
347 assert issue.is_a?(Issue)
348 assert issue.author.anonymous?
348 assert issue.author.anonymous?
349 assert !issue.project.is_public?
349 assert !issue.project.is_public?
350 end
350 end
351 end
351 end
352 end
352 end
353
353
354 def test_add_issue_by_created_user
354 def test_add_issue_by_created_user
355 Setting.default_language = 'en'
355 Setting.default_language = 'en'
356 assert_difference 'User.count' do
356 assert_difference 'User.count' do
357 issue = submit_email(
357 issue = submit_email(
358 'ticket_by_unknown_user.eml',
358 'ticket_by_unknown_user.eml',
359 :issue => {:project => 'ecookbook'},
359 :issue => {:project => 'ecookbook'},
360 :unknown_user => 'create'
360 :unknown_user => 'create'
361 )
361 )
362 assert issue.is_a?(Issue)
362 assert issue.is_a?(Issue)
363 assert issue.author.active?
363 assert issue.author.active?
364 assert_equal 'john.doe@somenet.foo', issue.author.mail
364 assert_equal 'john.doe@somenet.foo', issue.author.mail
365 assert_equal 'John', issue.author.firstname
365 assert_equal 'John', issue.author.firstname
366 assert_equal 'Doe', issue.author.lastname
366 assert_equal 'Doe', issue.author.lastname
367
367
368 # account information
368 # account information
369 email = ActionMailer::Base.deliveries.first
369 email = ActionMailer::Base.deliveries.first
370 assert_not_nil email
370 assert_not_nil email
371 assert email.subject.include?('account activation')
371 assert email.subject.include?('account activation')
372 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
372 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
373 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
373 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
374 assert_equal issue.author, User.try_to_login(login, password)
374 assert_equal issue.author, User.try_to_login(login, password)
375 end
375 end
376 end
376 end
377
377
378 def test_add_issue_should_send_notification
378 def test_add_issue_should_send_notification
379 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
379 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
380 assert issue.is_a?(Issue)
380 assert issue.is_a?(Issue)
381 assert !issue.new_record?
381 assert !issue.new_record?
382
382
383 mail = ActionMailer::Base.deliveries.last
383 mail = ActionMailer::Base.deliveries.last
384 assert_not_nil mail
384 assert_not_nil mail
385 assert mail.subject.include?("##{issue.id}")
385 assert mail.subject.include?("##{issue.id}")
386 assert mail.subject.include?('New ticket on a given project')
386 assert mail.subject.include?('New ticket on a given project')
387 end
387 end
388
388
389 def test_created_user_should_be_added_to_groups
389 def test_created_user_should_be_added_to_groups
390 group1 = Group.generate!
390 group1 = Group.generate!
391 group2 = Group.generate!
391 group2 = Group.generate!
392
392
393 assert_difference 'User.count' do
393 assert_difference 'User.count' do
394 submit_email(
394 submit_email(
395 'ticket_by_unknown_user.eml',
395 'ticket_by_unknown_user.eml',
396 :issue => {:project => 'ecookbook'},
396 :issue => {:project => 'ecookbook'},
397 :unknown_user => 'create',
397 :unknown_user => 'create',
398 :default_group => "#{group1.name},#{group2.name}"
398 :default_group => "#{group1.name},#{group2.name}"
399 )
399 )
400 end
400 end
401 user = User.order('id DESC').first
401 user = User.order('id DESC').first
402 assert_equal [group1, group2].sort, user.groups.sort
402 assert_equal [group1, group2].sort, user.groups.sort
403 end
403 end
404
404
405 def test_created_user_should_not_receive_account_information_with_no_account_info_option
405 def test_created_user_should_not_receive_account_information_with_no_account_info_option
406 assert_difference 'User.count' do
406 assert_difference 'User.count' do
407 submit_email(
407 submit_email(
408 'ticket_by_unknown_user.eml',
408 'ticket_by_unknown_user.eml',
409 :issue => {:project => 'ecookbook'},
409 :issue => {:project => 'ecookbook'},
410 :unknown_user => 'create',
410 :unknown_user => 'create',
411 :no_account_notice => '1'
411 :no_account_notice => '1'
412 )
412 )
413 end
413 end
414
414
415 # only 1 email for the new issue notification
415 # only 1 email for the new issue notification
416 assert_equal 1, ActionMailer::Base.deliveries.size
416 assert_equal 1, ActionMailer::Base.deliveries.size
417 email = ActionMailer::Base.deliveries.first
417 email = ActionMailer::Base.deliveries.first
418 assert_include 'Ticket by unknown user', email.subject
418 assert_include 'Ticket by unknown user', email.subject
419 end
419 end
420
420
421 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
421 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
422 assert_difference 'User.count' do
422 assert_difference 'User.count' do
423 submit_email(
423 submit_email(
424 'ticket_by_unknown_user.eml',
424 'ticket_by_unknown_user.eml',
425 :issue => {:project => 'ecookbook'},
425 :issue => {:project => 'ecookbook'},
426 :unknown_user => 'create',
426 :unknown_user => 'create',
427 :no_notification => '1'
427 :no_notification => '1'
428 )
428 )
429 end
429 end
430 user = User.order('id DESC').first
430 user = User.order('id DESC').first
431 assert_equal 'none', user.mail_notification
431 assert_equal 'none', user.mail_notification
432 end
432 end
433
433
434 def test_add_issue_without_from_header
434 def test_add_issue_without_from_header
435 Role.anonymous.add_permission!(:add_issues)
435 Role.anonymous.add_permission!(:add_issues)
436 assert_equal false, submit_email('ticket_without_from_header.eml')
436 assert_equal false, submit_email('ticket_without_from_header.eml')
437 end
437 end
438
438
439 def test_add_issue_with_invalid_attributes
439 def test_add_issue_with_invalid_attributes
440 with_settings :default_issue_start_date_to_creation_date => '0' do
440 with_settings :default_issue_start_date_to_creation_date => '0' do
441 issue = submit_email(
441 issue = submit_email(
442 'ticket_with_invalid_attributes.eml',
442 'ticket_with_invalid_attributes.eml',
443 :allow_override => 'tracker,category,priority'
443 :allow_override => 'tracker,category,priority'
444 )
444 )
445 assert issue.is_a?(Issue)
445 assert issue.is_a?(Issue)
446 assert !issue.new_record?
446 assert !issue.new_record?
447 issue.reload
447 issue.reload
448 assert_nil issue.assigned_to
448 assert_nil issue.assigned_to
449 assert_nil issue.start_date
449 assert_nil issue.start_date
450 assert_nil issue.due_date
450 assert_nil issue.due_date
451 assert_equal 0, issue.done_ratio
451 assert_equal 0, issue.done_ratio
452 assert_equal 'Normal', issue.priority.to_s
452 assert_equal 'Normal', issue.priority.to_s
453 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
453 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
454 end
454 end
455 end
455 end
456
456
457 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
457 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
458 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
458 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
459 email.gsub!(/^Project:.+$/, 'Project: invalid')
459 email.gsub!(/^Project:.+$/, 'Project: invalid')
460 end
460 end
461 assert issue.is_a?(Issue)
461 assert issue.is_a?(Issue)
462 assert !issue.new_record?
462 assert !issue.new_record?
463 assert_equal 'ecookbook', issue.project.identifier
463 assert_equal 'ecookbook', issue.project.identifier
464 end
464 end
465
465
466 def test_add_issue_with_localized_attributes
466 def test_add_issue_with_localized_attributes
467 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
467 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
468 issue = submit_email(
468 issue = submit_email(
469 'ticket_with_localized_attributes.eml',
469 'ticket_with_localized_attributes.eml',
470 :allow_override => 'tracker,category,priority'
470 :allow_override => 'tracker,category,priority'
471 )
471 )
472 assert issue.is_a?(Issue)
472 assert issue.is_a?(Issue)
473 assert !issue.new_record?
473 assert !issue.new_record?
474 issue.reload
474 issue.reload
475 assert_equal 'New ticket on a given project', issue.subject
475 assert_equal 'New ticket on a given project', issue.subject
476 assert_equal User.find_by_login('jsmith'), issue.author
476 assert_equal User.find_by_login('jsmith'), issue.author
477 assert_equal Project.find(2), issue.project
477 assert_equal Project.find(2), issue.project
478 assert_equal 'Feature request', issue.tracker.to_s
478 assert_equal 'Feature request', issue.tracker.to_s
479 assert_equal 'Stock management', issue.category.to_s
479 assert_equal 'Stock management', issue.category.to_s
480 assert_equal 'Urgent', issue.priority.to_s
480 assert_equal 'Urgent', issue.priority.to_s
481 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
481 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
482 end
482 end
483
483
484 def test_add_issue_with_japanese_keywords
484 def test_add_issue_with_japanese_keywords
485 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
485 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
486 tracker = Tracker.generate!(:name => ja_dev)
486 tracker = Tracker.generate!(:name => ja_dev)
487 Project.find(1).trackers << tracker
487 Project.find(1).trackers << tracker
488 issue = submit_email(
488 issue = submit_email(
489 'japanese_keywords_iso_2022_jp.eml',
489 'japanese_keywords_iso_2022_jp.eml',
490 :issue => {:project => 'ecookbook'},
490 :issue => {:project => 'ecookbook'},
491 :allow_override => 'tracker'
491 :allow_override => 'tracker'
492 )
492 )
493 assert_kind_of Issue, issue
493 assert_kind_of Issue, issue
494 assert_equal tracker, issue.tracker
494 assert_equal tracker, issue.tracker
495 end
495 end
496
496
497 def test_add_issue_from_apple_mail
497 def test_add_issue_from_apple_mail
498 issue = submit_email(
498 issue = submit_email(
499 'apple_mail_with_attachment.eml',
499 'apple_mail_with_attachment.eml',
500 :issue => {:project => 'ecookbook'}
500 :issue => {:project => 'ecookbook'}
501 )
501 )
502 assert_kind_of Issue, issue
502 assert_kind_of Issue, issue
503 assert_equal 1, issue.attachments.size
503 assert_equal 1, issue.attachments.size
504
504
505 attachment = issue.attachments.first
505 attachment = issue.attachments.first
506 assert_equal 'paella.jpg', attachment.filename
506 assert_equal 'paella.jpg', attachment.filename
507 assert_equal 10790, attachment.filesize
507 assert_equal 10790, attachment.filesize
508 assert File.exist?(attachment.diskfile)
508 assert File.exist?(attachment.diskfile)
509 assert_equal 10790, File.size(attachment.diskfile)
509 assert_equal 10790, File.size(attachment.diskfile)
510 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
510 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
511 end
511 end
512
512
513 def test_thunderbird_with_attachment_ja
513 def test_thunderbird_with_attachment_ja
514 issue = submit_email(
514 issue = submit_email(
515 'thunderbird_with_attachment_ja.eml',
515 'thunderbird_with_attachment_ja.eml',
516 :issue => {:project => 'ecookbook'}
516 :issue => {:project => 'ecookbook'}
517 )
517 )
518 assert_kind_of Issue, issue
518 assert_kind_of Issue, issue
519 assert_equal 1, issue.attachments.size
519 assert_equal 1, issue.attachments.size
520 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
520 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
521 attachment = issue.attachments.first
521 attachment = issue.attachments.first
522 assert_equal ja, attachment.filename
522 assert_equal ja, attachment.filename
523 assert_equal 5, attachment.filesize
523 assert_equal 5, attachment.filesize
524 assert File.exist?(attachment.diskfile)
524 assert File.exist?(attachment.diskfile)
525 assert_equal 5, File.size(attachment.diskfile)
525 assert_equal 5, File.size(attachment.diskfile)
526 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
526 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
527 end
527 end
528
528
529 def test_gmail_with_attachment_ja
529 def test_gmail_with_attachment_ja
530 issue = submit_email(
530 issue = submit_email(
531 'gmail_with_attachment_ja.eml',
531 'gmail_with_attachment_ja.eml',
532 :issue => {:project => 'ecookbook'}
532 :issue => {:project => 'ecookbook'}
533 )
533 )
534 assert_kind_of Issue, issue
534 assert_kind_of Issue, issue
535 assert_equal 1, issue.attachments.size
535 assert_equal 1, issue.attachments.size
536 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
536 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
537 attachment = issue.attachments.first
537 attachment = issue.attachments.first
538 assert_equal ja, attachment.filename
538 assert_equal ja, attachment.filename
539 assert_equal 5, attachment.filesize
539 assert_equal 5, attachment.filesize
540 assert File.exist?(attachment.diskfile)
540 assert File.exist?(attachment.diskfile)
541 assert_equal 5, File.size(attachment.diskfile)
541 assert_equal 5, File.size(attachment.diskfile)
542 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
542 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
543 end
543 end
544
544
545 def test_thunderbird_with_attachment_latin1
545 def test_thunderbird_with_attachment_latin1
546 issue = submit_email(
546 issue = submit_email(
547 'thunderbird_with_attachment_iso-8859-1.eml',
547 'thunderbird_with_attachment_iso-8859-1.eml',
548 :issue => {:project => 'ecookbook'}
548 :issue => {:project => 'ecookbook'}
549 )
549 )
550 assert_kind_of Issue, issue
550 assert_kind_of Issue, issue
551 assert_equal 1, issue.attachments.size
551 assert_equal 1, issue.attachments.size
552 u = "".force_encoding('UTF-8')
552 u = "".force_encoding('UTF-8')
553 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
553 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
554 11.times { u << u1 }
554 11.times { u << u1 }
555 attachment = issue.attachments.first
555 attachment = issue.attachments.first
556 assert_equal "#{u}.png", attachment.filename
556 assert_equal "#{u}.png", attachment.filename
557 assert_equal 130, attachment.filesize
557 assert_equal 130, attachment.filesize
558 assert File.exist?(attachment.diskfile)
558 assert File.exist?(attachment.diskfile)
559 assert_equal 130, File.size(attachment.diskfile)
559 assert_equal 130, File.size(attachment.diskfile)
560 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
560 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
561 end
561 end
562
562
563 def test_gmail_with_attachment_latin1
563 def test_gmail_with_attachment_latin1
564 issue = submit_email(
564 issue = submit_email(
565 'gmail_with_attachment_iso-8859-1.eml',
565 'gmail_with_attachment_iso-8859-1.eml',
566 :issue => {:project => 'ecookbook'}
566 :issue => {:project => 'ecookbook'}
567 )
567 )
568 assert_kind_of Issue, issue
568 assert_kind_of Issue, issue
569 assert_equal 1, issue.attachments.size
569 assert_equal 1, issue.attachments.size
570 u = "".force_encoding('UTF-8')
570 u = "".force_encoding('UTF-8')
571 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
571 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
572 11.times { u << u1 }
572 11.times { u << u1 }
573 attachment = issue.attachments.first
573 attachment = issue.attachments.first
574 assert_equal "#{u}.txt", attachment.filename
574 assert_equal "#{u}.txt", attachment.filename
575 assert_equal 5, attachment.filesize
575 assert_equal 5, attachment.filesize
576 assert File.exist?(attachment.diskfile)
576 assert File.exist?(attachment.diskfile)
577 assert_equal 5, File.size(attachment.diskfile)
577 assert_equal 5, File.size(attachment.diskfile)
578 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
578 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
579 end
579 end
580
580
581 def test_mail_with_attachment_latin2
581 def test_mail_with_attachment_latin2
582 issue = submit_email(
582 issue = submit_email(
583 'ticket_with_text_attachment_iso-8859-2.eml',
583 'ticket_with_text_attachment_iso-8859-2.eml',
584 :issue => {:project => 'ecookbook'}
584 :issue => {:project => 'ecookbook'}
585 )
585 )
586 assert_kind_of Issue, issue
586 assert_kind_of Issue, issue
587 assert_equal 1, issue.attachments.size
587 assert_equal 1, issue.attachments.size
588 attachment = issue.attachments.first
588 attachment = issue.attachments.first
589 assert_equal 'latin2.txt', attachment.filename
589 assert_equal 'latin2.txt', attachment.filename
590 assert_equal 19, attachment.filesize
590 assert_equal 19, attachment.filesize
591 assert File.exist?(attachment.diskfile)
591 assert File.exist?(attachment.diskfile)
592 assert_equal 19, File.size(attachment.diskfile)
592 assert_equal 19, File.size(attachment.diskfile)
593 content = "p\xF8\xEDli\xB9 \xBEluou\xE8k\xFD k\xF9n".force_encoding('CP852')
593 content = "p\xF8\xEDli\xB9 \xBEluou\xE8k\xFD k\xF9n".force_encoding('CP852')
594 assert_equal content, File.read(attachment.diskfile).force_encoding('CP852')
594 assert_equal content, File.read(attachment.diskfile).force_encoding('CP852')
595 end
595 end
596
596
597 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
597 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
598 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
598 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
599 assert_include 'first', issue.description
599 assert_include 'first', issue.description
600 assert_include 'second', issue.description
600 assert_include 'second', issue.description
601 assert_include 'third', issue.description
601 assert_include 'third', issue.description
602 end
602 end
603
603
604 def test_attachment_text_part_should_be_added_as_issue_attachment
604 def test_attachment_text_part_should_be_added_as_issue_attachment
605 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
605 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
606 assert_not_include 'Plain text attachment', issue.description
606 assert_not_include 'Plain text attachment', issue.description
607 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
607 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
608 assert_not_nil attachment
608 assert_not_nil attachment
609 assert_include 'Plain text attachment', File.read(attachment.diskfile)
609 assert_include 'Plain text attachment', File.read(attachment.diskfile)
610 end
610 end
611
611
612 def test_add_issue_with_iso_8859_1_subject
612 def test_add_issue_with_iso_8859_1_subject
613 issue = submit_email(
613 issue = submit_email(
614 'subject_as_iso-8859-1.eml',
614 'subject_as_iso-8859-1.eml',
615 :issue => {:project => 'ecookbook'}
615 :issue => {:project => 'ecookbook'}
616 )
616 )
617 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
617 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
618 assert_kind_of Issue, issue
618 assert_kind_of Issue, issue
619 assert_equal str, issue.subject
619 assert_equal str, issue.subject
620 end
620 end
621
621
622 def test_quoted_printable_utf8
622 def test_quoted_printable_utf8
623 issue = submit_email(
623 issue = submit_email(
624 'quoted_printable_utf8.eml',
624 'quoted_printable_utf8.eml',
625 :issue => {:project => 'ecookbook'}
625 :issue => {:project => 'ecookbook'}
626 )
626 )
627 assert_kind_of Issue, issue
627 assert_kind_of Issue, issue
628 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
628 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
629 assert_equal str, issue.description
629 assert_equal str, issue.description
630 end
630 end
631
631
632 def test_gmail_iso8859_2
632 def test_gmail_iso8859_2
633 issue = submit_email(
633 issue = submit_email(
634 'gmail-iso8859-2.eml',
634 'gmail-iso8859-2.eml',
635 :issue => {:project => 'ecookbook'}
635 :issue => {:project => 'ecookbook'}
636 )
636 )
637 assert_kind_of Issue, issue
637 assert_kind_of Issue, issue
638 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
638 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
639 assert issue.description.include?(str)
639 assert issue.description.include?(str)
640 end
640 end
641
641
642 def test_add_issue_with_japanese_subject
642 def test_add_issue_with_japanese_subject
643 issue = submit_email(
643 issue = submit_email(
644 'subject_japanese_1.eml',
644 'subject_japanese_1.eml',
645 :issue => {:project => 'ecookbook'}
645 :issue => {:project => 'ecookbook'}
646 )
646 )
647 assert_kind_of Issue, issue
647 assert_kind_of Issue, issue
648 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
648 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
649 assert_equal ja, issue.subject
649 assert_equal ja, issue.subject
650 end
650 end
651
651
652 def test_add_issue_with_korean_body
652 def test_add_issue_with_korean_body
653 # Make sure mail bodies with a charset unknown to Ruby
653 # Make sure mail bodies with a charset unknown to Ruby
654 # but known to the Mail gem 2.5.4 are handled correctly
654 # but known to the Mail gem 2.5.4 are handled correctly
655 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
655 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
656 issue = submit_email(
656 issue = submit_email(
657 'body_ks_c_5601-1987.eml',
657 'body_ks_c_5601-1987.eml',
658 :issue => {:project => 'ecookbook'}
658 :issue => {:project => 'ecookbook'}
659 )
659 )
660 assert_kind_of Issue, issue
660 assert_kind_of Issue, issue
661 assert_equal kr, issue.description
661 assert_equal kr, issue.description
662 end
662 end
663
663
664 def test_add_issue_with_no_subject_header
664 def test_add_issue_with_no_subject_header
665 issue = submit_email(
665 issue = submit_email(
666 'no_subject_header.eml',
666 'no_subject_header.eml',
667 :issue => {:project => 'ecookbook'}
667 :issue => {:project => 'ecookbook'}
668 )
668 )
669 assert_kind_of Issue, issue
669 assert_kind_of Issue, issue
670 assert_equal '(no subject)', issue.subject
670 assert_equal '(no subject)', issue.subject
671 end
671 end
672
672
673 def test_add_issue_with_mixed_japanese_subject
673 def test_add_issue_with_mixed_japanese_subject
674 issue = submit_email(
674 issue = submit_email(
675 'subject_japanese_2.eml',
675 'subject_japanese_2.eml',
676 :issue => {:project => 'ecookbook'}
676 :issue => {:project => 'ecookbook'}
677 )
677 )
678 assert_kind_of Issue, issue
678 assert_kind_of Issue, issue
679 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
679 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
680 assert_equal ja, issue.subject
680 assert_equal ja, issue.subject
681 end
681 end
682
682
683 def test_should_ignore_emails_from_locked_users
683 def test_should_ignore_emails_from_locked_users
684 User.find(2).lock!
684 User.find(2).lock!
685
685
686 MailHandler.any_instance.expects(:dispatch).never
686 MailHandler.any_instance.expects(:dispatch).never
687 assert_no_difference 'Issue.count' do
687 assert_no_difference 'Issue.count' do
688 assert_equal false, submit_email('ticket_on_given_project.eml')
688 assert_equal false, submit_email('ticket_on_given_project.eml')
689 end
689 end
690 end
690 end
691
691
692 def test_should_ignore_emails_from_emission_address
692 def test_should_ignore_emails_from_emission_address
693 Role.anonymous.add_permission!(:add_issues)
693 Role.anonymous.add_permission!(:add_issues)
694 assert_no_difference 'User.count' do
694 assert_no_difference 'User.count' do
695 assert_equal false,
695 assert_equal false,
696 submit_email(
696 submit_email(
697 'ticket_from_emission_address.eml',
697 'ticket_from_emission_address.eml',
698 :issue => {:project => 'ecookbook'},
698 :issue => {:project => 'ecookbook'},
699 :unknown_user => 'create'
699 :unknown_user => 'create'
700 )
700 )
701 end
701 end
702 end
702 end
703
703
704 def test_should_ignore_auto_replied_emails
704 def test_should_ignore_auto_replied_emails
705 MailHandler.any_instance.expects(:dispatch).never
705 MailHandler.any_instance.expects(:dispatch).never
706 [
706 [
707 "Auto-Submitted: auto-replied",
707 "Auto-Submitted: auto-replied",
708 "Auto-Submitted: Auto-Replied",
708 "Auto-Submitted: Auto-Replied",
709 "Auto-Submitted: auto-generated",
709 "Auto-Submitted: auto-generated",
710 'X-Autoreply: yes'
710 'X-Autoreply: yes'
711 ].each do |header|
711 ].each do |header|
712 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
712 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
713 raw = header + "\n" + raw
713 raw = header + "\n" + raw
714
714
715 assert_no_difference 'Issue.count' do
715 assert_no_difference 'Issue.count' do
716 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
716 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
717 end
717 end
718 end
718 end
719 end
719 end
720
720
721 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
721 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
722 [
722 [
723 "Auto-Submitted: auto-forwarded"
723 "Auto-Submitted: auto-forwarded"
724 ].each do |header|
724 ].each do |header|
725 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
725 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
726 raw = header + "\n" + raw
726 raw = header + "\n" + raw
727
727
728 assert_difference 'Issue.count', 1 do
728 assert_difference 'Issue.count', 1 do
729 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
729 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
730 end
730 end
731 end
731 end
732 end
732 end
733
733
734 def test_add_issue_should_send_email_notification
734 def test_add_issue_should_send_email_notification
735 Setting.notified_events = ['issue_added']
735 Setting.notified_events = ['issue_added']
736 # This email contains: 'Project: onlinestore'
736 # This email contains: 'Project: onlinestore'
737 issue = submit_email('ticket_on_given_project.eml')
737 issue = submit_email('ticket_on_given_project.eml')
738 assert issue.is_a?(Issue)
738 assert issue.is_a?(Issue)
739 assert_equal 1, ActionMailer::Base.deliveries.size
739 assert_equal 1, ActionMailer::Base.deliveries.size
740 end
740 end
741
741
742 def test_update_issue
742 def test_update_issue
743 journal = submit_email('ticket_reply.eml')
743 journal = submit_email('ticket_reply.eml')
744 assert journal.is_a?(Journal)
744 assert journal.is_a?(Journal)
745 assert_equal User.find_by_login('jsmith'), journal.user
745 assert_equal User.find_by_login('jsmith'), journal.user
746 assert_equal Issue.find(2), journal.journalized
746 assert_equal Issue.find(2), journal.journalized
747 assert_match /This is reply/, journal.notes
747 assert_match /This is reply/, journal.notes
748 assert_equal false, journal.private_notes
748 assert_equal false, journal.private_notes
749 assert_equal 'Feature request', journal.issue.tracker.name
749 assert_equal 'Feature request', journal.issue.tracker.name
750 end
750 end
751
751
752 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
752 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
753 journal = submit_email('ticket_reply_with_status.eml') do |email|
753 journal = submit_email('ticket_reply_with_status.eml') do |email|
754 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
754 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
755 end
755 end
756 assert journal.is_a?(Journal)
756 assert journal.is_a?(Journal)
757 assert_equal Issue.find(2), journal.journalized
757 assert_equal Issue.find(2), journal.journalized
758 end
758 end
759
759
760 def test_update_issue_should_accept_issue_id_inside_brackets
760 def test_update_issue_should_accept_issue_id_inside_brackets
761 journal = submit_email('ticket_reply_with_status.eml') do |email|
761 journal = submit_email('ticket_reply_with_status.eml') do |email|
762 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
762 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
763 end
763 end
764 assert journal.is_a?(Journal)
764 assert journal.is_a?(Journal)
765 assert_equal Issue.find(2), journal.journalized
765 assert_equal Issue.find(2), journal.journalized
766 end
766 end
767
767
768 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
768 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
769 journal = submit_email('ticket_reply_with_status.eml') do |email|
769 journal = submit_email('ticket_reply_with_status.eml') do |email|
770 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
770 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
771 end
771 end
772 assert journal.is_a?(Journal)
772 assert journal.is_a?(Journal)
773 assert_equal Issue.find(2), journal.journalized
773 assert_equal Issue.find(2), journal.journalized
774 end
774 end
775
775
776 def test_update_issue_with_attribute_changes
776 def test_update_issue_with_attribute_changes
777 journal = submit_email('ticket_reply_with_status.eml', :allow_override => ['status','assigned_to','start_date','due_date', 'float field'])
777 journal = submit_email('ticket_reply_with_status.eml', :allow_override => ['status','assigned_to','start_date','due_date', 'float field'])
778 assert journal.is_a?(Journal)
778 assert journal.is_a?(Journal)
779 issue = Issue.find(journal.issue.id)
779 issue = Issue.find(journal.issue.id)
780 assert_equal User.find_by_login('jsmith'), journal.user
780 assert_equal User.find_by_login('jsmith'), journal.user
781 assert_equal Issue.find(2), journal.journalized
781 assert_equal Issue.find(2), journal.journalized
782 assert_match /This is reply/, journal.notes
782 assert_match /This is reply/, journal.notes
783 assert_equal 'Feature request', journal.issue.tracker.name
783 assert_equal 'Feature request', journal.issue.tracker.name
784 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
784 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
785 assert_equal '2010-01-01', issue.start_date.to_s
785 assert_equal '2010-01-01', issue.start_date.to_s
786 assert_equal '2010-12-31', issue.due_date.to_s
786 assert_equal '2010-12-31', issue.due_date.to_s
787 assert_equal User.find_by_login('jsmith'), issue.assigned_to
787 assert_equal User.find_by_login('jsmith'), issue.assigned_to
788 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
788 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
789 # keywords should be removed from the email body
789 # keywords should be removed from the email body
790 assert !journal.notes.match(/^Status:/i)
790 assert !journal.notes.match(/^Status:/i)
791 assert !journal.notes.match(/^Start Date:/i)
791 assert !journal.notes.match(/^Start Date:/i)
792 end
792 end
793
793
794 def test_update_issue_with_attachment
794 def test_update_issue_with_attachment
795 assert_difference 'Journal.count' do
795 assert_difference 'Journal.count' do
796 assert_difference 'JournalDetail.count' do
796 assert_difference 'JournalDetail.count' do
797 assert_difference 'Attachment.count' do
797 assert_difference 'Attachment.count' do
798 assert_no_difference 'Issue.count' do
798 assert_no_difference 'Issue.count' do
799 journal = submit_email('ticket_with_attachment.eml') do |raw|
799 journal = submit_email('ticket_with_attachment.eml') do |raw|
800 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
800 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
801 end
801 end
802 end
802 end
803 end
803 end
804 end
804 end
805 end
805 end
806 journal = Journal.order('id DESC').first
806 journal = Journal.order('id DESC').first
807 assert_equal Issue.find(2), journal.journalized
807 assert_equal Issue.find(2), journal.journalized
808 assert_equal 1, journal.details.size
808 assert_equal 1, journal.details.size
809
809
810 detail = journal.details.first
810 detail = journal.details.first
811 assert_equal 'attachment', detail.property
811 assert_equal 'attachment', detail.property
812 assert_equal 'Paella.jpg', detail.value
812 assert_equal 'Paella.jpg', detail.value
813 end
813 end
814
814
815 def test_update_issue_should_send_email_notification
815 def test_update_issue_should_send_email_notification
816 journal = submit_email('ticket_reply.eml')
816 journal = submit_email('ticket_reply.eml')
817 assert journal.is_a?(Journal)
817 assert journal.is_a?(Journal)
818 assert_equal 1, ActionMailer::Base.deliveries.size
818 assert_equal 1, ActionMailer::Base.deliveries.size
819 end
819 end
820
820
821 def test_update_issue_should_not_set_defaults
821 def test_update_issue_should_not_set_defaults
822 journal = submit_email(
822 journal = submit_email(
823 'ticket_reply.eml',
823 'ticket_reply.eml',
824 :issue => {:tracker => 'Support request', :priority => 'High'}
824 :issue => {:tracker => 'Support request', :priority => 'High'}
825 )
825 )
826 assert journal.is_a?(Journal)
826 assert journal.is_a?(Journal)
827 assert_match /This is reply/, journal.notes
827 assert_match /This is reply/, journal.notes
828 assert_equal 'Feature request', journal.issue.tracker.name
828 assert_equal 'Feature request', journal.issue.tracker.name
829 assert_equal 'Normal', journal.issue.priority.name
829 assert_equal 'Normal', journal.issue.priority.name
830 end
830 end
831
831
832 def test_update_issue_should_add_cc_as_watchers
832 def test_update_issue_should_add_cc_as_watchers
833 Watcher.delete_all
833 Watcher.delete_all
834 issue = Issue.find(2)
834 issue = Issue.find(2)
835
835
836 assert_difference 'Watcher.count' do
836 assert_difference 'Watcher.count' do
837 assert submit_email('issue_update_with_cc.eml')
837 assert submit_email('issue_update_with_cc.eml')
838 end
838 end
839 issue.reload
839 issue.reload
840 assert_equal 1, issue.watcher_user_ids.size
840 assert_equal 1, issue.watcher_user_ids.size
841 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
841 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
842 end
842 end
843
843
844 def test_update_issue_should_not_add_cc_as_watchers_if_already_watching
844 def test_update_issue_should_not_add_cc_as_watchers_if_already_watching
845 Watcher.delete_all
845 Watcher.delete_all
846 issue = Issue.find(2)
846 issue = Issue.find(2)
847 Watcher.create!(:watchable => issue, :user => User.find_by_mail('dlopper@somenet.foo'))
847 Watcher.create!(:watchable => issue, :user => User.find_by_mail('dlopper@somenet.foo'))
848
848
849 assert_no_difference 'Watcher.count' do
849 assert_no_difference 'Watcher.count' do
850 assert submit_email('issue_update_with_cc.eml')
850 assert submit_email('issue_update_with_cc.eml')
851 end
851 end
852 end
852 end
853
853
854 def test_replying_to_a_private_note_should_add_reply_as_private
854 def test_replying_to_a_private_note_should_add_reply_as_private
855 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
855 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
856
856
857 assert_difference 'Journal.count' do
857 assert_difference 'Journal.count' do
858 journal = submit_email('ticket_reply.eml') do |email|
858 journal = submit_email('ticket_reply.eml') do |email|
859 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
859 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
860 end
860 end
861
861
862 assert_kind_of Journal, journal
862 assert_kind_of Journal, journal
863 assert_match /This is reply/, journal.notes
863 assert_match /This is reply/, journal.notes
864 assert_equal true, journal.private_notes
864 assert_equal true, journal.private_notes
865 end
865 end
866 end
866 end
867
867
868 def test_reply_to_a_message
868 def test_reply_to_a_message
869 m = submit_email('message_reply.eml')
869 m = submit_email('message_reply.eml')
870 assert m.is_a?(Message)
870 assert m.is_a?(Message)
871 assert !m.new_record?
871 assert !m.new_record?
872 m.reload
872 m.reload
873 assert_equal 'Reply via email', m.subject
873 assert_equal 'Reply via email', m.subject
874 # The email replies to message #2 which is part of the thread of message #1
874 # The email replies to message #2 which is part of the thread of message #1
875 assert_equal Message.find(1), m.parent
875 assert_equal Message.find(1), m.parent
876 end
876 end
877
877
878 def test_reply_to_a_message_by_subject
878 def test_reply_to_a_message_by_subject
879 m = submit_email('message_reply_by_subject.eml')
879 m = submit_email('message_reply_by_subject.eml')
880 assert m.is_a?(Message)
880 assert m.is_a?(Message)
881 assert !m.new_record?
881 assert !m.new_record?
882 m.reload
882 m.reload
883 assert_equal 'Reply to the first post', m.subject
883 assert_equal 'Reply to the first post', m.subject
884 assert_equal Message.find(1), m.parent
884 assert_equal Message.find(1), m.parent
885 end
885 end
886
886
887 def test_should_convert_tags_of_html_only_emails
887 def test_should_convert_tags_of_html_only_emails
888 with_settings :text_formatting => 'textile' do
888 with_settings :text_formatting => 'textile' do
889 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
889 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
890 assert issue.is_a?(Issue)
890 assert issue.is_a?(Issue)
891 assert !issue.new_record?
891 assert !issue.new_record?
892 issue.reload
892 issue.reload
893 assert_equal 'HTML email', issue.subject
893 assert_equal 'HTML email', issue.subject
894 assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description
894 assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description
895 end
895 end
896 end
896 end
897
897
898 def test_should_handle_outlook_web_access_2010_html_only
898 def test_should_handle_outlook_web_access_2010_html_only
899 issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'})
899 issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'})
900 assert issue.is_a?(Issue)
900 assert issue.is_a?(Issue)
901 issue.reload
901 issue.reload
902 assert_equal 'Upgrade Redmine to 3.0.x', issue.subject
902 assert_equal 'Upgrade Redmine to 3.0.x', issue.subject
903 assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description
903 assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description
904 end
904 end
905
905
906 def test_should_handle_outlook_2010_html_only
906 def test_should_handle_outlook_2010_html_only
907 issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'})
907 issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'})
908 assert issue.is_a?(Issue)
908 assert issue.is_a?(Issue)
909 issue.reload
909 issue.reload
910 assert_equal 'Test email', issue.subject
910 assert_equal 'Test email', issue.subject
911 assert_equal "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" +
911 assert_equal "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" +
912 " no special formatting has been chosen. I’m going to save this as a draft and then manually" +
912 " no special formatting has been chosen. I’m going to save this as a draft and then manually" +
913 " drop it into the Inbox for scraping by Redmine 3.0.2.", issue.description
913 " drop it into the Inbox for scraping by Redmine 3.0.2.", issue.description
914 end
914 end
915
915
916 test "truncate emails with no setting should add the entire email into the issue" do
916 test "truncate emails with no setting should add the entire email into the issue" do
917 with_settings :mail_handler_body_delimiters => '' do
917 with_settings :mail_handler_body_delimiters => '' do
918 issue = submit_email('ticket_on_given_project.eml')
918 issue = submit_email('ticket_on_given_project.eml')
919 assert_issue_created(issue)
919 assert_issue_created(issue)
920 assert issue.description.include?('---')
920 assert issue.description.include?('---')
921 assert issue.description.include?('This paragraph is after the delimiter')
921 assert issue.description.include?('This paragraph is after the delimiter')
922 end
922 end
923 end
923 end
924
924
925 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
925 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
926 with_settings :mail_handler_body_delimiters => '---' do
926 with_settings :mail_handler_body_delimiters => '---' do
927 issue = submit_email('ticket_on_given_project.eml')
927 issue = submit_email('ticket_on_given_project.eml')
928 assert_issue_created(issue)
928 assert_issue_created(issue)
929 assert issue.description.include?('This paragraph is before delimiters')
929 assert issue.description.include?('This paragraph is before delimiters')
930 assert issue.description.include?('--- This line starts with a delimiter')
930 assert issue.description.include?('--- This line starts with a delimiter')
931 assert !issue.description.match(/^---$/)
931 assert !issue.description.match(/^---$/)
932 assert !issue.description.include?('This paragraph is after the delimiter')
932 assert !issue.description.include?('This paragraph is after the delimiter')
933 end
933 end
934 end
934 end
935
935
936 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
936 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
937 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
937 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
938 journal = submit_email('issue_update_with_quoted_reply_above.eml')
938 journal = submit_email('issue_update_with_quoted_reply_above.eml')
939 assert journal.is_a?(Journal)
939 assert journal.is_a?(Journal)
940 assert journal.notes.include?('An update to the issue by the sender.')
940 assert journal.notes.include?('An update to the issue by the sender.')
941 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
941 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
942 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
942 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
943 end
943 end
944 end
944 end
945
945
946 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
946 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
947 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
947 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
948 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
948 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
949 assert journal.is_a?(Journal)
949 assert journal.is_a?(Journal)
950 assert journal.notes.include?('An update to the issue by the sender.')
950 assert journal.notes.include?('An update to the issue by the sender.')
951 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
951 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
952 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
952 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
953 end
953 end
954 end
954 end
955
955
956 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
956 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
957 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
957 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
958 issue = submit_email('ticket_on_given_project.eml')
958 issue = submit_email('ticket_on_given_project.eml')
959 assert_issue_created(issue)
959 assert_issue_created(issue)
960 assert issue.description.include?('This paragraph is before delimiters')
960 assert issue.description.include?('This paragraph is before delimiters')
961 assert !issue.description.include?('BREAK')
961 assert !issue.description.include?('BREAK')
962 assert !issue.description.include?('This paragraph is between delimiters')
962 assert !issue.description.include?('This paragraph is between delimiters')
963 assert !issue.description.match(/^---$/)
963 assert !issue.description.match(/^---$/)
964 assert !issue.description.include?('This paragraph is after the delimiter')
964 assert !issue.description.include?('This paragraph is after the delimiter')
965 end
965 end
966 end
966 end
967
967
968 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
968 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
969 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
969 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
970 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
970 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
971 assert issue.is_a?(Issue)
971 assert issue.is_a?(Issue)
972 assert !issue.new_record?
972 assert !issue.new_record?
973 assert_equal 0, issue.reload.attachments.size
973 assert_equal 0, issue.reload.attachments.size
974 end
974 end
975 end
975 end
976
976
977 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
977 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
978 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
978 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
979 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
979 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
980 assert issue.is_a?(Issue)
980 assert issue.is_a?(Issue)
981 assert !issue.new_record?
981 assert !issue.new_record?
982 assert_equal 1, issue.reload.attachments.size
982 assert_equal 1, issue.reload.attachments.size
983 end
983 end
984 end
984 end
985
985
986 def test_email_with_long_subject_line
986 def test_email_with_long_subject_line
987 issue = submit_email('ticket_with_long_subject.eml')
987 issue = submit_email('ticket_with_long_subject.eml')
988 assert issue.is_a?(Issue)
988 assert issue.is_a?(Issue)
989 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]
989 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]
990 end
990 end
991
991
992 def test_first_keyword_should_be_matched
992 def test_first_keyword_should_be_matched
993 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
993 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
994 assert issue.is_a?(Issue)
994 assert issue.is_a?(Issue)
995 assert_equal 'High', issue.priority.name
995 assert_equal 'High', issue.priority.name
996 end
996 end
997
997
998 def test_keyword_after_delimiter_should_be_ignored
998 def test_keyword_after_delimiter_should_be_ignored
999 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
999 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
1000 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
1000 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
1001 assert issue.is_a?(Issue)
1001 assert issue.is_a?(Issue)
1002 assert_equal 'Normal', issue.priority.name
1002 assert_equal 'Normal', issue.priority.name
1003 end
1003 end
1004 end
1004 end
1005
1005
1006 def test_new_user_from_attributes_should_return_valid_user
1006 def test_new_user_from_attributes_should_return_valid_user
1007 to_test = {
1007 to_test = {
1008 # [address, name] => [login, firstname, lastname]
1008 # [address, name] => [login, firstname, lastname]
1009 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
1009 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
1010 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
1010 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
1011 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
1011 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
1012 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
1012 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
1013 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
1013 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
1014 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
1014 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
1015 }
1015 }
1016
1016
1017 to_test.each do |attrs, expected|
1017 to_test.each do |attrs, expected|
1018 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
1018 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
1019
1019
1020 assert user.valid?, user.errors.full_messages.to_s
1020 assert user.valid?, user.errors.full_messages.to_s
1021 assert_equal attrs.first, user.mail
1021 assert_equal attrs.first, user.mail
1022 assert_equal expected[0], user.login
1022 assert_equal expected[0], user.login
1023 assert_equal expected[1], user.firstname
1023 assert_equal expected[1], user.firstname
1024 assert_equal expected[2], user.lastname
1024 assert_equal expected[2], user.lastname
1025 assert_equal 'only_my_events', user.mail_notification
1025 assert_equal 'only_my_events', user.mail_notification
1026 end
1026 end
1027 end
1027 end
1028
1028
1029 def test_new_user_from_attributes_should_use_default_login_if_invalid
1029 def test_new_user_from_attributes_should_use_default_login_if_invalid
1030 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
1030 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
1031 assert user.valid?
1031 assert user.valid?
1032 assert user.login =~ /^user[a-f0-9]+$/
1032 assert user.login =~ /^user[a-f0-9]+$/
1033 assert_equal 'foo+bar@example.net', user.mail
1033 assert_equal 'foo+bar@example.net', user.mail
1034 end
1034 end
1035
1035
1036 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
1036 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
1037 assert_difference 'User.count' do
1037 assert_difference 'User.count' do
1038 issue = submit_email(
1038 issue = submit_email(
1039 'fullname_of_sender_as_utf8_encoded.eml',
1039 'fullname_of_sender_as_utf8_encoded.eml',
1040 :issue => {:project => 'ecookbook'},
1040 :issue => {:project => 'ecookbook'},
1041 :unknown_user => 'create'
1041 :unknown_user => 'create'
1042 )
1042 )
1043 end
1043 end
1044 user = User.order('id DESC').first
1044 user = User.order('id DESC').first
1045 assert_equal "foo@example.org", user.mail
1045 assert_equal "foo@example.org", user.mail
1046 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
1046 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
1047 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
1047 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
1048 assert_equal str1, user.firstname
1048 assert_equal str1, user.firstname
1049 assert_equal str2, user.lastname
1049 assert_equal str2, user.lastname
1050 end
1050 end
1051
1051
1052 def test_extract_options_from_env_should_return_options
1052 def test_extract_options_from_env_should_return_options
1053 options = MailHandler.extract_options_from_env({
1053 options = MailHandler.extract_options_from_env({
1054 'tracker' => 'defect',
1054 'tracker' => 'defect',
1055 'project' => 'foo',
1055 'project' => 'foo',
1056 'unknown_user' => 'create'
1056 'unknown_user' => 'create'
1057 })
1057 })
1058
1058
1059 assert_equal({
1059 assert_equal({
1060 :issue => {:tracker => 'defect', :project => 'foo'},
1060 :issue => {:tracker => 'defect', :project => 'foo'},
1061 :unknown_user => 'create'
1061 :unknown_user => 'create'
1062 }, options)
1062 }, options)
1063 end
1063 end
1064
1064
1065 def test_safe_receive_should_rescue_exceptions_and_return_false
1065 def test_safe_receive_should_rescue_exceptions_and_return_false
1066 MailHandler.stubs(:receive).raises(Exception.new "Something went wrong")
1066 MailHandler.stubs(:receive).raises(Exception.new "Something went wrong")
1067
1067
1068 assert_equal false, MailHandler.safe_receive
1068 assert_equal false, MailHandler.safe_receive
1069 end
1069 end
1070
1070
1071 private
1071 private
1072
1072
1073 def submit_email(filename, options={})
1073 def submit_email(filename, options={})
1074 raw = IO.read(File.join(FIXTURES_PATH, filename))
1074 raw = IO.read(File.join(FIXTURES_PATH, filename))
1075 yield raw if block_given?
1075 yield raw if block_given?
1076 MailHandler.receive(raw, options)
1076 MailHandler.receive(raw, options)
1077 end
1077 end
1078
1078
1079 def assert_issue_created(issue)
1079 def assert_issue_created(issue)
1080 assert issue.is_a?(Issue)
1080 assert issue.is_a?(Issue)
1081 assert !issue.new_record?
1081 assert !issue.new_record?
1082 issue.reload
1082 issue.reload
1083 end
1083 end
1084 end
1084 end
@@ -1,89 +1,89
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UserPreferenceTest < ActiveSupport::TestCase
20 class UserPreferenceTest < ActiveSupport::TestCase
21 fixtures :users, :user_preferences
21 fixtures :users, :user_preferences
22
22
23 def test_hide_mail_should_default_to_true
23 def test_hide_mail_should_default_to_true
24 preference = UserPreference.new
24 preference = UserPreference.new
25 assert_equal true, preference.hide_mail
25 assert_equal true, preference.hide_mail
26 end
26 end
27
27
28 def test_hide_mail_should_default_to_false_with_setting
28 def test_hide_mail_should_default_to_false_with_setting
29 with_settings :default_users_hide_mail => '0' do
29 with_settings :default_users_hide_mail => '0' do
30 preference = UserPreference.new
30 preference = UserPreference.new
31 assert_equal false, preference.hide_mail
31 assert_equal false, preference.hide_mail
32 end
32 end
33 end
33 end
34
34
35 def test_no_self_notified_should_default_to_true
36 preference = UserPreference.new
37 assert_equal true, preference.no_self_notified
38 end
39
35 def test_create
40 def test_create
36 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
41 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
37 user.login = "newuser"
42 user.login = "newuser"
38 user.password, user.password_confirmation = "password", "password"
43 user.password, user.password_confirmation = "password", "password"
39 assert user.save
44 assert user.save
40
45
41 assert_kind_of UserPreference, user.pref
46 assert_kind_of UserPreference, user.pref
42 assert_kind_of Hash, user.pref.others
47 assert_kind_of Hash, user.pref.others
43 assert user.pref.save
48 assert user.pref.save
44 end
49 end
45
50
46 def test_update
51 def test_update
47 user = User.find(1)
52 user = User.find(1)
48 assert_equal true, user.pref.hide_mail
53 assert_equal true, user.pref.hide_mail
49 user.pref['preftest'] = 'value'
54 user.pref['preftest'] = 'value'
50 assert user.pref.save
55 assert user.pref.save
51
56
52 user.reload
57 user.reload
53 assert_equal 'value', user.pref['preftest']
58 assert_equal 'value', user.pref['preftest']
54 end
59 end
55
60
56 def test_others_hash
61 def test_others_hash
57 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
62 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
58 user.login = "newuser"
63 user.login = "newuser"
59 user.password, user.password_confirmation = "password", "password"
64 user.password, user.password_confirmation = "password", "password"
60 assert user.save
65 assert user.save
61 assert_nil user.preference
66 assert_nil user.preference
62 up = UserPreference.new(:user => user)
67 up = UserPreference.new(:user => user)
63 assert_kind_of Hash, up.others
68 assert_kind_of Hash, up.others
64 up.others = nil
69 up.others = nil
65 assert_nil up.others
70 assert_nil up.others
66 assert up.save
71 assert up.save
67 assert_kind_of Hash, up.others
72 assert_kind_of Hash, up.others
68 end
73 end
69
74
70 def test_others_should_be_blank_after_initialization
71 pref = User.new.pref
72 assert_equal({}, pref.others)
73 end
74
75 def test_reading_value_from_nil_others_hash
75 def test_reading_value_from_nil_others_hash
76 up = UserPreference.new(:user => User.new)
76 up = UserPreference.new(:user => User.new)
77 up.others = nil
77 up.others = nil
78 assert_nil up.others
78 assert_nil up.others
79 assert_nil up[:foo]
79 assert_nil up[:foo]
80 end
80 end
81
81
82 def test_writing_value_to_nil_others_hash
82 def test_writing_value_to_nil_others_hash
83 up = UserPreference.new(:user => User.new)
83 up = UserPreference.new(:user => User.new)
84 up.others = nil
84 up.others = nil
85 assert_nil up.others
85 assert_nil up.others
86 up[:foo] = 'bar'
86 up[:foo] = 'bar'
87 assert_equal 'bar', up[:foo]
87 assert_equal 'bar', up[:foo]
88 end
88 end
89 end
89 end
General Comments 0
You need to be logged in to leave comments. Login now