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