##// END OF EJS Templates
Use assert_nil instead of assert_equal....
Jean-Philippe Lang -
r15678:bf5dade8df89
parent child
Show More
@@ -1,235 +1,235
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ProjectEnumerationsControllerTest < Redmine::ControllerTest
20 class ProjectEnumerationsControllerTest < Redmine::ControllerTest
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users, :issue_categories,
22 :enumerations, :users, :issue_categories,
23 :projects_trackers,
23 :projects_trackers,
24 :roles,
24 :roles,
25 :member_roles,
25 :member_roles,
26 :members,
26 :members,
27 :enabled_modules,
27 :enabled_modules,
28 :custom_fields, :custom_fields_projects,
28 :custom_fields, :custom_fields_projects,
29 :custom_fields_trackers, :custom_values,
29 :custom_fields_trackers, :custom_values,
30 :time_entries
30 :time_entries
31
31
32 self.use_transactional_fixtures = false
32 self.use_transactional_fixtures = false
33
33
34 def setup
34 def setup
35 @request.session[:user_id] = nil
35 @request.session[:user_id] = nil
36 Setting.default_language = 'en'
36 Setting.default_language = 'en'
37 end
37 end
38
38
39 def test_update_to_override_system_activities
39 def test_update_to_override_system_activities
40 @request.session[:user_id] = 2 # manager
40 @request.session[:user_id] = 2 # manager
41 billable_field = TimeEntryActivityCustomField.find_by_name("Billable")
41 billable_field = TimeEntryActivityCustomField.find_by_name("Billable")
42
42
43 put :update, :project_id => 1, :enumerations => {
43 put :update, :project_id => 1, :enumerations => {
44 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate
44 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate
45 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value
45 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value
46 "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value
46 "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value
47 "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes
47 "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes
48 }
48 }
49
49
50 assert_response :redirect
50 assert_response :redirect
51 assert_redirected_to '/projects/ecookbook/settings/activities'
51 assert_redirected_to '/projects/ecookbook/settings/activities'
52
52
53 # Created project specific activities...
53 # Created project specific activities...
54 project = Project.find('ecookbook')
54 project = Project.find('ecookbook')
55
55
56 # ... Design
56 # ... Design
57 design = project.time_entry_activities.find_by_name("Design")
57 design = project.time_entry_activities.find_by_name("Design")
58 assert design, "Project activity not found"
58 assert design, "Project activity not found"
59
59
60 assert_equal 9, design.parent_id # Relate to the system activity
60 assert_equal 9, design.parent_id # Relate to the system activity
61 assert_not_equal design.parent.id, design.id # Different records
61 assert_not_equal design.parent.id, design.id # Different records
62 assert_equal design.parent.name, design.name # Same name
62 assert_equal design.parent.name, design.name # Same name
63 assert !design.active?
63 assert !design.active?
64
64
65 # ... Development
65 # ... Development
66 development = project.time_entry_activities.find_by_name("Development")
66 development = project.time_entry_activities.find_by_name("Development")
67 assert development, "Project activity not found"
67 assert development, "Project activity not found"
68
68
69 assert_equal 10, development.parent_id # Relate to the system activity
69 assert_equal 10, development.parent_id # Relate to the system activity
70 assert_not_equal development.parent.id, development.id # Different records
70 assert_not_equal development.parent.id, development.id # Different records
71 assert_equal development.parent.name, development.name # Same name
71 assert_equal development.parent.name, development.name # Same name
72 assert development.active?
72 assert development.active?
73 assert_equal "0", development.custom_value_for(billable_field).value
73 assert_equal "0", development.custom_value_for(billable_field).value
74
74
75 # ... Inactive Activity
75 # ... Inactive Activity
76 previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity")
76 previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity")
77 assert previously_inactive, "Project activity not found"
77 assert previously_inactive, "Project activity not found"
78
78
79 assert_equal 14, previously_inactive.parent_id # Relate to the system activity
79 assert_equal 14, previously_inactive.parent_id # Relate to the system activity
80 assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records
80 assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records
81 assert_equal previously_inactive.parent.name, previously_inactive.name # Same name
81 assert_equal previously_inactive.parent.name, previously_inactive.name # Same name
82 assert previously_inactive.active?
82 assert previously_inactive.active?
83 assert_equal "1", previously_inactive.custom_value_for(billable_field).value
83 assert_equal "1", previously_inactive.custom_value_for(billable_field).value
84
84
85 # ... QA
85 # ... QA
86 assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified"
86 assert_nil project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified"
87 end
87 end
88
88
89 def test_update_will_update_project_specific_activities
89 def test_update_will_update_project_specific_activities
90 @request.session[:user_id] = 2 # manager
90 @request.session[:user_id] = 2 # manager
91
91
92 project_activity = TimeEntryActivity.new({
92 project_activity = TimeEntryActivity.new({
93 :name => 'Project Specific',
93 :name => 'Project Specific',
94 :parent => TimeEntryActivity.first,
94 :parent => TimeEntryActivity.first,
95 :project => Project.find(1),
95 :project => Project.find(1),
96 :active => true
96 :active => true
97 })
97 })
98 assert project_activity.save
98 assert project_activity.save
99 project_activity_two = TimeEntryActivity.new({
99 project_activity_two = TimeEntryActivity.new({
100 :name => 'Project Specific Two',
100 :name => 'Project Specific Two',
101 :parent => TimeEntryActivity.last,
101 :parent => TimeEntryActivity.last,
102 :project => Project.find(1),
102 :project => Project.find(1),
103 :active => true
103 :active => true
104 })
104 })
105 assert project_activity_two.save
105 assert project_activity_two.save
106
106
107
107
108 put :update, :project_id => 1, :enumerations => {
108 put :update, :project_id => 1, :enumerations => {
109 project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate
109 project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate
110 project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate
110 project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate
111 }
111 }
112
112
113 assert_response :redirect
113 assert_response :redirect
114 assert_redirected_to '/projects/ecookbook/settings/activities'
114 assert_redirected_to '/projects/ecookbook/settings/activities'
115
115
116 # Created project specific activities...
116 # Created project specific activities...
117 project = Project.find('ecookbook')
117 project = Project.find('ecookbook')
118 assert_equal 2, project.time_entry_activities.count
118 assert_equal 2, project.time_entry_activities.count
119
119
120 activity_one = project.time_entry_activities.find_by_name(project_activity.name)
120 activity_one = project.time_entry_activities.find_by_name(project_activity.name)
121 assert activity_one, "Project activity not found"
121 assert activity_one, "Project activity not found"
122 assert_equal project_activity.id, activity_one.id
122 assert_equal project_activity.id, activity_one.id
123 assert !activity_one.active?
123 assert !activity_one.active?
124
124
125 activity_two = project.time_entry_activities.find_by_name(project_activity_two.name)
125 activity_two = project.time_entry_activities.find_by_name(project_activity_two.name)
126 assert activity_two, "Project activity not found"
126 assert activity_two, "Project activity not found"
127 assert_equal project_activity_two.id, activity_two.id
127 assert_equal project_activity_two.id, activity_two.id
128 assert !activity_two.active?
128 assert !activity_two.active?
129 end
129 end
130
130
131 def test_update_when_creating_new_activities_will_convert_existing_data
131 def test_update_when_creating_new_activities_will_convert_existing_data
132 assert_equal 3, TimeEntry.where(:activity_id => 9, :project_id => 1).count
132 assert_equal 3, TimeEntry.where(:activity_id => 9, :project_id => 1).count
133
133
134 @request.session[:user_id] = 2 # manager
134 @request.session[:user_id] = 2 # manager
135 put :update, :project_id => 1, :enumerations => {
135 put :update, :project_id => 1, :enumerations => {
136 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate
136 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate
137 }
137 }
138 assert_response :redirect
138 assert_response :redirect
139
139
140 # No more TimeEntries using the system activity
140 # No more TimeEntries using the system activity
141 assert_equal 0, TimeEntry.where(:activity_id => 9, :project_id => 1).count,
141 assert_equal 0, TimeEntry.where(:activity_id => 9, :project_id => 1).count,
142 "Time Entries still assigned to system activities"
142 "Time Entries still assigned to system activities"
143 # All TimeEntries using project activity
143 # All TimeEntries using project activity
144 project_specific_activity = TimeEntryActivity.find_by_parent_id_and_project_id(9, 1)
144 project_specific_activity = TimeEntryActivity.find_by_parent_id_and_project_id(9, 1)
145 assert_equal 3, TimeEntry.where(:activity_id => project_specific_activity.id,
145 assert_equal 3, TimeEntry.where(:activity_id => project_specific_activity.id,
146 :project_id => 1).count
146 :project_id => 1).count
147 "No Time Entries assigned to the project activity"
147 "No Time Entries assigned to the project activity"
148 end
148 end
149
149
150 def test_update_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised
150 def test_update_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised
151 # TODO: Need to cause an exception on create but these tests
151 # TODO: Need to cause an exception on create but these tests
152 # aren't setup for mocking. Just create a record now so the
152 # aren't setup for mocking. Just create a record now so the
153 # second one is a dupicate
153 # second one is a dupicate
154 parent = TimeEntryActivity.find(9)
154 parent = TimeEntryActivity.find(9)
155 TimeEntryActivity.create!({:name => parent.name, :project_id => 1,
155 TimeEntryActivity.create!({:name => parent.name, :project_id => 1,
156 :position => parent.position, :active => true, :parent_id => 9})
156 :position => parent.position, :active => true, :parent_id => 9})
157 TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1),
157 TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1),
158 :issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'})
158 :issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'})
159 assert_equal 3, TimeEntry.where(:activity_id => 9, :project_id => 1).count
159 assert_equal 3, TimeEntry.where(:activity_id => 9, :project_id => 1).count
160 assert_equal 1, TimeEntry.where(:activity_id => 10, :project_id => 1).count
160 assert_equal 1, TimeEntry.where(:activity_id => 10, :project_id => 1).count
161
161
162 @request.session[:user_id] = 2 # manager
162 @request.session[:user_id] = 2 # manager
163 put :update, :project_id => 1, :enumerations => {
163 put :update, :project_id => 1, :enumerations => {
164 # Design
164 # Design
165 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"},
165 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"},
166 # Development, Change custom value
166 # Development, Change custom value
167 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}
167 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}
168 }
168 }
169 assert_response :redirect
169 assert_response :redirect
170
170
171 # TimeEntries shouldn't have been reassigned on the failed record
171 # TimeEntries shouldn't have been reassigned on the failed record
172 assert_equal 3, TimeEntry.where(:activity_id => 9,
172 assert_equal 3, TimeEntry.where(:activity_id => 9,
173 :project_id => 1).count
173 :project_id => 1).count
174 "Time Entries are not assigned to system activities"
174 "Time Entries are not assigned to system activities"
175 # TimeEntries shouldn't have been reassigned on the saved record either
175 # TimeEntries shouldn't have been reassigned on the saved record either
176 assert_equal 1, TimeEntry.where(:activity_id => 10,
176 assert_equal 1, TimeEntry.where(:activity_id => 10,
177 :project_id => 1).count
177 :project_id => 1).count
178 "Time Entries are not assigned to system activities"
178 "Time Entries are not assigned to system activities"
179 end
179 end
180
180
181 def test_destroy
181 def test_destroy
182 @request.session[:user_id] = 2 # manager
182 @request.session[:user_id] = 2 # manager
183 project_activity = TimeEntryActivity.new({
183 project_activity = TimeEntryActivity.new({
184 :name => 'Project Specific',
184 :name => 'Project Specific',
185 :parent => TimeEntryActivity.first,
185 :parent => TimeEntryActivity.first,
186 :project => Project.find(1),
186 :project => Project.find(1),
187 :active => true
187 :active => true
188 })
188 })
189 assert project_activity.save
189 assert project_activity.save
190 project_activity_two = TimeEntryActivity.new({
190 project_activity_two = TimeEntryActivity.new({
191 :name => 'Project Specific Two',
191 :name => 'Project Specific Two',
192 :parent => TimeEntryActivity.last,
192 :parent => TimeEntryActivity.last,
193 :project => Project.find(1),
193 :project => Project.find(1),
194 :active => true
194 :active => true
195 })
195 })
196 assert project_activity_two.save
196 assert project_activity_two.save
197
197
198 delete :destroy, :project_id => 1
198 delete :destroy, :project_id => 1
199 assert_response :redirect
199 assert_response :redirect
200 assert_redirected_to '/projects/ecookbook/settings/activities'
200 assert_redirected_to '/projects/ecookbook/settings/activities'
201
201
202 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
202 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
203 assert_nil TimeEntryActivity.find_by_id(project_activity_two.id)
203 assert_nil TimeEntryActivity.find_by_id(project_activity_two.id)
204 end
204 end
205
205
206 def test_destroy_should_reassign_time_entries_back_to_the_system_activity
206 def test_destroy_should_reassign_time_entries_back_to_the_system_activity
207 @request.session[:user_id] = 2 # manager
207 @request.session[:user_id] = 2 # manager
208 project_activity = TimeEntryActivity.new({
208 project_activity = TimeEntryActivity.new({
209 :name => 'Project Specific Design',
209 :name => 'Project Specific Design',
210 :parent => TimeEntryActivity.find(9),
210 :parent => TimeEntryActivity.find(9),
211 :project => Project.find(1),
211 :project => Project.find(1),
212 :active => true
212 :active => true
213 })
213 })
214 assert project_activity.save
214 assert project_activity.save
215 assert TimeEntry.where(["project_id = ? AND activity_id = ?", 1, 9]).
215 assert TimeEntry.where(["project_id = ? AND activity_id = ?", 1, 9]).
216 update_all("activity_id = '#{project_activity.id}'")
216 update_all("activity_id = '#{project_activity.id}'")
217 assert_equal 3, TimeEntry.where(:activity_id => project_activity.id,
217 assert_equal 3, TimeEntry.where(:activity_id => project_activity.id,
218 :project_id => 1).count
218 :project_id => 1).count
219 delete :destroy, :project_id => 1
219 delete :destroy, :project_id => 1
220 assert_response :redirect
220 assert_response :redirect
221 assert_redirected_to '/projects/ecookbook/settings/activities'
221 assert_redirected_to '/projects/ecookbook/settings/activities'
222
222
223 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
223 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
224 assert_equal 0, TimeEntry.where(
224 assert_equal 0, TimeEntry.where(
225 :activity_id => project_activity.id,
225 :activity_id => project_activity.id,
226 :project_id => 1
226 :project_id => 1
227 ).count,
227 ).count,
228 "TimeEntries still assigned to project specific activity"
228 "TimeEntries still assigned to project specific activity"
229 assert_equal 3, TimeEntry.where(
229 assert_equal 3, TimeEntry.where(
230 :activity_id => 9,
230 :activity_id => 9,
231 :project_id => 1
231 :project_id => 1
232 ).count,
232 ).count,
233 "TimeEntries still assigned to project specific activity"
233 "TimeEntries still assigned to project specific activity"
234 end
234 end
235 end
235 end
@@ -1,635 +1,635
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UsersControllerTest < Redmine::ControllerTest
20 class UsersControllerTest < Redmine::ControllerTest
21 include Redmine::I18n
21 include Redmine::I18n
22
22
23 fixtures :users, :email_addresses, :projects, :members, :member_roles, :roles,
23 fixtures :users, :email_addresses, :projects, :members, :member_roles, :roles,
24 :custom_fields, :custom_values, :groups_users,
24 :custom_fields, :custom_values, :groups_users,
25 :auth_sources,
25 :auth_sources,
26 :enabled_modules,
26 :enabled_modules,
27 :issues, :issue_statuses,
27 :issues, :issue_statuses,
28 :trackers
28 :trackers
29
29
30 def setup
30 def setup
31 User.current = nil
31 User.current = nil
32 @request.session[:user_id] = 1 # admin
32 @request.session[:user_id] = 1 # admin
33 end
33 end
34
34
35 def test_index
35 def test_index
36 get :index
36 get :index
37 assert_response :success
37 assert_response :success
38 assert_select 'table.users'
38 assert_select 'table.users'
39 assert_select 'tr.user.active'
39 assert_select 'tr.user.active'
40 assert_select 'tr.user.locked', 0
40 assert_select 'tr.user.locked', 0
41 end
41 end
42
42
43 def test_index_with_status_filter
43 def test_index_with_status_filter
44 get :index, :params => {:status => 3}
44 get :index, :params => {:status => 3}
45 assert_response :success
45 assert_response :success
46 assert_select 'tr.user.active', 0
46 assert_select 'tr.user.active', 0
47 assert_select 'tr.user.locked'
47 assert_select 'tr.user.locked'
48 end
48 end
49
49
50 def test_index_with_name_filter
50 def test_index_with_name_filter
51 get :index, :params => {:name => 'john'}
51 get :index, :params => {:name => 'john'}
52 assert_response :success
52 assert_response :success
53 assert_select 'tr.user td.username', :text => 'jsmith'
53 assert_select 'tr.user td.username', :text => 'jsmith'
54 assert_select 'tr.user', 1
54 assert_select 'tr.user', 1
55 end
55 end
56
56
57 def test_index_with_group_filter
57 def test_index_with_group_filter
58 get :index, :params => {:group_id => '10'}
58 get :index, :params => {:group_id => '10'}
59 assert_response :success
59 assert_response :success
60
60
61 assert_select 'tr.user', Group.find(10).users.count
61 assert_select 'tr.user', Group.find(10).users.count
62 assert_select 'select[name=group_id]' do
62 assert_select 'select[name=group_id]' do
63 assert_select 'option[value="10"][selected=selected]'
63 assert_select 'option[value="10"][selected=selected]'
64 end
64 end
65 end
65 end
66
66
67 def test_show
67 def test_show
68 @request.session[:user_id] = nil
68 @request.session[:user_id] = nil
69 get :show, :params => {:id => 2}
69 get :show, :params => {:id => 2}
70 assert_response :success
70 assert_response :success
71 assert_select 'h2', :text => /John Smith/
71 assert_select 'h2', :text => /John Smith/
72 end
72 end
73
73
74 def test_show_should_display_visible_custom_fields
74 def test_show_should_display_visible_custom_fields
75 @request.session[:user_id] = nil
75 @request.session[:user_id] = nil
76 UserCustomField.find_by_name('Phone number').update_attribute :visible, true
76 UserCustomField.find_by_name('Phone number').update_attribute :visible, true
77 get :show, :params => {:id => 2}
77 get :show, :params => {:id => 2}
78 assert_response :success
78 assert_response :success
79
79
80 assert_select 'li', :text => /Phone number/
80 assert_select 'li', :text => /Phone number/
81 end
81 end
82
82
83 def test_show_should_not_display_hidden_custom_fields
83 def test_show_should_not_display_hidden_custom_fields
84 @request.session[:user_id] = nil
84 @request.session[:user_id] = nil
85 UserCustomField.find_by_name('Phone number').update_attribute :visible, false
85 UserCustomField.find_by_name('Phone number').update_attribute :visible, false
86 get :show, :params => {:id => 2}
86 get :show, :params => {:id => 2}
87 assert_response :success
87 assert_response :success
88
88
89 assert_select 'li', :text => /Phone number/, :count => 0
89 assert_select 'li', :text => /Phone number/, :count => 0
90 end
90 end
91
91
92 def test_show_should_not_fail_when_custom_values_are_nil
92 def test_show_should_not_fail_when_custom_values_are_nil
93 user = User.find(2)
93 user = User.find(2)
94
94
95 # Create a custom field to illustrate the issue
95 # Create a custom field to illustrate the issue
96 custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
96 custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
97 custom_value = user.custom_values.build(:custom_field => custom_field).save!
97 custom_value = user.custom_values.build(:custom_field => custom_field).save!
98
98
99 get :show, :params => {:id => 2}
99 get :show, :params => {:id => 2}
100 assert_response :success
100 assert_response :success
101 end
101 end
102
102
103 def test_show_inactive
103 def test_show_inactive
104 @request.session[:user_id] = nil
104 @request.session[:user_id] = nil
105 get :show, :params => {:id => 5}
105 get :show, :params => {:id => 5}
106 assert_response 404
106 assert_response 404
107 end
107 end
108
108
109 def test_show_inactive_by_admin
109 def test_show_inactive_by_admin
110 @request.session[:user_id] = 1
110 @request.session[:user_id] = 1
111 get :show, :params => {:id => 5}
111 get :show, :params => {:id => 5}
112 assert_response 200
112 assert_response 200
113 assert_select 'h2', :text => /Dave2 Lopper2/
113 assert_select 'h2', :text => /Dave2 Lopper2/
114 end
114 end
115
115
116 def test_show_user_who_is_not_visible_should_return_404
116 def test_show_user_who_is_not_visible_should_return_404
117 Role.anonymous.update! :users_visibility => 'members_of_visible_projects'
117 Role.anonymous.update! :users_visibility => 'members_of_visible_projects'
118 user = User.generate!
118 user = User.generate!
119
119
120 @request.session[:user_id] = nil
120 @request.session[:user_id] = nil
121 get :show, :params => {:id => user.id}
121 get :show, :params => {:id => user.id}
122 assert_response 404
122 assert_response 404
123 end
123 end
124
124
125 def test_show_displays_memberships_based_on_project_visibility
125 def test_show_displays_memberships_based_on_project_visibility
126 @request.session[:user_id] = 1
126 @request.session[:user_id] = 1
127 get :show, :params => {:id => 2}
127 get :show, :params => {:id => 2}
128 assert_response :success
128 assert_response :success
129
129
130 # membership of private project admin can see
130 # membership of private project admin can see
131 assert_select 'li a', :text => "OnlineStore"
131 assert_select 'li a', :text => "OnlineStore"
132 end
132 end
133
133
134 def test_show_current_should_require_authentication
134 def test_show_current_should_require_authentication
135 @request.session[:user_id] = nil
135 @request.session[:user_id] = nil
136 get :show, :params => {:id => 'current'}
136 get :show, :params => {:id => 'current'}
137 assert_response 302
137 assert_response 302
138 end
138 end
139
139
140 def test_show_current
140 def test_show_current
141 @request.session[:user_id] = 2
141 @request.session[:user_id] = 2
142 get :show, :params => {:id => 'current'}
142 get :show, :params => {:id => 'current'}
143 assert_response :success
143 assert_response :success
144 assert_select 'h2', :text => /John Smith/
144 assert_select 'h2', :text => /John Smith/
145 end
145 end
146
146
147 def test_new
147 def test_new
148 get :new
148 get :new
149 assert_response :success
149 assert_response :success
150 assert_select 'input[name=?]', 'user[login]'
150 assert_select 'input[name=?]', 'user[login]'
151 end
151 end
152
152
153 def test_create
153 def test_create
154 Setting.bcc_recipients = '1'
154 Setting.bcc_recipients = '1'
155
155
156 assert_difference 'User.count' do
156 assert_difference 'User.count' do
157 assert_difference 'ActionMailer::Base.deliveries.size' do
157 assert_difference 'ActionMailer::Base.deliveries.size' do
158 post :create, :params => {
158 post :create, :params => {
159 :user => {
159 :user => {
160 :firstname => 'John',
160 :firstname => 'John',
161 :lastname => 'Doe',
161 :lastname => 'Doe',
162 :login => 'jdoe',
162 :login => 'jdoe',
163 :password => 'secret123',
163 :password => 'secret123',
164 :password_confirmation => 'secret123',
164 :password_confirmation => 'secret123',
165 :mail => 'jdoe@gmail.com',
165 :mail => 'jdoe@gmail.com',
166 :mail_notification => 'none'
166 :mail_notification => 'none'
167 },
167 },
168 :send_information => '1'
168 :send_information => '1'
169 }
169 }
170 end
170 end
171 end
171 end
172
172
173 user = User.order('id DESC').first
173 user = User.order('id DESC').first
174 assert_redirected_to :controller => 'users', :action => 'edit', :id => user.id
174 assert_redirected_to :controller => 'users', :action => 'edit', :id => user.id
175
175
176 assert_equal 'John', user.firstname
176 assert_equal 'John', user.firstname
177 assert_equal 'Doe', user.lastname
177 assert_equal 'Doe', user.lastname
178 assert_equal 'jdoe', user.login
178 assert_equal 'jdoe', user.login
179 assert_equal 'jdoe@gmail.com', user.mail
179 assert_equal 'jdoe@gmail.com', user.mail
180 assert_equal 'none', user.mail_notification
180 assert_equal 'none', user.mail_notification
181 assert user.check_password?('secret123')
181 assert user.check_password?('secret123')
182
182
183 mail = ActionMailer::Base.deliveries.last
183 mail = ActionMailer::Base.deliveries.last
184 assert_not_nil mail
184 assert_not_nil mail
185 assert_equal [user.mail], mail.bcc
185 assert_equal [user.mail], mail.bcc
186 assert_mail_body_match 'secret', mail
186 assert_mail_body_match 'secret', mail
187 end
187 end
188
188
189 def test_create_with_preferences
189 def test_create_with_preferences
190 assert_difference 'User.count' do
190 assert_difference 'User.count' do
191 post :create, :params => {
191 post :create, :params => {
192 :user => {
192 :user => {
193 :firstname => 'John',
193 :firstname => 'John',
194 :lastname => 'Doe',
194 :lastname => 'Doe',
195 :login => 'jdoe',
195 :login => 'jdoe',
196 :password => 'secret123',
196 :password => 'secret123',
197 :password_confirmation => 'secret123',
197 :password_confirmation => 'secret123',
198 :mail => 'jdoe@gmail.com',
198 :mail => 'jdoe@gmail.com',
199 :mail_notification => 'none'
199 :mail_notification => 'none'
200 },
200 },
201 :pref => {
201 :pref => {
202 'hide_mail' => '1',
202 'hide_mail' => '1',
203 'time_zone' => 'Paris',
203 'time_zone' => 'Paris',
204 'comments_sorting' => 'desc',
204 'comments_sorting' => 'desc',
205 'warn_on_leaving_unsaved' => '0',
205 'warn_on_leaving_unsaved' => '0',
206 'textarea_font' => 'proportional'
206 'textarea_font' => 'proportional'
207 }
207 }
208 }
208 }
209 end
209 end
210 user = User.order('id DESC').first
210 user = User.order('id DESC').first
211 assert_equal 'jdoe', user.login
211 assert_equal 'jdoe', user.login
212 assert_equal true, user.pref.hide_mail
212 assert_equal true, user.pref.hide_mail
213 assert_equal 'Paris', user.pref.time_zone
213 assert_equal 'Paris', user.pref.time_zone
214 assert_equal 'desc', user.pref[:comments_sorting]
214 assert_equal 'desc', user.pref[:comments_sorting]
215 assert_equal '0', user.pref[:warn_on_leaving_unsaved]
215 assert_equal '0', user.pref[:warn_on_leaving_unsaved]
216 assert_equal 'proportional', user.pref[:textarea_font]
216 assert_equal 'proportional', user.pref[:textarea_font]
217 end
217 end
218
218
219 def test_create_with_generate_password_should_email_the_password
219 def test_create_with_generate_password_should_email_the_password
220 assert_difference 'User.count' do
220 assert_difference 'User.count' do
221 post :create, :params => {
221 post :create, :params => {
222 :user => {
222 :user => {
223 :login => 'randompass',
223 :login => 'randompass',
224 :firstname => 'Random',
224 :firstname => 'Random',
225 :lastname => 'Pass',
225 :lastname => 'Pass',
226 :mail => 'randompass@example.net',
226 :mail => 'randompass@example.net',
227 :language => 'en',
227 :language => 'en',
228 :generate_password => '1',
228 :generate_password => '1',
229 :password => '',
229 :password => '',
230 :password_confirmation => ''
230 :password_confirmation => ''
231 },
231 },
232 :send_information => 1
232 :send_information => 1
233 }
233 }
234 end
234 end
235 user = User.order('id DESC').first
235 user = User.order('id DESC').first
236 assert_equal 'randompass', user.login
236 assert_equal 'randompass', user.login
237
237
238 mail = ActionMailer::Base.deliveries.last
238 mail = ActionMailer::Base.deliveries.last
239 assert_not_nil mail
239 assert_not_nil mail
240 m = mail_body(mail).match(/Password: ([a-zA-Z0-9]+)/)
240 m = mail_body(mail).match(/Password: ([a-zA-Z0-9]+)/)
241 assert m
241 assert m
242 password = m[1]
242 password = m[1]
243 assert user.check_password?(password)
243 assert user.check_password?(password)
244 end
244 end
245
245
246 def test_create_and_continue
246 def test_create_and_continue
247 post :create, :params => {
247 post :create, :params => {
248 :user => {
248 :user => {
249 :login => 'randompass',
249 :login => 'randompass',
250 :firstname => 'Random',
250 :firstname => 'Random',
251 :lastname => 'Pass',
251 :lastname => 'Pass',
252 :mail => 'randompass@example.net',
252 :mail => 'randompass@example.net',
253 :generate_password => '1'
253 :generate_password => '1'
254 },
254 },
255 :continue => '1'
255 :continue => '1'
256 }
256 }
257 assert_redirected_to '/users/new?user%5Bgenerate_password%5D=1'
257 assert_redirected_to '/users/new?user%5Bgenerate_password%5D=1'
258 end
258 end
259
259
260 def test_create_with_failure
260 def test_create_with_failure
261 assert_no_difference 'User.count' do
261 assert_no_difference 'User.count' do
262 post :create, :params => {:user => {}}
262 post :create, :params => {:user => {}}
263 end
263 end
264 assert_response :success
264 assert_response :success
265 assert_select_error /Email cannot be blank/
265 assert_select_error /Email cannot be blank/
266 end
266 end
267
267
268 def test_create_with_failure_sould_preserve_preference
268 def test_create_with_failure_sould_preserve_preference
269 assert_no_difference 'User.count' do
269 assert_no_difference 'User.count' do
270 post :create, :params => {
270 post :create, :params => {
271 :user => {},
271 :user => {},
272 :pref => {
272 :pref => {
273 'no_self_notified' => '1',
273 'no_self_notified' => '1',
274 'hide_mail' => '1',
274 'hide_mail' => '1',
275 'time_zone' => 'Paris',
275 'time_zone' => 'Paris',
276 'comments_sorting' => 'desc',
276 'comments_sorting' => 'desc',
277 'warn_on_leaving_unsaved' => '0'
277 'warn_on_leaving_unsaved' => '0'
278 }
278 }
279 }
279 }
280 end
280 end
281 assert_response :success
281 assert_response :success
282
282
283 assert_select 'select#pref_time_zone option[selected=selected]', :text => /Paris/
283 assert_select 'select#pref_time_zone option[selected=selected]', :text => /Paris/
284 assert_select 'input#pref_no_self_notified[value="1"][checked=checked]'
284 assert_select 'input#pref_no_self_notified[value="1"][checked=checked]'
285 end
285 end
286
286
287 def test_create_admin_should_send_security_notification
287 def test_create_admin_should_send_security_notification
288 ActionMailer::Base.deliveries.clear
288 ActionMailer::Base.deliveries.clear
289 post :create, :params => {
289 post :create, :params => {
290 :user => {
290 :user => {
291 :firstname => 'Edgar',
291 :firstname => 'Edgar',
292 :lastname => 'Schmoe',
292 :lastname => 'Schmoe',
293 :login => 'eschmoe',
293 :login => 'eschmoe',
294 :password => 'secret123',
294 :password => 'secret123',
295 :password_confirmation => 'secret123',
295 :password_confirmation => 'secret123',
296 :mail => 'eschmoe@example.foo',
296 :mail => 'eschmoe@example.foo',
297 :admin => '1'
297 :admin => '1'
298 }
298 }
299 }
299 }
300
300
301 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
301 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
302 assert_mail_body_match '0.0.0.0', mail
302 assert_mail_body_match '0.0.0.0', mail
303 assert_mail_body_match I18n.t(:mail_body_security_notification_add, field: I18n.t(:field_admin), value: 'eschmoe'), mail
303 assert_mail_body_match I18n.t(:mail_body_security_notification_add, field: I18n.t(:field_admin), value: 'eschmoe'), mail
304 assert_select_email do
304 assert_select_email do
305 assert_select 'a[href^=?]', 'http://localhost:3000/users', :text => 'Users'
305 assert_select 'a[href^=?]', 'http://localhost:3000/users', :text => 'Users'
306 end
306 end
307
307
308 # All admins should receive this
308 # All admins should receive this
309 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
309 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
310 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
310 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
311 end
311 end
312 end
312 end
313
313
314 def test_create_non_admin_should_not_send_security_notification
314 def test_create_non_admin_should_not_send_security_notification
315 ActionMailer::Base.deliveries.clear
315 ActionMailer::Base.deliveries.clear
316 post :create, :params => {
316 post :create, :params => {
317 :user => {
317 :user => {
318 :firstname => 'Edgar',
318 :firstname => 'Edgar',
319 :lastname => 'Schmoe',
319 :lastname => 'Schmoe',
320 :login => 'eschmoe',
320 :login => 'eschmoe',
321 :password => 'secret123',
321 :password => 'secret123',
322 :password_confirmation => 'secret123',
322 :password_confirmation => 'secret123',
323 :mail => 'eschmoe@example.foo',
323 :mail => 'eschmoe@example.foo',
324 :admin => '0'
324 :admin => '0'
325 }
325 }
326 }
326 }
327 assert_nil ActionMailer::Base.deliveries.last
327 assert_nil ActionMailer::Base.deliveries.last
328 end
328 end
329
329
330
330
331 def test_edit
331 def test_edit
332 get :edit, :params => {:id => 2}
332 get :edit, :params => {:id => 2}
333 assert_response :success
333 assert_response :success
334 assert_select 'input[name=?][value=?]', 'user[login]', 'jsmith'
334 assert_select 'input[name=?][value=?]', 'user[login]', 'jsmith'
335 end
335 end
336
336
337 def test_edit_registered_user
337 def test_edit_registered_user
338 assert User.find(2).register!
338 assert User.find(2).register!
339
339
340 get :edit, :params => {:id => 2}
340 get :edit, :params => {:id => 2}
341 assert_response :success
341 assert_response :success
342 assert_select 'a', :text => 'Activate'
342 assert_select 'a', :text => 'Activate'
343 end
343 end
344
344
345 def test_update
345 def test_update
346 ActionMailer::Base.deliveries.clear
346 ActionMailer::Base.deliveries.clear
347 put :update, :params => {
347 put :update, :params => {
348 :id => 2,
348 :id => 2,
349 :user => {:firstname => 'Changed', :mail_notification => 'only_assigned'},
349 :user => {:firstname => 'Changed', :mail_notification => 'only_assigned'},
350 :pref => {:hide_mail => '1', :comments_sorting => 'desc'}
350 :pref => {:hide_mail => '1', :comments_sorting => 'desc'}
351 }
351 }
352 user = User.find(2)
352 user = User.find(2)
353 assert_equal 'Changed', user.firstname
353 assert_equal 'Changed', user.firstname
354 assert_equal 'only_assigned', user.mail_notification
354 assert_equal 'only_assigned', user.mail_notification
355 assert_equal true, user.pref[:hide_mail]
355 assert_equal true, user.pref[:hide_mail]
356 assert_equal 'desc', user.pref[:comments_sorting]
356 assert_equal 'desc', user.pref[:comments_sorting]
357 assert ActionMailer::Base.deliveries.empty?
357 assert ActionMailer::Base.deliveries.empty?
358 end
358 end
359
359
360 def test_update_with_failure
360 def test_update_with_failure
361 assert_no_difference 'User.count' do
361 assert_no_difference 'User.count' do
362 put :update, :params => {
362 put :update, :params => {
363 :id => 2,
363 :id => 2,
364 :user => {:firstname => ''}
364 :user => {:firstname => ''}
365 }
365 }
366 end
366 end
367 assert_response :success
367 assert_response :success
368 assert_select_error /First name cannot be blank/
368 assert_select_error /First name cannot be blank/
369 end
369 end
370
370
371 def test_update_with_group_ids_should_assign_groups
371 def test_update_with_group_ids_should_assign_groups
372 put :update, :params => {
372 put :update, :params => {
373 :id => 2,
373 :id => 2,
374 :user => {:group_ids => ['10']}
374 :user => {:group_ids => ['10']}
375 }
375 }
376 user = User.find(2)
376 user = User.find(2)
377 assert_equal [10], user.group_ids
377 assert_equal [10], user.group_ids
378 end
378 end
379
379
380 def test_update_with_activation_should_send_a_notification
380 def test_update_with_activation_should_send_a_notification
381 u = User.new(:firstname => 'Foo', :lastname => 'Bar', :mail => 'foo.bar@somenet.foo', :language => 'fr')
381 u = User.new(:firstname => 'Foo', :lastname => 'Bar', :mail => 'foo.bar@somenet.foo', :language => 'fr')
382 u.login = 'foo'
382 u.login = 'foo'
383 u.status = User::STATUS_REGISTERED
383 u.status = User::STATUS_REGISTERED
384 u.save!
384 u.save!
385 ActionMailer::Base.deliveries.clear
385 ActionMailer::Base.deliveries.clear
386 Setting.bcc_recipients = '1'
386 Setting.bcc_recipients = '1'
387
387
388 put :update, :params => {
388 put :update, :params => {
389 :id => u.id,
389 :id => u.id,
390 :user => {:status => User::STATUS_ACTIVE}
390 :user => {:status => User::STATUS_ACTIVE}
391 }
391 }
392 assert u.reload.active?
392 assert u.reload.active?
393 mail = ActionMailer::Base.deliveries.last
393 mail = ActionMailer::Base.deliveries.last
394 assert_not_nil mail
394 assert_not_nil mail
395 assert_equal ['foo.bar@somenet.foo'], mail.bcc
395 assert_equal ['foo.bar@somenet.foo'], mail.bcc
396 assert_mail_body_match ll('fr', :notice_account_activated), mail
396 assert_mail_body_match ll('fr', :notice_account_activated), mail
397 end
397 end
398
398
399 def test_update_with_password_change_should_send_a_notification
399 def test_update_with_password_change_should_send_a_notification
400 ActionMailer::Base.deliveries.clear
400 ActionMailer::Base.deliveries.clear
401 Setting.bcc_recipients = '1'
401 Setting.bcc_recipients = '1'
402
402
403 put :update, :params => {
403 put :update, :params => {
404 :id => 2,
404 :id => 2,
405 :user => {:password => 'newpass123', :password_confirmation => 'newpass123'},
405 :user => {:password => 'newpass123', :password_confirmation => 'newpass123'},
406 :send_information => '1'
406 :send_information => '1'
407 }
407 }
408 u = User.find(2)
408 u = User.find(2)
409 assert u.check_password?('newpass123')
409 assert u.check_password?('newpass123')
410
410
411 mail = ActionMailer::Base.deliveries.last
411 mail = ActionMailer::Base.deliveries.last
412 assert_not_nil mail
412 assert_not_nil mail
413 assert_equal [u.mail], mail.bcc
413 assert_equal [u.mail], mail.bcc
414 assert_mail_body_match 'newpass123', mail
414 assert_mail_body_match 'newpass123', mail
415 end
415 end
416
416
417 def test_update_with_generate_password_should_email_the_password
417 def test_update_with_generate_password_should_email_the_password
418 ActionMailer::Base.deliveries.clear
418 ActionMailer::Base.deliveries.clear
419 Setting.bcc_recipients = '1'
419 Setting.bcc_recipients = '1'
420
420
421 put :update, :params => {
421 put :update, :params => {
422 :id => 2,
422 :id => 2,
423 :user => {
423 :user => {
424 :generate_password => '1',
424 :generate_password => '1',
425 :password => '',
425 :password => '',
426 :password_confirmation => ''
426 :password_confirmation => ''
427 },
427 },
428 :send_information => '1'
428 :send_information => '1'
429 }
429 }
430
430
431 mail = ActionMailer::Base.deliveries.last
431 mail = ActionMailer::Base.deliveries.last
432 assert_not_nil mail
432 assert_not_nil mail
433 m = mail_body(mail).match(/Password: ([a-zA-Z0-9]+)/)
433 m = mail_body(mail).match(/Password: ([a-zA-Z0-9]+)/)
434 assert m
434 assert m
435 password = m[1]
435 password = m[1]
436 assert User.find(2).check_password?(password)
436 assert User.find(2).check_password?(password)
437 end
437 end
438
438
439 def test_update_without_generate_password_should_not_change_password
439 def test_update_without_generate_password_should_not_change_password
440 put :update, :params => {
440 put :update, :params => {
441 :id => 2, :user => {
441 :id => 2, :user => {
442 :firstname => 'changed',
442 :firstname => 'changed',
443 :generate_password => '0',
443 :generate_password => '0',
444 :password => '',
444 :password => '',
445 :password_confirmation => ''
445 :password_confirmation => ''
446 },
446 },
447 :send_information => '1'
447 :send_information => '1'
448 }
448 }
449
449
450 user = User.find(2)
450 user = User.find(2)
451 assert_equal 'changed', user.firstname
451 assert_equal 'changed', user.firstname
452 assert user.check_password?('jsmith')
452 assert user.check_password?('jsmith')
453 end
453 end
454
454
455 def test_update_user_switchin_from_auth_source_to_password_authentication
455 def test_update_user_switchin_from_auth_source_to_password_authentication
456 # Configure as auth source
456 # Configure as auth source
457 u = User.find(2)
457 u = User.find(2)
458 u.auth_source = AuthSource.find(1)
458 u.auth_source = AuthSource.find(1)
459 u.save!
459 u.save!
460
460
461 put :update, :params => {
461 put :update, :params => {
462 :id => u.id,
462 :id => u.id,
463 :user => {:auth_source_id => '', :password => 'newpass123', :password_confirmation => 'newpass123'}
463 :user => {:auth_source_id => '', :password => 'newpass123', :password_confirmation => 'newpass123'}
464 }
464 }
465
465
466 assert_equal nil, u.reload.auth_source
466 assert_nil u.reload.auth_source
467 assert u.check_password?('newpass123')
467 assert u.check_password?('newpass123')
468 end
468 end
469
469
470 def test_update_notified_project
470 def test_update_notified_project
471 get :edit, :params => {:id => 2}
471 get :edit, :params => {:id => 2}
472 assert_response :success
472 assert_response :success
473 u = User.find(2)
473 u = User.find(2)
474 assert_equal [1, 2, 5], u.projects.collect{|p| p.id}.sort
474 assert_equal [1, 2, 5], u.projects.collect{|p| p.id}.sort
475 assert_equal [1, 2, 5], u.notified_projects_ids.sort
475 assert_equal [1, 2, 5], u.notified_projects_ids.sort
476 assert_select 'input[name=?][value=?]', 'user[notified_project_ids][]', '1'
476 assert_select 'input[name=?][value=?]', 'user[notified_project_ids][]', '1'
477 assert_equal 'all', u.mail_notification
477 assert_equal 'all', u.mail_notification
478 put :update, :params => {
478 put :update, :params => {
479 :id => 2,
479 :id => 2,
480 :user => {
480 :user => {
481 :mail_notification => 'selected',
481 :mail_notification => 'selected',
482 :notified_project_ids => [1, 2]
482 :notified_project_ids => [1, 2]
483 }
483 }
484 }
484 }
485 u = User.find(2)
485 u = User.find(2)
486 assert_equal 'selected', u.mail_notification
486 assert_equal 'selected', u.mail_notification
487 assert_equal [1, 2], u.notified_projects_ids.sort
487 assert_equal [1, 2], u.notified_projects_ids.sort
488 end
488 end
489
489
490 def test_update_status_should_not_update_attributes
490 def test_update_status_should_not_update_attributes
491 user = User.find(2)
491 user = User.find(2)
492 user.pref[:no_self_notified] = '1'
492 user.pref[:no_self_notified] = '1'
493 user.pref.save
493 user.pref.save
494
494
495 put :update, :params => {
495 put :update, :params => {
496 :id => 2,
496 :id => 2,
497 :user => {:status => 3}
497 :user => {:status => 3}
498 }
498 }
499 assert_response 302
499 assert_response 302
500 user = User.find(2)
500 user = User.find(2)
501 assert_equal 3, user.status
501 assert_equal 3, user.status
502 assert_equal '1', user.pref[:no_self_notified]
502 assert_equal '1', user.pref[:no_self_notified]
503 end
503 end
504
504
505 def test_update_assign_admin_should_send_security_notification
505 def test_update_assign_admin_should_send_security_notification
506 ActionMailer::Base.deliveries.clear
506 ActionMailer::Base.deliveries.clear
507 put :update, :params => {
507 put :update, :params => {
508 :id => 2,
508 :id => 2,
509 :user => {:admin => 1}
509 :user => {:admin => 1}
510 }
510 }
511
511
512 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
512 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
513 assert_mail_body_match I18n.t(:mail_body_security_notification_add, field: I18n.t(:field_admin), value: User.find(2).login), mail
513 assert_mail_body_match I18n.t(:mail_body_security_notification_add, field: I18n.t(:field_admin), value: User.find(2).login), mail
514
514
515 # All admins should receive this
515 # All admins should receive this
516 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
516 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
517 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
517 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
518 end
518 end
519 end
519 end
520
520
521 def test_update_unassign_admin_should_send_security_notification
521 def test_update_unassign_admin_should_send_security_notification
522 user = User.find(2)
522 user = User.find(2)
523 user.admin = true
523 user.admin = true
524 user.save!
524 user.save!
525
525
526 ActionMailer::Base.deliveries.clear
526 ActionMailer::Base.deliveries.clear
527 put :update, :params => {
527 put :update, :params => {
528 :id => user.id,
528 :id => user.id,
529 :user => {:admin => 0}
529 :user => {:admin => 0}
530 }
530 }
531
531
532 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
532 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
533 assert_mail_body_match I18n.t(:mail_body_security_notification_remove, field: I18n.t(:field_admin), value: user.login), mail
533 assert_mail_body_match I18n.t(:mail_body_security_notification_remove, field: I18n.t(:field_admin), value: user.login), mail
534
534
535 # All admins should receive this
535 # All admins should receive this
536 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
536 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
537 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
537 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
538 end
538 end
539 end
539 end
540
540
541 def test_update_lock_admin_should_send_security_notification
541 def test_update_lock_admin_should_send_security_notification
542 user = User.find(2)
542 user = User.find(2)
543 user.admin = true
543 user.admin = true
544 user.save!
544 user.save!
545
545
546 ActionMailer::Base.deliveries.clear
546 ActionMailer::Base.deliveries.clear
547 put :update, :params => {
547 put :update, :params => {
548 :id => 2,
548 :id => 2,
549 :user => {:status => Principal::STATUS_LOCKED}
549 :user => {:status => Principal::STATUS_LOCKED}
550 }
550 }
551
551
552 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
552 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
553 assert_mail_body_match I18n.t(:mail_body_security_notification_remove, field: I18n.t(:field_admin), value: User.find(2).login), mail
553 assert_mail_body_match I18n.t(:mail_body_security_notification_remove, field: I18n.t(:field_admin), value: User.find(2).login), mail
554
554
555 # All admins should receive this
555 # All admins should receive this
556 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
556 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
557 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
557 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
558 end
558 end
559
559
560 # if user is already locked, destroying should not send a second mail
560 # if user is already locked, destroying should not send a second mail
561 # (for active admins see furtherbelow)
561 # (for active admins see furtherbelow)
562 ActionMailer::Base.deliveries.clear
562 ActionMailer::Base.deliveries.clear
563 delete :destroy, :params => {:id => 1}
563 delete :destroy, :params => {:id => 1}
564 assert_nil ActionMailer::Base.deliveries.last
564 assert_nil ActionMailer::Base.deliveries.last
565
565
566 end
566 end
567
567
568 def test_update_unlock_admin_should_send_security_notification
568 def test_update_unlock_admin_should_send_security_notification
569 user = User.find(5) # already locked
569 user = User.find(5) # already locked
570 user.admin = true
570 user.admin = true
571 user.save!
571 user.save!
572 ActionMailer::Base.deliveries.clear
572 ActionMailer::Base.deliveries.clear
573 put :update, :params => {
573 put :update, :params => {
574 :id => user.id,
574 :id => user.id,
575 :user => {:status => Principal::STATUS_ACTIVE}
575 :user => {:status => Principal::STATUS_ACTIVE}
576 }
576 }
577
577
578 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
578 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
579 assert_mail_body_match I18n.t(:mail_body_security_notification_add, field: I18n.t(:field_admin), value: user.login), mail
579 assert_mail_body_match I18n.t(:mail_body_security_notification_add, field: I18n.t(:field_admin), value: user.login), mail
580
580
581 # All admins should receive this
581 # All admins should receive this
582 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
582 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
583 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
583 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
584 end
584 end
585 end
585 end
586
586
587 def test_update_admin_unrelated_property_should_not_send_security_notification
587 def test_update_admin_unrelated_property_should_not_send_security_notification
588 ActionMailer::Base.deliveries.clear
588 ActionMailer::Base.deliveries.clear
589 put :update, :params => {
589 put :update, :params => {
590 :id => 1,
590 :id => 1,
591 :user => {:firstname => 'Jimmy'}
591 :user => {:firstname => 'Jimmy'}
592 }
592 }
593 assert_nil ActionMailer::Base.deliveries.last
593 assert_nil ActionMailer::Base.deliveries.last
594 end
594 end
595
595
596 def test_destroy
596 def test_destroy
597 assert_difference 'User.count', -1 do
597 assert_difference 'User.count', -1 do
598 delete :destroy, :params => {:id => 2}
598 delete :destroy, :params => {:id => 2}
599 end
599 end
600 assert_redirected_to '/users'
600 assert_redirected_to '/users'
601 assert_nil User.find_by_id(2)
601 assert_nil User.find_by_id(2)
602 end
602 end
603
603
604 def test_destroy_should_be_denied_for_non_admin_users
604 def test_destroy_should_be_denied_for_non_admin_users
605 @request.session[:user_id] = 3
605 @request.session[:user_id] = 3
606
606
607 assert_no_difference 'User.count' do
607 assert_no_difference 'User.count' do
608 get :destroy, :params => {:id => 2}
608 get :destroy, :params => {:id => 2}
609 end
609 end
610 assert_response 403
610 assert_response 403
611 end
611 end
612
612
613 def test_destroy_should_redirect_to_back_url_param
613 def test_destroy_should_redirect_to_back_url_param
614 assert_difference 'User.count', -1 do
614 assert_difference 'User.count', -1 do
615 delete :destroy, :params => {:id => 2, :back_url => '/users?name=foo'}
615 delete :destroy, :params => {:id => 2, :back_url => '/users?name=foo'}
616 end
616 end
617 assert_redirected_to '/users?name=foo'
617 assert_redirected_to '/users?name=foo'
618 end
618 end
619
619
620 def test_destroy_active_admin_should_send_security_notification
620 def test_destroy_active_admin_should_send_security_notification
621 user = User.find(2)
621 user = User.find(2)
622 user.admin = true
622 user.admin = true
623 user.save!
623 user.save!
624 ActionMailer::Base.deliveries.clear
624 ActionMailer::Base.deliveries.clear
625 delete :destroy, :params => {:id => user.id}
625 delete :destroy, :params => {:id => user.id}
626
626
627 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
627 assert_not_nil (mail = ActionMailer::Base.deliveries.last)
628 assert_mail_body_match I18n.t(:mail_body_security_notification_remove, field: I18n.t(:field_admin), value: user.login), mail
628 assert_mail_body_match I18n.t(:mail_body_security_notification_remove, field: I18n.t(:field_admin), value: user.login), mail
629
629
630 # All admins should receive this
630 # All admins should receive this
631 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
631 User.where(admin: true, status: Principal::STATUS_ACTIVE).each do |admin|
632 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
632 assert_not_nil ActionMailer::Base.deliveries.detect{|mail| [mail.bcc, mail.cc].flatten.include?(admin.mail) }
633 end
633 end
634 end
634 end
635 end
635 end
@@ -1,61 +1,61
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class AdminTest < Redmine::IntegrationTest
20 class AdminTest < Redmine::IntegrationTest
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users, :issue_categories,
22 :enumerations, :users, :issue_categories,
23 :projects_trackers,
23 :projects_trackers,
24 :roles,
24 :roles,
25 :member_roles,
25 :member_roles,
26 :members,
26 :members,
27 :enabled_modules
27 :enabled_modules
28
28
29 def test_add_user
29 def test_add_user
30 log_user("admin", "admin")
30 log_user("admin", "admin")
31 get "/users/new"
31 get "/users/new"
32 assert_response :success
32 assert_response :success
33
33
34 post "/users",
34 post "/users",
35 :user => { :login => "psmith", :firstname => "Paul",
35 :user => { :login => "psmith", :firstname => "Paul",
36 :lastname => "Smith", :mail => "psmith@somenet.foo",
36 :lastname => "Smith", :mail => "psmith@somenet.foo",
37 :language => "en", :password => "psmith09",
37 :language => "en", :password => "psmith09",
38 :password_confirmation => "psmith09" }
38 :password_confirmation => "psmith09" }
39
39
40 user = User.find_by_login("psmith")
40 user = User.find_by_login("psmith")
41 assert_kind_of User, user
41 assert_kind_of User, user
42 assert_redirected_to "/users/#{ user.id }/edit"
42 assert_redirected_to "/users/#{ user.id }/edit"
43
43
44 logged_user = User.try_to_login("psmith", "psmith09")
44 logged_user = User.try_to_login("psmith", "psmith09")
45 assert_kind_of User, logged_user
45 assert_kind_of User, logged_user
46 assert_equal "Paul", logged_user.firstname
46 assert_equal "Paul", logged_user.firstname
47
47
48 put "/users/#{user.id}", :id => user.id, :user => { :status => User::STATUS_LOCKED }
48 put "/users/#{user.id}", :id => user.id, :user => { :status => User::STATUS_LOCKED }
49 assert_redirected_to "/users/#{ user.id }/edit"
49 assert_redirected_to "/users/#{ user.id }/edit"
50 locked_user = User.try_to_login("psmith", "psmith09")
50 locked_user = User.try_to_login("psmith", "psmith09")
51 assert_equal nil, locked_user
51 assert_nil locked_user
52 end
52 end
53
53
54 test "Add a user as an anonymous user should fail" do
54 test "Add a user as an anonymous user should fail" do
55 post '/users',
55 post '/users',
56 :user => { :login => 'psmith', :firstname => 'Paul'},
56 :user => { :login => 'psmith', :firstname => 'Paul'},
57 :password => "psmith09", :password_confirmation => "psmith09"
57 :password => "psmith09", :password_confirmation => "psmith09"
58 assert_response :redirect
58 assert_response :redirect
59 assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fusers"
59 assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fusers"
60 end
60 end
61 end
61 end
@@ -1,882 +1,882
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :member_roles,
25 :member_roles,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :issue_relations,
28 :issue_relations,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries,
44 :queries,
45 :attachments
45 :attachments
46
46
47 test "GET /issues.xml should contain metadata" do
47 test "GET /issues.xml should contain metadata" do
48 get '/issues.xml'
48 get '/issues.xml'
49 assert_select 'issues[type=array][total_count][limit="25"][offset="0"]'
49 assert_select 'issues[type=array][total_count][limit="25"][offset="0"]'
50 end
50 end
51
51
52 test "GET /issues.xml with nometa param should not contain metadata" do
52 test "GET /issues.xml with nometa param should not contain metadata" do
53 get '/issues.xml?nometa=1'
53 get '/issues.xml?nometa=1'
54 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
54 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
55 end
55 end
56
56
57 test "GET /issues.xml with nometa header should not contain metadata" do
57 test "GET /issues.xml with nometa header should not contain metadata" do
58 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
58 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
59 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
59 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
60 end
60 end
61
61
62 test "GET /issues.xml with offset and limit" do
62 test "GET /issues.xml with offset and limit" do
63 get '/issues.xml?offset=2&limit=3'
63 get '/issues.xml?offset=2&limit=3'
64 assert_select 'issues[type=array][total_count][limit="3"][offset="2"]'
64 assert_select 'issues[type=array][total_count][limit="3"][offset="2"]'
65 assert_select 'issues issue', 3
65 assert_select 'issues issue', 3
66 end
66 end
67
67
68 test "GET /issues.xml with relations" do
68 test "GET /issues.xml with relations" do
69 get '/issues.xml?include=relations'
69 get '/issues.xml?include=relations'
70
70
71 assert_response :success
71 assert_response :success
72 assert_equal 'application/xml', @response.content_type
72 assert_equal 'application/xml', @response.content_type
73
73
74 assert_select 'issue id', :text => '3' do
74 assert_select 'issue id', :text => '3' do
75 assert_select '~ relations relation', 1
75 assert_select '~ relations relation', 1
76 assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
76 assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
77 end
77 end
78
78
79 assert_select 'issue id', :text => '1' do
79 assert_select 'issue id', :text => '1' do
80 assert_select '~ relations'
80 assert_select '~ relations'
81 assert_select '~ relations relation', 0
81 assert_select '~ relations relation', 0
82 end
82 end
83 end
83 end
84
84
85 test "GET /issues.xml with invalid query params" do
85 test "GET /issues.xml with invalid query params" do
86 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
86 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
87
87
88 assert_response :unprocessable_entity
88 assert_response :unprocessable_entity
89 assert_equal 'application/xml', @response.content_type
89 assert_equal 'application/xml', @response.content_type
90 assert_select 'errors error', :text => "Start date cannot be blank"
90 assert_select 'errors error', :text => "Start date cannot be blank"
91 end
91 end
92
92
93 test "GET /issues.xml with custom field filter" do
93 test "GET /issues.xml with custom field filter" do
94 get '/issues.xml',
94 get '/issues.xml',
95 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
95 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
96
96
97 expected_ids = Issue.visible.
97 expected_ids = Issue.visible.
98 joins(:custom_values).
98 joins(:custom_values).
99 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
99 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
100 assert expected_ids.any?
100 assert expected_ids.any?
101
101
102 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
102 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
103 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
103 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
104 end
104 end
105 end
105 end
106
106
107 test "GET /issues.xml with custom field filter (shorthand method)" do
107 test "GET /issues.xml with custom field filter (shorthand method)" do
108 get '/issues.xml', {:cf_1 => 'MySQL'}
108 get '/issues.xml', {:cf_1 => 'MySQL'}
109
109
110 expected_ids = Issue.visible.
110 expected_ids = Issue.visible.
111 joins(:custom_values).
111 joins(:custom_values).
112 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
112 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
113 assert expected_ids.any?
113 assert expected_ids.any?
114
114
115 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
115 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
116 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
116 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
117 end
117 end
118 end
118 end
119
119
120 def test_index_should_include_issue_attributes
120 def test_index_should_include_issue_attributes
121 get '/issues.xml'
121 get '/issues.xml'
122 assert_select 'issues>issue>is_private', :text => 'false'
122 assert_select 'issues>issue>is_private', :text => 'false'
123 end
123 end
124
124
125 def test_index_should_allow_timestamp_filtering
125 def test_index_should_allow_timestamp_filtering
126 Issue.delete_all
126 Issue.delete_all
127 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
127 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
128 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
128 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
129
129
130 get '/issues.xml',
130 get '/issues.xml',
131 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
131 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
132 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
132 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
133 assert_select 'issues>issue', :count => 1
133 assert_select 'issues>issue', :count => 1
134 assert_select 'issues>issue>subject', :text => '1'
134 assert_select 'issues>issue>subject', :text => '1'
135
135
136 get '/issues.xml',
136 get '/issues.xml',
137 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
137 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
138 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
138 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
139 assert_select 'issues>issue', :count => 1
139 assert_select 'issues>issue', :count => 1
140 assert_select 'issues>issue>subject', :text => '2'
140 assert_select 'issues>issue>subject', :text => '2'
141
141
142 get '/issues.xml',
142 get '/issues.xml',
143 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
143 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
144 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
144 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
145 assert_select 'issues>issue', :count => 2
145 assert_select 'issues>issue', :count => 2
146 end
146 end
147
147
148 test "GET /issues.xml with filter" do
148 test "GET /issues.xml with filter" do
149 get '/issues.xml?status_id=5'
149 get '/issues.xml?status_id=5'
150
150
151 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
151 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
152 assert expected_ids.any?
152 assert expected_ids.any?
153
153
154 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
154 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
155 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
155 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
156 end
156 end
157 end
157 end
158
158
159 test "GET /issues.json with filter" do
159 test "GET /issues.json with filter" do
160 get '/issues.json?status_id=5'
160 get '/issues.json?status_id=5'
161
161
162 json = ActiveSupport::JSON.decode(response.body)
162 json = ActiveSupport::JSON.decode(response.body)
163 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
163 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
164 assert_equal 3, status_ids_used.length
164 assert_equal 3, status_ids_used.length
165 assert status_ids_used.all? {|id| id == 5 }
165 assert status_ids_used.all? {|id| id == 5 }
166 end
166 end
167
167
168 test "GET /issues/:id.xml with journals" do
168 test "GET /issues/:id.xml with journals" do
169 Journal.find(2).update_attribute(:private_notes, true)
169 Journal.find(2).update_attribute(:private_notes, true)
170
170
171 get '/issues/1.xml?include=journals', {}, credentials('jsmith')
171 get '/issues/1.xml?include=journals', {}, credentials('jsmith')
172
172
173 assert_select 'issue journals[type=array]' do
173 assert_select 'issue journals[type=array]' do
174 assert_select 'journal[id="1"]' do
174 assert_select 'journal[id="1"]' do
175 assert_select 'private_notes', :text => 'false'
175 assert_select 'private_notes', :text => 'false'
176 assert_select 'details[type=array]' do
176 assert_select 'details[type=array]' do
177 assert_select 'detail[name=status_id]' do
177 assert_select 'detail[name=status_id]' do
178 assert_select 'old_value', :text => '1'
178 assert_select 'old_value', :text => '1'
179 assert_select 'new_value', :text => '2'
179 assert_select 'new_value', :text => '2'
180 end
180 end
181 end
181 end
182 end
182 end
183 assert_select 'journal[id="2"]' do
183 assert_select 'journal[id="2"]' do
184 assert_select 'private_notes', :text => 'true'
184 assert_select 'private_notes', :text => 'true'
185 assert_select 'details[type=array]'
185 assert_select 'details[type=array]'
186 end
186 end
187 end
187 end
188 end
188 end
189
189
190 test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
190 test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
191 get '/issues/1.xml?include=journals'
191 get '/issues/1.xml?include=journals'
192
192
193 iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
193 iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
194 assert_select 'issue>created_on', :text => iso_date
194 assert_select 'issue>created_on', :text => iso_date
195 assert_select 'issue>updated_on', :text => iso_date
195 assert_select 'issue>updated_on', :text => iso_date
196 assert_select 'issue journal>created_on', :text => iso_date
196 assert_select 'issue journal>created_on', :text => iso_date
197 end
197 end
198
198
199 test "GET /issues/:id.xml with custom fields" do
199 test "GET /issues/:id.xml with custom fields" do
200 get '/issues/3.xml'
200 get '/issues/3.xml'
201
201
202 assert_select 'issue custom_fields[type=array]' do
202 assert_select 'issue custom_fields[type=array]' do
203 assert_select 'custom_field[id="1"]' do
203 assert_select 'custom_field[id="1"]' do
204 assert_select 'value', :text => 'MySQL'
204 assert_select 'value', :text => 'MySQL'
205 end
205 end
206 end
206 end
207 assert_nothing_raised do
207 assert_nothing_raised do
208 Hash.from_xml(response.body).to_xml
208 Hash.from_xml(response.body).to_xml
209 end
209 end
210 end
210 end
211
211
212 test "GET /issues/:id.xml with multi custom fields" do
212 test "GET /issues/:id.xml with multi custom fields" do
213 field = CustomField.find(1)
213 field = CustomField.find(1)
214 field.update_attribute :multiple, true
214 field.update_attribute :multiple, true
215 issue = Issue.find(3)
215 issue = Issue.find(3)
216 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
216 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
217 issue.save!
217 issue.save!
218
218
219 get '/issues/3.xml'
219 get '/issues/3.xml'
220 assert_response :success
220 assert_response :success
221
221
222 assert_select 'issue custom_fields[type=array]' do
222 assert_select 'issue custom_fields[type=array]' do
223 assert_select 'custom_field[id="1"]' do
223 assert_select 'custom_field[id="1"]' do
224 assert_select 'value[type=array] value', 2
224 assert_select 'value[type=array] value', 2
225 end
225 end
226 end
226 end
227 xml = Hash.from_xml(response.body)
227 xml = Hash.from_xml(response.body)
228 custom_fields = xml['issue']['custom_fields']
228 custom_fields = xml['issue']['custom_fields']
229 assert_kind_of Array, custom_fields
229 assert_kind_of Array, custom_fields
230 field = custom_fields.detect {|f| f['id'] == '1'}
230 field = custom_fields.detect {|f| f['id'] == '1'}
231 assert_kind_of Hash, field
231 assert_kind_of Hash, field
232 assert_equal ['MySQL', 'Oracle'], field['value'].sort
232 assert_equal ['MySQL', 'Oracle'], field['value'].sort
233 end
233 end
234
234
235 test "GET /issues/:id.json with multi custom fields" do
235 test "GET /issues/:id.json with multi custom fields" do
236 field = CustomField.find(1)
236 field = CustomField.find(1)
237 field.update_attribute :multiple, true
237 field.update_attribute :multiple, true
238 issue = Issue.find(3)
238 issue = Issue.find(3)
239 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
239 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
240 issue.save!
240 issue.save!
241
241
242 get '/issues/3.json'
242 get '/issues/3.json'
243 assert_response :success
243 assert_response :success
244
244
245 json = ActiveSupport::JSON.decode(response.body)
245 json = ActiveSupport::JSON.decode(response.body)
246 custom_fields = json['issue']['custom_fields']
246 custom_fields = json['issue']['custom_fields']
247 assert_kind_of Array, custom_fields
247 assert_kind_of Array, custom_fields
248 field = custom_fields.detect {|f| f['id'] == 1}
248 field = custom_fields.detect {|f| f['id'] == 1}
249 assert_kind_of Hash, field
249 assert_kind_of Hash, field
250 assert_equal ['MySQL', 'Oracle'], field['value'].sort
250 assert_equal ['MySQL', 'Oracle'], field['value'].sort
251 end
251 end
252
252
253 test "GET /issues/:id.xml with empty value for multi custom field" do
253 test "GET /issues/:id.xml with empty value for multi custom field" do
254 field = CustomField.find(1)
254 field = CustomField.find(1)
255 field.update_attribute :multiple, true
255 field.update_attribute :multiple, true
256 issue = Issue.find(3)
256 issue = Issue.find(3)
257 issue.custom_field_values = {1 => ['']}
257 issue.custom_field_values = {1 => ['']}
258 issue.save!
258 issue.save!
259
259
260 get '/issues/3.xml'
260 get '/issues/3.xml'
261
261
262 assert_select 'issue custom_fields[type=array]' do
262 assert_select 'issue custom_fields[type=array]' do
263 assert_select 'custom_field[id="1"]' do
263 assert_select 'custom_field[id="1"]' do
264 assert_select 'value[type=array]:empty'
264 assert_select 'value[type=array]:empty'
265 end
265 end
266 end
266 end
267 xml = Hash.from_xml(response.body)
267 xml = Hash.from_xml(response.body)
268 custom_fields = xml['issue']['custom_fields']
268 custom_fields = xml['issue']['custom_fields']
269 assert_kind_of Array, custom_fields
269 assert_kind_of Array, custom_fields
270 field = custom_fields.detect {|f| f['id'] == '1'}
270 field = custom_fields.detect {|f| f['id'] == '1'}
271 assert_kind_of Hash, field
271 assert_kind_of Hash, field
272 assert_equal [], field['value']
272 assert_equal [], field['value']
273 end
273 end
274
274
275 test "GET /issues/:id.json with empty value for multi custom field" do
275 test "GET /issues/:id.json with empty value for multi custom field" do
276 field = CustomField.find(1)
276 field = CustomField.find(1)
277 field.update_attribute :multiple, true
277 field.update_attribute :multiple, true
278 issue = Issue.find(3)
278 issue = Issue.find(3)
279 issue.custom_field_values = {1 => ['']}
279 issue.custom_field_values = {1 => ['']}
280 issue.save!
280 issue.save!
281
281
282 get '/issues/3.json'
282 get '/issues/3.json'
283 assert_response :success
283 assert_response :success
284 json = ActiveSupport::JSON.decode(response.body)
284 json = ActiveSupport::JSON.decode(response.body)
285 custom_fields = json['issue']['custom_fields']
285 custom_fields = json['issue']['custom_fields']
286 assert_kind_of Array, custom_fields
286 assert_kind_of Array, custom_fields
287 field = custom_fields.detect {|f| f['id'] == 1}
287 field = custom_fields.detect {|f| f['id'] == 1}
288 assert_kind_of Hash, field
288 assert_kind_of Hash, field
289 assert_equal [], field['value'].sort
289 assert_equal [], field['value'].sort
290 end
290 end
291
291
292 test "GET /issues/:id.xml with attachments" do
292 test "GET /issues/:id.xml with attachments" do
293 get '/issues/3.xml?include=attachments'
293 get '/issues/3.xml?include=attachments'
294
294
295 assert_select 'issue attachments[type=array]' do
295 assert_select 'issue attachments[type=array]' do
296 assert_select 'attachment', 4
296 assert_select 'attachment', 4
297 assert_select 'attachment id', :text => '1' do
297 assert_select 'attachment id', :text => '1' do
298 assert_select '~ filename', :text => 'error281.txt'
298 assert_select '~ filename', :text => 'error281.txt'
299 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/1/error281.txt'
299 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/1/error281.txt'
300 end
300 end
301 end
301 end
302 end
302 end
303
303
304 test "GET /issues/:id.xml with subtasks" do
304 test "GET /issues/:id.xml with subtasks" do
305 issue = Issue.generate_with_descendants!(:project_id => 1)
305 issue = Issue.generate_with_descendants!(:project_id => 1)
306 get "/issues/#{issue.id}.xml?include=children"
306 get "/issues/#{issue.id}.xml?include=children"
307
307
308 assert_select 'issue id', :text => issue.id.to_s do
308 assert_select 'issue id', :text => issue.id.to_s do
309 assert_select '~ children[type=array] > issue', 2
309 assert_select '~ children[type=array] > issue', 2
310 assert_select '~ children[type=array] > issue > children', 1
310 assert_select '~ children[type=array] > issue > children', 1
311 end
311 end
312 end
312 end
313
313
314 test "GET /issues/:id.json with subtasks" do
314 test "GET /issues/:id.json with subtasks" do
315 issue = Issue.generate_with_descendants!(:project_id => 1)
315 issue = Issue.generate_with_descendants!(:project_id => 1)
316 get "/issues/#{issue.id}.json?include=children"
316 get "/issues/#{issue.id}.json?include=children"
317
317
318 json = ActiveSupport::JSON.decode(response.body)
318 json = ActiveSupport::JSON.decode(response.body)
319 assert_equal 2, json['issue']['children'].size
319 assert_equal 2, json['issue']['children'].size
320 assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
320 assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
321 end
321 end
322
322
323 def test_show_should_include_issue_attributes
323 def test_show_should_include_issue_attributes
324 get '/issues/1.xml'
324 get '/issues/1.xml'
325 assert_select 'issue>is_private', :text => 'false'
325 assert_select 'issue>is_private', :text => 'false'
326 end
326 end
327
327
328 test "GET /issues/:id.xml?include=watchers should include watchers" do
328 test "GET /issues/:id.xml?include=watchers should include watchers" do
329 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
329 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
330
330
331 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
331 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
332
332
333 assert_response :ok
333 assert_response :ok
334 assert_equal 'application/xml', response.content_type
334 assert_equal 'application/xml', response.content_type
335 assert_select 'issue' do
335 assert_select 'issue' do
336 assert_select 'watchers', Issue.find(1).watchers.count
336 assert_select 'watchers', Issue.find(1).watchers.count
337 assert_select 'watchers' do
337 assert_select 'watchers' do
338 assert_select 'user[id="3"]'
338 assert_select 'user[id="3"]'
339 end
339 end
340 end
340 end
341 end
341 end
342
342
343 test "GET /issues/:id.xml should not disclose associated changesets from projects the user has no access to" do
343 test "GET /issues/:id.xml should not disclose associated changesets from projects the user has no access to" do
344 project = Project.generate!(:is_public => false)
344 project = Project.generate!(:is_public => false)
345 repository = Repository::Subversion.create!(:project => project, :url => "svn://localhost")
345 repository = Repository::Subversion.create!(:project => project, :url => "svn://localhost")
346 Issue.find(1).changesets << Changeset.generate!(:repository => repository)
346 Issue.find(1).changesets << Changeset.generate!(:repository => repository)
347 assert Issue.find(1).changesets.any?
347 assert Issue.find(1).changesets.any?
348
348
349 get '/issues/1.xml?include=changesets', {}, credentials('jsmith')
349 get '/issues/1.xml?include=changesets', {}, credentials('jsmith')
350
350
351 # the user jsmith has no permission to view the associated changeset
351 # the user jsmith has no permission to view the associated changeset
352 assert_select 'issue changesets[type=array]' do
352 assert_select 'issue changesets[type=array]' do
353 assert_select 'changeset', 0
353 assert_select 'changeset', 0
354 end
354 end
355 end
355 end
356
356
357 test "GET /issues/:id.xml should contains total_estimated_hours and total_spent_hours" do
357 test "GET /issues/:id.xml should contains total_estimated_hours and total_spent_hours" do
358 parent = Issue.find(3)
358 parent = Issue.find(3)
359 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
359 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
360 TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
360 TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
361 :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
361 :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
362 get '/issues/3.xml'
362 get '/issues/3.xml'
363
363
364 assert_equal 'application/xml', response.content_type
364 assert_equal 'application/xml', response.content_type
365 assert_select 'issue' do
365 assert_select 'issue' do
366 assert_select 'estimated_hours', parent.estimated_hours.to_s
366 assert_select 'estimated_hours', parent.estimated_hours.to_s
367 assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
367 assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
368 assert_select 'spent_hours', parent.spent_hours.to_s
368 assert_select 'spent_hours', parent.spent_hours.to_s
369 assert_select 'total_spent_hours', (parent.spent_hours.to_f + 2.5).to_s
369 assert_select 'total_spent_hours', (parent.spent_hours.to_f + 2.5).to_s
370 end
370 end
371 end
371 end
372
372
373 test "GET /issues/:id.xml should contains total_estimated_hours, and should not contains spent_hours and total_spent_hours when permission does not exists" do
373 test "GET /issues/:id.xml should contains total_estimated_hours, and should not contains spent_hours and total_spent_hours when permission does not exists" do
374 parent = Issue.find(3)
374 parent = Issue.find(3)
375 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
375 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
376 # remove permission!
376 # remove permission!
377 Role.anonymous.remove_permission! :view_time_entries
377 Role.anonymous.remove_permission! :view_time_entries
378 #Role.all.each { |role| role.remove_permission! :view_time_entries }
378 #Role.all.each { |role| role.remove_permission! :view_time_entries }
379 get '/issues/3.xml'
379 get '/issues/3.xml'
380
380
381 assert_equal 'application/xml', response.content_type
381 assert_equal 'application/xml', response.content_type
382 assert_select 'issue' do
382 assert_select 'issue' do
383 assert_select 'estimated_hours', parent.estimated_hours.to_s
383 assert_select 'estimated_hours', parent.estimated_hours.to_s
384 assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
384 assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
385 assert_select 'spent_hours', false
385 assert_select 'spent_hours', false
386 assert_select 'total_spent_hours', false
386 assert_select 'total_spent_hours', false
387 end
387 end
388 end
388 end
389
389
390 test "GET /issues/:id.json should contains total_estimated_hours and total_spent_hours" do
390 test "GET /issues/:id.json should contains total_estimated_hours and total_spent_hours" do
391 parent = Issue.find(3)
391 parent = Issue.find(3)
392 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
392 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
393 TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
393 TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
394 :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
394 :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
395 get '/issues/3.json'
395 get '/issues/3.json'
396
396
397 assert_equal 'application/json', response.content_type
397 assert_equal 'application/json', response.content_type
398 json = ActiveSupport::JSON.decode(response.body)
398 json = ActiveSupport::JSON.decode(response.body)
399 assert_equal parent.estimated_hours, json['issue']['estimated_hours']
399 assert_equal parent.estimated_hours, json['issue']['estimated_hours']
400 assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
400 assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
401 assert_equal parent.spent_hours, json['issue']['spent_hours']
401 assert_equal parent.spent_hours, json['issue']['spent_hours']
402 assert_equal (parent.spent_hours.to_f + 2.5), json['issue']['total_spent_hours']
402 assert_equal (parent.spent_hours.to_f + 2.5), json['issue']['total_spent_hours']
403 end
403 end
404
404
405 test "GET /issues/:id.json should contains total_estimated_hours, and should not contains spent_hours and total_spent_hours when permission does not exists" do
405 test "GET /issues/:id.json should contains total_estimated_hours, and should not contains spent_hours and total_spent_hours when permission does not exists" do
406 parent = Issue.find(3)
406 parent = Issue.find(3)
407 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
407 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
408 # remove permission!
408 # remove permission!
409 Role.anonymous.remove_permission! :view_time_entries
409 Role.anonymous.remove_permission! :view_time_entries
410 #Role.all.each { |role| role.remove_permission! :view_time_entries }
410 #Role.all.each { |role| role.remove_permission! :view_time_entries }
411 get '/issues/3.json'
411 get '/issues/3.json'
412
412
413 assert_equal 'application/json', response.content_type
413 assert_equal 'application/json', response.content_type
414 json = ActiveSupport::JSON.decode(response.body)
414 json = ActiveSupport::JSON.decode(response.body)
415 assert_equal parent.estimated_hours, json['issue']['estimated_hours']
415 assert_equal parent.estimated_hours, json['issue']['estimated_hours']
416 assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
416 assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
417 assert_equal nil, json['issue']['spent_hours']
417 assert_nil json['issue']['spent_hours']
418 assert_equal nil, json['issue']['total_spent_hours']
418 assert_nil json['issue']['total_spent_hours']
419 end
419 end
420
420
421 test "POST /issues.xml should create an issue with the attributes" do
421 test "POST /issues.xml should create an issue with the attributes" do
422
422
423 payload = <<-XML
423 payload = <<-XML
424 <?xml version="1.0" encoding="UTF-8" ?>
424 <?xml version="1.0" encoding="UTF-8" ?>
425 <issue>
425 <issue>
426 <project_id>1</project_id>
426 <project_id>1</project_id>
427 <tracker_id>2</tracker_id>
427 <tracker_id>2</tracker_id>
428 <status_id>3</status_id>
428 <status_id>3</status_id>
429 <subject>API test</subject>
429 <subject>API test</subject>
430 </issue>
430 </issue>
431 XML
431 XML
432
432
433 assert_difference('Issue.count') do
433 assert_difference('Issue.count') do
434 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
434 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
435 end
435 end
436 issue = Issue.order('id DESC').first
436 issue = Issue.order('id DESC').first
437 assert_equal 1, issue.project_id
437 assert_equal 1, issue.project_id
438 assert_equal 2, issue.tracker_id
438 assert_equal 2, issue.tracker_id
439 assert_equal 3, issue.status_id
439 assert_equal 3, issue.status_id
440 assert_equal 'API test', issue.subject
440 assert_equal 'API test', issue.subject
441
441
442 assert_response :created
442 assert_response :created
443 assert_equal 'application/xml', @response.content_type
443 assert_equal 'application/xml', @response.content_type
444 assert_select 'issue > id', :text => issue.id.to_s
444 assert_select 'issue > id', :text => issue.id.to_s
445 end
445 end
446
446
447 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
447 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
448 assert_difference('Issue.count') do
448 assert_difference('Issue.count') do
449 post '/issues.xml',
449 post '/issues.xml',
450 {:issue => {:project_id => 1, :subject => 'Watchers',
450 {:issue => {:project_id => 1, :subject => 'Watchers',
451 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
451 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
452 assert_response :created
452 assert_response :created
453 end
453 end
454 issue = Issue.order('id desc').first
454 issue = Issue.order('id desc').first
455 assert_equal 2, issue.watchers.size
455 assert_equal 2, issue.watchers.size
456 assert_equal [1, 3], issue.watcher_user_ids.sort
456 assert_equal [1, 3], issue.watcher_user_ids.sort
457 end
457 end
458
458
459 test "POST /issues.xml with failure should return errors" do
459 test "POST /issues.xml with failure should return errors" do
460 assert_no_difference('Issue.count') do
460 assert_no_difference('Issue.count') do
461 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
461 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
462 end
462 end
463
463
464 assert_select 'errors error', :text => "Subject cannot be blank"
464 assert_select 'errors error', :text => "Subject cannot be blank"
465 end
465 end
466
466
467 test "POST /issues.json should create an issue with the attributes" do
467 test "POST /issues.json should create an issue with the attributes" do
468
468
469 payload = <<-JSON
469 payload = <<-JSON
470 {
470 {
471 "issue": {
471 "issue": {
472 "project_id": "1",
472 "project_id": "1",
473 "tracker_id": "2",
473 "tracker_id": "2",
474 "status_id": "3",
474 "status_id": "3",
475 "subject": "API test"
475 "subject": "API test"
476 }
476 }
477 }
477 }
478 JSON
478 JSON
479
479
480 assert_difference('Issue.count') do
480 assert_difference('Issue.count') do
481 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
481 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
482 end
482 end
483
483
484 issue = Issue.order('id DESC').first
484 issue = Issue.order('id DESC').first
485 assert_equal 1, issue.project_id
485 assert_equal 1, issue.project_id
486 assert_equal 2, issue.tracker_id
486 assert_equal 2, issue.tracker_id
487 assert_equal 3, issue.status_id
487 assert_equal 3, issue.status_id
488 assert_equal 'API test', issue.subject
488 assert_equal 'API test', issue.subject
489 end
489 end
490
490
491 test "POST /issues.json should accept project identifier as project_id" do
491 test "POST /issues.json should accept project identifier as project_id" do
492 assert_difference('Issue.count') do
492 assert_difference('Issue.count') do
493 post '/issues.json',
493 post '/issues.json',
494 {:issue => {:project_id => 'subproject1', :tracker_id => 2, :subject => 'Foo'}},
494 {:issue => {:project_id => 'subproject1', :tracker_id => 2, :subject => 'Foo'}},
495 credentials('jsmith')
495 credentials('jsmith')
496
496
497 assert_response :created
497 assert_response :created
498 end
498 end
499 end
499 end
500
500
501 test "POST /issues.json without tracker_id should accept custom fields" do
501 test "POST /issues.json without tracker_id should accept custom fields" do
502 field = IssueCustomField.generate!(
502 field = IssueCustomField.generate!(
503 :field_format => 'list',
503 :field_format => 'list',
504 :multiple => true,
504 :multiple => true,
505 :possible_values => ["V1", "V2", "V3"],
505 :possible_values => ["V1", "V2", "V3"],
506 :default_value => "V2",
506 :default_value => "V2",
507 :is_for_all => true,
507 :is_for_all => true,
508 :trackers => Tracker.all.to_a
508 :trackers => Tracker.all.to_a
509 )
509 )
510
510
511 payload = <<-JSON
511 payload = <<-JSON
512 {
512 {
513 "issue": {
513 "issue": {
514 "project_id": "1",
514 "project_id": "1",
515 "subject": "Multivalued custom field",
515 "subject": "Multivalued custom field",
516 "custom_field_values":{"#{field.id}":["V1","V3"]}
516 "custom_field_values":{"#{field.id}":["V1","V3"]}
517 }
517 }
518 }
518 }
519 JSON
519 JSON
520
520
521 assert_difference('Issue.count') do
521 assert_difference('Issue.count') do
522 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
522 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
523 end
523 end
524
524
525 assert_response :created
525 assert_response :created
526 issue = Issue.order('id DESC').first
526 issue = Issue.order('id DESC').first
527 assert_equal ["V1", "V3"], issue.custom_field_value(field).sort
527 assert_equal ["V1", "V3"], issue.custom_field_value(field).sort
528 end
528 end
529
529
530 test "POST /issues.json with omitted custom field should set default value" do
530 test "POST /issues.json with omitted custom field should set default value" do
531 field = IssueCustomField.generate!(:default_value => "Default")
531 field = IssueCustomField.generate!(:default_value => "Default")
532
532
533 issue = new_record(Issue) do
533 issue = new_record(Issue) do
534 post '/issues.json',
534 post '/issues.json',
535 {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {}}},
535 {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {}}},
536 credentials('jsmith')
536 credentials('jsmith')
537 end
537 end
538 assert_equal "Default", issue.custom_field_value(field)
538 assert_equal "Default", issue.custom_field_value(field)
539 end
539 end
540
540
541 test "POST /issues.json with custom field set to blank should not set default value" do
541 test "POST /issues.json with custom field set to blank should not set default value" do
542 field = IssueCustomField.generate!(:default_value => "Default")
542 field = IssueCustomField.generate!(:default_value => "Default")
543
543
544 issue = new_record(Issue) do
544 issue = new_record(Issue) do
545 post '/issues.json',
545 post '/issues.json',
546 {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {field.id.to_s => ""}}},
546 {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {field.id.to_s => ""}}},
547 credentials('jsmith')
547 credentials('jsmith')
548 end
548 end
549 assert_equal "", issue.custom_field_value(field)
549 assert_equal "", issue.custom_field_value(field)
550 end
550 end
551
551
552 test "POST /issues.json with failure should return errors" do
552 test "POST /issues.json with failure should return errors" do
553 assert_no_difference('Issue.count') do
553 assert_no_difference('Issue.count') do
554 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
554 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
555 end
555 end
556
556
557 json = ActiveSupport::JSON.decode(response.body)
557 json = ActiveSupport::JSON.decode(response.body)
558 assert json['errors'].include?("Subject cannot be blank")
558 assert json['errors'].include?("Subject cannot be blank")
559 end
559 end
560
560
561 test "POST /issues.json with invalid project_id should respond with 422" do
561 test "POST /issues.json with invalid project_id should respond with 422" do
562 post '/issues.json', {:issue => {:project_id => 999, :subject => "API"}}, credentials('jsmith')
562 post '/issues.json', {:issue => {:project_id => 999, :subject => "API"}}, credentials('jsmith')
563 assert_response 422
563 assert_response 422
564 end
564 end
565
565
566 test "PUT /issues/:id.xml" do
566 test "PUT /issues/:id.xml" do
567 assert_difference('Journal.count') do
567 assert_difference('Journal.count') do
568 put '/issues/6.xml',
568 put '/issues/6.xml',
569 {:issue => {:subject => 'API update', :notes => 'A new note'}},
569 {:issue => {:subject => 'API update', :notes => 'A new note'}},
570 credentials('jsmith')
570 credentials('jsmith')
571 end
571 end
572
572
573 issue = Issue.find(6)
573 issue = Issue.find(6)
574 assert_equal "API update", issue.subject
574 assert_equal "API update", issue.subject
575 journal = Journal.last
575 journal = Journal.last
576 assert_equal "A new note", journal.notes
576 assert_equal "A new note", journal.notes
577 end
577 end
578
578
579 test "PUT /issues/:id.xml with custom fields" do
579 test "PUT /issues/:id.xml with custom fields" do
580 put '/issues/3.xml',
580 put '/issues/3.xml',
581 {:issue => {:custom_fields => [
581 {:issue => {:custom_fields => [
582 {'id' => '1', 'value' => 'PostgreSQL' },
582 {'id' => '1', 'value' => 'PostgreSQL' },
583 {'id' => '2', 'value' => '150'}
583 {'id' => '2', 'value' => '150'}
584 ]}},
584 ]}},
585 credentials('jsmith')
585 credentials('jsmith')
586
586
587 issue = Issue.find(3)
587 issue = Issue.find(3)
588 assert_equal '150', issue.custom_value_for(2).value
588 assert_equal '150', issue.custom_value_for(2).value
589 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
589 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
590 end
590 end
591
591
592 test "PUT /issues/:id.xml with multi custom fields" do
592 test "PUT /issues/:id.xml with multi custom fields" do
593 field = CustomField.find(1)
593 field = CustomField.find(1)
594 field.update_attribute :multiple, true
594 field.update_attribute :multiple, true
595
595
596 put '/issues/3.xml',
596 put '/issues/3.xml',
597 {:issue => {:custom_fields => [
597 {:issue => {:custom_fields => [
598 {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
598 {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
599 {'id' => '2', 'value' => '150'}
599 {'id' => '2', 'value' => '150'}
600 ]}},
600 ]}},
601 credentials('jsmith')
601 credentials('jsmith')
602
602
603 issue = Issue.find(3)
603 issue = Issue.find(3)
604 assert_equal '150', issue.custom_value_for(2).value
604 assert_equal '150', issue.custom_value_for(2).value
605 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
605 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
606 end
606 end
607
607
608 test "PUT /issues/:id.xml with project change" do
608 test "PUT /issues/:id.xml with project change" do
609 put '/issues/3.xml',
609 put '/issues/3.xml',
610 {:issue => {:project_id => 2, :subject => 'Project changed'}},
610 {:issue => {:project_id => 2, :subject => 'Project changed'}},
611 credentials('jsmith')
611 credentials('jsmith')
612
612
613 issue = Issue.find(3)
613 issue = Issue.find(3)
614 assert_equal 2, issue.project_id
614 assert_equal 2, issue.project_id
615 assert_equal 'Project changed', issue.subject
615 assert_equal 'Project changed', issue.subject
616 end
616 end
617
617
618 test "PUT /issues/:id.xml with notes only" do
618 test "PUT /issues/:id.xml with notes only" do
619 assert_difference('Journal.count') do
619 assert_difference('Journal.count') do
620 put '/issues/6.xml',
620 put '/issues/6.xml',
621 {:issue => {:notes => 'Notes only'}},
621 {:issue => {:notes => 'Notes only'}},
622 credentials('jsmith')
622 credentials('jsmith')
623 end
623 end
624
624
625 journal = Journal.last
625 journal = Journal.last
626 assert_equal "Notes only", journal.notes
626 assert_equal "Notes only", journal.notes
627 end
627 end
628
628
629 test "PUT /issues/:id.json with omitted custom field should not change blank value to default value" do
629 test "PUT /issues/:id.json with omitted custom field should not change blank value to default value" do
630 field = IssueCustomField.generate!(:default_value => "Default")
630 field = IssueCustomField.generate!(:default_value => "Default")
631 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
631 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
632 assert_equal "", issue.reload.custom_field_value(field)
632 assert_equal "", issue.reload.custom_field_value(field)
633
633
634 assert_difference('Journal.count') do
634 assert_difference('Journal.count') do
635 put "/issues/#{issue.id}.json",
635 put "/issues/#{issue.id}.json",
636 {:issue => {:custom_field_values => {}, :notes => 'API'}},
636 {:issue => {:custom_field_values => {}, :notes => 'API'}},
637 credentials('jsmith')
637 credentials('jsmith')
638 end
638 end
639
639
640 assert_equal "", issue.reload.custom_field_value(field)
640 assert_equal "", issue.reload.custom_field_value(field)
641 end
641 end
642
642
643 test "PUT /issues/:id.json with custom field set to blank should not change blank value to default value" do
643 test "PUT /issues/:id.json with custom field set to blank should not change blank value to default value" do
644 field = IssueCustomField.generate!(:default_value => "Default")
644 field = IssueCustomField.generate!(:default_value => "Default")
645 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
645 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
646 assert_equal "", issue.reload.custom_field_value(field)
646 assert_equal "", issue.reload.custom_field_value(field)
647
647
648 assert_difference('Journal.count') do
648 assert_difference('Journal.count') do
649 put "/issues/#{issue.id}.json",
649 put "/issues/#{issue.id}.json",
650 {:issue => {:custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
650 {:issue => {:custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
651 credentials('jsmith')
651 credentials('jsmith')
652 end
652 end
653
653
654 assert_equal "", issue.reload.custom_field_value(field)
654 assert_equal "", issue.reload.custom_field_value(field)
655 end
655 end
656
656
657 test "PUT /issues/:id.json with tracker change and omitted custom field specific to that tracker should set default value" do
657 test "PUT /issues/:id.json with tracker change and omitted custom field specific to that tracker should set default value" do
658 field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
658 field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
659 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
659 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
660
660
661 assert_difference('Journal.count') do
661 assert_difference('Journal.count') do
662 put "/issues/#{issue.id}.json",
662 put "/issues/#{issue.id}.json",
663 {:issue => {:tracker_id => 2, :custom_field_values => {}, :notes => 'API'}},
663 {:issue => {:tracker_id => 2, :custom_field_values => {}, :notes => 'API'}},
664 credentials('jsmith')
664 credentials('jsmith')
665 end
665 end
666
666
667 assert_equal 2, issue.reload.tracker_id
667 assert_equal 2, issue.reload.tracker_id
668 assert_equal "Default", issue.reload.custom_field_value(field)
668 assert_equal "Default", issue.reload.custom_field_value(field)
669 end
669 end
670
670
671 test "PUT /issues/:id.json with tracker change and custom field specific to that tracker set to blank should not set default value" do
671 test "PUT /issues/:id.json with tracker change and custom field specific to that tracker set to blank should not set default value" do
672 field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
672 field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
673 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
673 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
674
674
675 assert_difference('Journal.count') do
675 assert_difference('Journal.count') do
676 put "/issues/#{issue.id}.json",
676 put "/issues/#{issue.id}.json",
677 {:issue => {:tracker_id => 2, :custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
677 {:issue => {:tracker_id => 2, :custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
678 credentials('jsmith')
678 credentials('jsmith')
679 end
679 end
680
680
681 assert_equal 2, issue.reload.tracker_id
681 assert_equal 2, issue.reload.tracker_id
682 assert_equal "", issue.reload.custom_field_value(field)
682 assert_equal "", issue.reload.custom_field_value(field)
683 end
683 end
684
684
685 test "PUT /issues/:id.xml with failed update" do
685 test "PUT /issues/:id.xml with failed update" do
686 put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith')
686 put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith')
687
687
688 assert_response :unprocessable_entity
688 assert_response :unprocessable_entity
689 assert_select 'errors error', :text => "Subject cannot be blank"
689 assert_select 'errors error', :text => "Subject cannot be blank"
690 end
690 end
691
691
692 test "PUT /issues/:id.xml with invalid assignee should return error" do
692 test "PUT /issues/:id.xml with invalid assignee should return error" do
693 user = User.generate!
693 user = User.generate!
694 put '/issues/6.xml', {:issue => {:assigned_to_id => user.id}}, credentials('jsmith')
694 put '/issues/6.xml', {:issue => {:assigned_to_id => user.id}}, credentials('jsmith')
695
695
696 assert_response :unprocessable_entity
696 assert_response :unprocessable_entity
697 assert_select 'errors error', :text => "Assignee is invalid"
697 assert_select 'errors error', :text => "Assignee is invalid"
698 end
698 end
699
699
700 test "PUT /issues/:id.json" do
700 test "PUT /issues/:id.json" do
701 assert_difference('Journal.count') do
701 assert_difference('Journal.count') do
702 put '/issues/6.json',
702 put '/issues/6.json',
703 {:issue => {:subject => 'API update', :notes => 'A new note'}},
703 {:issue => {:subject => 'API update', :notes => 'A new note'}},
704 credentials('jsmith')
704 credentials('jsmith')
705
705
706 assert_response :ok
706 assert_response :ok
707 assert_equal '', response.body
707 assert_equal '', response.body
708 end
708 end
709
709
710 issue = Issue.find(6)
710 issue = Issue.find(6)
711 assert_equal "API update", issue.subject
711 assert_equal "API update", issue.subject
712 journal = Journal.last
712 journal = Journal.last
713 assert_equal "A new note", journal.notes
713 assert_equal "A new note", journal.notes
714 end
714 end
715
715
716 test "PUT /issues/:id.json with failed update" do
716 test "PUT /issues/:id.json with failed update" do
717 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
717 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
718
718
719 assert_response :unprocessable_entity
719 assert_response :unprocessable_entity
720 json = ActiveSupport::JSON.decode(response.body)
720 json = ActiveSupport::JSON.decode(response.body)
721 assert json['errors'].include?("Subject cannot be blank")
721 assert json['errors'].include?("Subject cannot be blank")
722 end
722 end
723
723
724 test "DELETE /issues/:id.xml" do
724 test "DELETE /issues/:id.xml" do
725 assert_difference('Issue.count', -1) do
725 assert_difference('Issue.count', -1) do
726 delete '/issues/6.xml', {}, credentials('jsmith')
726 delete '/issues/6.xml', {}, credentials('jsmith')
727
727
728 assert_response :ok
728 assert_response :ok
729 assert_equal '', response.body
729 assert_equal '', response.body
730 end
730 end
731 assert_nil Issue.find_by_id(6)
731 assert_nil Issue.find_by_id(6)
732 end
732 end
733
733
734 test "DELETE /issues/:id.json" do
734 test "DELETE /issues/:id.json" do
735 assert_difference('Issue.count', -1) do
735 assert_difference('Issue.count', -1) do
736 delete '/issues/6.json', {}, credentials('jsmith')
736 delete '/issues/6.json', {}, credentials('jsmith')
737
737
738 assert_response :ok
738 assert_response :ok
739 assert_equal '', response.body
739 assert_equal '', response.body
740 end
740 end
741 assert_nil Issue.find_by_id(6)
741 assert_nil Issue.find_by_id(6)
742 end
742 end
743
743
744 test "POST /issues/:id/watchers.xml should add watcher" do
744 test "POST /issues/:id/watchers.xml should add watcher" do
745 assert_difference 'Watcher.count' do
745 assert_difference 'Watcher.count' do
746 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
746 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
747
747
748 assert_response :ok
748 assert_response :ok
749 assert_equal '', response.body
749 assert_equal '', response.body
750 end
750 end
751 watcher = Watcher.order('id desc').first
751 watcher = Watcher.order('id desc').first
752 assert_equal Issue.find(1), watcher.watchable
752 assert_equal Issue.find(1), watcher.watchable
753 assert_equal User.find(3), watcher.user
753 assert_equal User.find(3), watcher.user
754 end
754 end
755
755
756 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
756 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
757 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
757 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
758
758
759 assert_difference 'Watcher.count', -1 do
759 assert_difference 'Watcher.count', -1 do
760 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
760 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
761
761
762 assert_response :ok
762 assert_response :ok
763 assert_equal '', response.body
763 assert_equal '', response.body
764 end
764 end
765 assert_equal false, Issue.find(1).watched_by?(User.find(3))
765 assert_equal false, Issue.find(1).watched_by?(User.find(3))
766 end
766 end
767
767
768 def test_create_issue_with_uploaded_file
768 def test_create_issue_with_uploaded_file
769 token = xml_upload('test_create_with_upload', credentials('jsmith'))
769 token = xml_upload('test_create_with_upload', credentials('jsmith'))
770 attachment = Attachment.find_by_token(token)
770 attachment = Attachment.find_by_token(token)
771
771
772 # create the issue with the upload's token
772 # create the issue with the upload's token
773 assert_difference 'Issue.count' do
773 assert_difference 'Issue.count' do
774 post '/issues.xml',
774 post '/issues.xml',
775 {:issue => {:project_id => 1, :subject => 'Uploaded file',
775 {:issue => {:project_id => 1, :subject => 'Uploaded file',
776 :uploads => [{:token => token, :filename => 'test.txt',
776 :uploads => [{:token => token, :filename => 'test.txt',
777 :content_type => 'text/plain'}]}},
777 :content_type => 'text/plain'}]}},
778 credentials('jsmith')
778 credentials('jsmith')
779 assert_response :created
779 assert_response :created
780 end
780 end
781 issue = Issue.order('id DESC').first
781 issue = Issue.order('id DESC').first
782 assert_equal 1, issue.attachments.count
782 assert_equal 1, issue.attachments.count
783 assert_equal attachment, issue.attachments.first
783 assert_equal attachment, issue.attachments.first
784
784
785 attachment.reload
785 attachment.reload
786 assert_equal 'test.txt', attachment.filename
786 assert_equal 'test.txt', attachment.filename
787 assert_equal 'text/plain', attachment.content_type
787 assert_equal 'text/plain', attachment.content_type
788 assert_equal 'test_create_with_upload'.size, attachment.filesize
788 assert_equal 'test_create_with_upload'.size, attachment.filesize
789 assert_equal 2, attachment.author_id
789 assert_equal 2, attachment.author_id
790
790
791 # get the issue with its attachments
791 # get the issue with its attachments
792 get "/issues/#{issue.id}.xml", :include => 'attachments'
792 get "/issues/#{issue.id}.xml", :include => 'attachments'
793 assert_response :success
793 assert_response :success
794 xml = Hash.from_xml(response.body)
794 xml = Hash.from_xml(response.body)
795 attachments = xml['issue']['attachments']
795 attachments = xml['issue']['attachments']
796 assert_kind_of Array, attachments
796 assert_kind_of Array, attachments
797 assert_equal 1, attachments.size
797 assert_equal 1, attachments.size
798 url = attachments.first['content_url']
798 url = attachments.first['content_url']
799 assert_not_nil url
799 assert_not_nil url
800
800
801 # download the attachment
801 # download the attachment
802 get url
802 get url
803 assert_response :success
803 assert_response :success
804 assert_equal 'test_create_with_upload', response.body
804 assert_equal 'test_create_with_upload', response.body
805 end
805 end
806
806
807 def test_create_issue_with_multiple_uploaded_files_as_xml
807 def test_create_issue_with_multiple_uploaded_files_as_xml
808 token1 = xml_upload('File content 1', credentials('jsmith'))
808 token1 = xml_upload('File content 1', credentials('jsmith'))
809 token2 = xml_upload('File content 2', credentials('jsmith'))
809 token2 = xml_upload('File content 2', credentials('jsmith'))
810
810
811 payload = <<-XML
811 payload = <<-XML
812 <?xml version="1.0" encoding="UTF-8" ?>
812 <?xml version="1.0" encoding="UTF-8" ?>
813 <issue>
813 <issue>
814 <project_id>1</project_id>
814 <project_id>1</project_id>
815 <tracker_id>1</tracker_id>
815 <tracker_id>1</tracker_id>
816 <subject>Issue with multiple attachments</subject>
816 <subject>Issue with multiple attachments</subject>
817 <uploads type="array">
817 <uploads type="array">
818 <upload>
818 <upload>
819 <token>#{token1}</token>
819 <token>#{token1}</token>
820 <filename>test1.txt</filename>
820 <filename>test1.txt</filename>
821 </upload>
821 </upload>
822 <upload>
822 <upload>
823 <token>#{token2}</token>
823 <token>#{token2}</token>
824 <filename>test1.txt</filename>
824 <filename>test1.txt</filename>
825 </upload>
825 </upload>
826 </uploads>
826 </uploads>
827 </issue>
827 </issue>
828 XML
828 XML
829
829
830 assert_difference 'Issue.count' do
830 assert_difference 'Issue.count' do
831 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
831 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
832 assert_response :created
832 assert_response :created
833 end
833 end
834 issue = Issue.order('id DESC').first
834 issue = Issue.order('id DESC').first
835 assert_equal 2, issue.attachments.count
835 assert_equal 2, issue.attachments.count
836 end
836 end
837
837
838 def test_create_issue_with_multiple_uploaded_files_as_json
838 def test_create_issue_with_multiple_uploaded_files_as_json
839 token1 = json_upload('File content 1', credentials('jsmith'))
839 token1 = json_upload('File content 1', credentials('jsmith'))
840 token2 = json_upload('File content 2', credentials('jsmith'))
840 token2 = json_upload('File content 2', credentials('jsmith'))
841
841
842 payload = <<-JSON
842 payload = <<-JSON
843 {
843 {
844 "issue": {
844 "issue": {
845 "project_id": "1",
845 "project_id": "1",
846 "tracker_id": "1",
846 "tracker_id": "1",
847 "subject": "Issue with multiple attachments",
847 "subject": "Issue with multiple attachments",
848 "uploads": [
848 "uploads": [
849 {"token": "#{token1}", "filename": "test1.txt"},
849 {"token": "#{token1}", "filename": "test1.txt"},
850 {"token": "#{token2}", "filename": "test2.txt"}
850 {"token": "#{token2}", "filename": "test2.txt"}
851 ]
851 ]
852 }
852 }
853 }
853 }
854 JSON
854 JSON
855
855
856 assert_difference 'Issue.count' do
856 assert_difference 'Issue.count' do
857 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
857 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
858 assert_response :created
858 assert_response :created
859 end
859 end
860 issue = Issue.order('id DESC').first
860 issue = Issue.order('id DESC').first
861 assert_equal 2, issue.attachments.count
861 assert_equal 2, issue.attachments.count
862 end
862 end
863
863
864 def test_update_issue_with_uploaded_file
864 def test_update_issue_with_uploaded_file
865 token = xml_upload('test_upload_with_upload', credentials('jsmith'))
865 token = xml_upload('test_upload_with_upload', credentials('jsmith'))
866 attachment = Attachment.find_by_token(token)
866 attachment = Attachment.find_by_token(token)
867
867
868 # update the issue with the upload's token
868 # update the issue with the upload's token
869 assert_difference 'Journal.count' do
869 assert_difference 'Journal.count' do
870 put '/issues/1.xml',
870 put '/issues/1.xml',
871 {:issue => {:notes => 'Attachment added',
871 {:issue => {:notes => 'Attachment added',
872 :uploads => [{:token => token, :filename => 'test.txt',
872 :uploads => [{:token => token, :filename => 'test.txt',
873 :content_type => 'text/plain'}]}},
873 :content_type => 'text/plain'}]}},
874 credentials('jsmith')
874 credentials('jsmith')
875 assert_response :ok
875 assert_response :ok
876 assert_equal '', @response.body
876 assert_equal '', @response.body
877 end
877 end
878
878
879 issue = Issue.find(1)
879 issue = Issue.find(1)
880 assert_include attachment, issue.attachments
880 assert_include attachment, issue.attachments
881 end
881 end
882 end
882 end
@@ -1,234 +1,234
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class AuthSourceLdapTest < ActiveSupport::TestCase
20 class AuthSourceLdapTest < ActiveSupport::TestCase
21 include Redmine::I18n
21 include Redmine::I18n
22 fixtures :auth_sources
22 fixtures :auth_sources
23
23
24 def setup
24 def setup
25 end
25 end
26
26
27 def test_initialize
27 def test_initialize
28 auth_source = AuthSourceLdap.new
28 auth_source = AuthSourceLdap.new
29 assert_nil auth_source.id
29 assert_nil auth_source.id
30 assert_equal "AuthSourceLdap", auth_source.type
30 assert_equal "AuthSourceLdap", auth_source.type
31 assert_equal "", auth_source.name
31 assert_equal "", auth_source.name
32 assert_nil auth_source.host
32 assert_nil auth_source.host
33 assert_nil auth_source.port
33 assert_nil auth_source.port
34 assert_nil auth_source.account
34 assert_nil auth_source.account
35 assert_equal "", auth_source.account_password
35 assert_equal "", auth_source.account_password
36 assert_nil auth_source.base_dn
36 assert_nil auth_source.base_dn
37 assert_nil auth_source.attr_login
37 assert_nil auth_source.attr_login
38 assert_nil auth_source.attr_firstname
38 assert_nil auth_source.attr_firstname
39 assert_nil auth_source.attr_lastname
39 assert_nil auth_source.attr_lastname
40 assert_nil auth_source.attr_mail
40 assert_nil auth_source.attr_mail
41 assert_equal false, auth_source.onthefly_register
41 assert_equal false, auth_source.onthefly_register
42 assert_equal false, auth_source.tls
42 assert_equal false, auth_source.tls
43 assert_nil auth_source.filter
43 assert_nil auth_source.filter
44 assert_nil auth_source.timeout
44 assert_nil auth_source.timeout
45 end
45 end
46
46
47 def test_create
47 def test_create
48 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName')
48 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName')
49 assert a.save
49 assert a.save
50 end
50 end
51
51
52 def test_should_strip_ldap_attributes
52 def test_should_strip_ldap_attributes
53 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
53 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
54 :attr_firstname => 'givenName ')
54 :attr_firstname => 'givenName ')
55 assert a.save
55 assert a.save
56 assert_equal 'givenName', a.reload.attr_firstname
56 assert_equal 'givenName', a.reload.attr_firstname
57 end
57 end
58
58
59 def test_replace_port_zero_to_389
59 def test_replace_port_zero_to_389
60 a = AuthSourceLdap.new(
60 a = AuthSourceLdap.new(
61 :name => 'My LDAP', :host => 'ldap.example.net', :port => 0,
61 :name => 'My LDAP', :host => 'ldap.example.net', :port => 0,
62 :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
62 :base_dn => 'dc=example,dc=net', :attr_login => 'sAMAccountName',
63 :attr_firstname => 'givenName ')
63 :attr_firstname => 'givenName ')
64 assert a.save
64 assert a.save
65 assert_equal 389, a.port
65 assert_equal 389, a.port
66 end
66 end
67
67
68 def test_filter_should_be_validated
68 def test_filter_should_be_validated
69 set_language_if_valid 'en'
69 set_language_if_valid 'en'
70
70
71 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :attr_login => 'sn')
71 a = AuthSourceLdap.new(:name => 'My LDAP', :host => 'ldap.example.net', :port => 389, :attr_login => 'sn')
72 a.filter = "(mail=*@redmine.org"
72 a.filter = "(mail=*@redmine.org"
73 assert !a.valid?
73 assert !a.valid?
74 assert_include "LDAP filter is invalid", a.errors.full_messages
74 assert_include "LDAP filter is invalid", a.errors.full_messages
75
75
76 a.filter = "(mail=*@redmine.org)"
76 a.filter = "(mail=*@redmine.org)"
77 assert a.valid?
77 assert a.valid?
78 end
78 end
79
79
80 if ldap_configured?
80 if ldap_configured?
81 test '#authenticate with a valid LDAP user should return the user attributes' do
81 test '#authenticate with a valid LDAP user should return the user attributes' do
82 auth = AuthSourceLdap.find(1)
82 auth = AuthSourceLdap.find(1)
83 auth.update_attribute :onthefly_register, true
83 auth.update_attribute :onthefly_register, true
84
84
85 attributes = auth.authenticate('example1','123456')
85 attributes = auth.authenticate('example1','123456')
86 assert attributes.is_a?(Hash), "An hash was not returned"
86 assert attributes.is_a?(Hash), "An hash was not returned"
87 assert_equal 'Example', attributes[:firstname]
87 assert_equal 'Example', attributes[:firstname]
88 assert_equal 'One', attributes[:lastname]
88 assert_equal 'One', attributes[:lastname]
89 assert_equal 'example1@redmine.org', attributes[:mail]
89 assert_equal 'example1@redmine.org', attributes[:mail]
90 assert_equal auth.id, attributes[:auth_source_id]
90 assert_equal auth.id, attributes[:auth_source_id]
91 attributes.keys.each do |attribute|
91 attributes.keys.each do |attribute|
92 assert User.new.respond_to?("#{attribute}="), "Unexpected :#{attribute} attribute returned"
92 assert User.new.respond_to?("#{attribute}="), "Unexpected :#{attribute} attribute returned"
93 end
93 end
94 end
94 end
95
95
96 test '#authenticate with an invalid LDAP user should return nil' do
96 test '#authenticate with an invalid LDAP user should return nil' do
97 auth = AuthSourceLdap.find(1)
97 auth = AuthSourceLdap.find(1)
98 assert_equal nil, auth.authenticate('nouser','123456')
98 assert_nil auth.authenticate('nouser','123456')
99 end
99 end
100
100
101 test '#authenticate without a login should return nil' do
101 test '#authenticate without a login should return nil' do
102 auth = AuthSourceLdap.find(1)
102 auth = AuthSourceLdap.find(1)
103 assert_equal nil, auth.authenticate('','123456')
103 assert_nil auth.authenticate('','123456')
104 end
104 end
105
105
106 test '#authenticate without a password should return nil' do
106 test '#authenticate without a password should return nil' do
107 auth = AuthSourceLdap.find(1)
107 auth = AuthSourceLdap.find(1)
108 assert_equal nil, auth.authenticate('edavis','')
108 assert_nil auth.authenticate('edavis','')
109 end
109 end
110
110
111 test '#authenticate without filter should return any user' do
111 test '#authenticate without filter should return any user' do
112 auth = AuthSourceLdap.find(1)
112 auth = AuthSourceLdap.find(1)
113 assert auth.authenticate('example1','123456')
113 assert auth.authenticate('example1','123456')
114 assert auth.authenticate('edavis', '123456')
114 assert auth.authenticate('edavis', '123456')
115 end
115 end
116
116
117 test '#authenticate with filter should return user who matches the filter only' do
117 test '#authenticate with filter should return user who matches the filter only' do
118 auth = AuthSourceLdap.find(1)
118 auth = AuthSourceLdap.find(1)
119 auth.filter = "(mail=*@redmine.org)"
119 auth.filter = "(mail=*@redmine.org)"
120
120
121 assert auth.authenticate('example1','123456')
121 assert auth.authenticate('example1','123456')
122 assert_nil auth.authenticate('edavis', '123456')
122 assert_nil auth.authenticate('edavis', '123456')
123 end
123 end
124
124
125 def test_authenticate_should_timeout
125 def test_authenticate_should_timeout
126 auth_source = AuthSourceLdap.find(1)
126 auth_source = AuthSourceLdap.find(1)
127 auth_source.timeout = 1
127 auth_source.timeout = 1
128 def auth_source.initialize_ldap_con(*args); sleep(5); end
128 def auth_source.initialize_ldap_con(*args); sleep(5); end
129
129
130 assert_raise AuthSourceTimeoutException do
130 assert_raise AuthSourceTimeoutException do
131 auth_source.authenticate 'example1', '123456'
131 auth_source.authenticate 'example1', '123456'
132 end
132 end
133 end
133 end
134
134
135 def test_search_should_return_matching_entries
135 def test_search_should_return_matching_entries
136 results = AuthSource.search("exa")
136 results = AuthSource.search("exa")
137 assert_equal 1, results.size
137 assert_equal 1, results.size
138 result = results.first
138 result = results.first
139 assert_kind_of Hash, result
139 assert_kind_of Hash, result
140 assert_equal "example1", result[:login]
140 assert_equal "example1", result[:login]
141 assert_equal "Example", result[:firstname]
141 assert_equal "Example", result[:firstname]
142 assert_equal "One", result[:lastname]
142 assert_equal "One", result[:lastname]
143 assert_equal "example1@redmine.org", result[:mail]
143 assert_equal "example1@redmine.org", result[:mail]
144 assert_equal 1, result[:auth_source_id]
144 assert_equal 1, result[:auth_source_id]
145 end
145 end
146
146
147 def test_search_with_no_match_should_return_an_empty_array
147 def test_search_with_no_match_should_return_an_empty_array
148 results = AuthSource.search("wro")
148 results = AuthSource.search("wro")
149 assert_equal [], results
149 assert_equal [], results
150 end
150 end
151
151
152 def test_search_with_exception_should_return_an_empty_array
152 def test_search_with_exception_should_return_an_empty_array
153 Net::LDAP.stubs(:new).raises(Net::LDAP::LdapError, 'Cannot connect')
153 Net::LDAP.stubs(:new).raises(Net::LDAP::LdapError, 'Cannot connect')
154
154
155 results = AuthSource.search("exa")
155 results = AuthSource.search("exa")
156 assert_equal [], results
156 assert_equal [], results
157 end
157 end
158
158
159 def test_test_connection_with_correct_host_and_port
159 def test_test_connection_with_correct_host_and_port
160 auth_source = AuthSourceLdap.find(1)
160 auth_source = AuthSourceLdap.find(1)
161
161
162 assert_nothing_raised Net::LDAP::Error do
162 assert_nothing_raised Net::LDAP::Error do
163 auth_source.test_connection
163 auth_source.test_connection
164 end
164 end
165 end
165 end
166
166
167 def test_test_connection_with_incorrect_host
167 def test_test_connection_with_incorrect_host
168 auth_source = AuthSourceLdap.find(1)
168 auth_source = AuthSourceLdap.find(1)
169 auth_source.host = "badhost"
169 auth_source.host = "badhost"
170 auth_source.save!
170 auth_source.save!
171
171
172 assert_raise Net::LDAP::Error do
172 assert_raise Net::LDAP::Error do
173 auth_source.test_connection
173 auth_source.test_connection
174 end
174 end
175 end
175 end
176
176
177 def test_test_connection_with_incorrect_port
177 def test_test_connection_with_incorrect_port
178 auth_source = AuthSourceLdap.find(1)
178 auth_source = AuthSourceLdap.find(1)
179 auth_source.port = 1234
179 auth_source.port = 1234
180 auth_source.save!
180 auth_source.save!
181
181
182 assert_raise Net::LDAP::Error do
182 assert_raise Net::LDAP::Error do
183 auth_source.test_connection
183 auth_source.test_connection
184 end
184 end
185 end
185 end
186
186
187 def test_test_connection_bind_with_account_and_password
187 def test_test_connection_bind_with_account_and_password
188 auth_source = AuthSourceLdap.find(1)
188 auth_source = AuthSourceLdap.find(1)
189 auth_source.account = "cn=admin,dc=redmine,dc=org"
189 auth_source.account = "cn=admin,dc=redmine,dc=org"
190 auth_source.account_password = "secret"
190 auth_source.account_password = "secret"
191 auth_source.save!
191 auth_source.save!
192
192
193 assert_equal "cn=admin,dc=redmine,dc=org", auth_source.account
193 assert_equal "cn=admin,dc=redmine,dc=org", auth_source.account
194 assert_equal "secret", auth_source.account_password
194 assert_equal "secret", auth_source.account_password
195 assert_nil auth_source.test_connection
195 assert_nil auth_source.test_connection
196 end
196 end
197
197
198 def test_test_connection_bind_without_account_and_password
198 def test_test_connection_bind_without_account_and_password
199 auth_source = AuthSourceLdap.find(1)
199 auth_source = AuthSourceLdap.find(1)
200
200
201 assert_nil auth_source.account
201 assert_nil auth_source.account
202 assert_equal "", auth_source.account_password
202 assert_equal "", auth_source.account_password
203 assert_nil auth_source.test_connection
203 assert_nil auth_source.test_connection
204 end
204 end
205
205
206 def test_test_connection_bind_with_incorrect_account
206 def test_test_connection_bind_with_incorrect_account
207 auth_source = AuthSourceLdap.find(1)
207 auth_source = AuthSourceLdap.find(1)
208 auth_source.account = "cn=baduser,dc=redmine,dc=org"
208 auth_source.account = "cn=baduser,dc=redmine,dc=org"
209 auth_source.account_password = "secret"
209 auth_source.account_password = "secret"
210 auth_source.save!
210 auth_source.save!
211
211
212 assert_equal "cn=baduser,dc=redmine,dc=org", auth_source.account
212 assert_equal "cn=baduser,dc=redmine,dc=org", auth_source.account
213 assert_equal "secret", auth_source.account_password
213 assert_equal "secret", auth_source.account_password
214 assert_raise AuthSourceException do
214 assert_raise AuthSourceException do
215 auth_source.test_connection
215 auth_source.test_connection
216 end
216 end
217 end
217 end
218
218
219 def test_test_connection_bind_with_incorrect_password
219 def test_test_connection_bind_with_incorrect_password
220 auth_source = AuthSourceLdap.find(1)
220 auth_source = AuthSourceLdap.find(1)
221 auth_source.account = "cn=admin,dc=redmine,dc=org"
221 auth_source.account = "cn=admin,dc=redmine,dc=org"
222 auth_source.account_password = "badpassword"
222 auth_source.account_password = "badpassword"
223 auth_source.save!
223 auth_source.save!
224
224
225 assert_equal "cn=admin,dc=redmine,dc=org", auth_source.account
225 assert_equal "cn=admin,dc=redmine,dc=org", auth_source.account
226 assert_equal "badpassword", auth_source.account_password
226 assert_equal "badpassword", auth_source.account_password
227 assert_raise AuthSourceException do
227 assert_raise AuthSourceException do
228 auth_source.test_connection
228 auth_source.test_connection
229 end
229 end
230 end
230 end
231 else
231 else
232 puts '(Test LDAP server not configured)'
232 puts '(Test LDAP server not configured)'
233 end
233 end
234 end
234 end
@@ -1,608 +1,608
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class ChangesetTest < ActiveSupport::TestCase
22 class ChangesetTest < ActiveSupport::TestCase
23 fixtures :projects, :repositories,
23 fixtures :projects, :repositories,
24 :issues, :issue_statuses, :issue_categories,
24 :issues, :issue_statuses, :issue_categories,
25 :journals, :journal_details,
25 :journals, :journal_details,
26 :workflows,
26 :workflows,
27 :changesets, :changes,
27 :changesets, :changes,
28 :enumerations,
28 :enumerations,
29 :custom_fields, :custom_values,
29 :custom_fields, :custom_values,
30 :users, :members, :member_roles,
30 :users, :members, :member_roles,
31 :email_addresses,
31 :email_addresses,
32 :trackers, :projects_trackers,
32 :trackers, :projects_trackers,
33 :enabled_modules, :roles
33 :enabled_modules, :roles
34
34
35 def test_ref_keywords_any
35 def test_ref_keywords_any
36 ActionMailer::Base.deliveries.clear
36 ActionMailer::Base.deliveries.clear
37 Setting.commit_ref_keywords = '*'
37 Setting.commit_ref_keywords = '*'
38 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => '5', 'done_ratio' => '90'}]
38 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => '5', 'done_ratio' => '90'}]
39
39
40 c = Changeset.new(:repository => Project.find(1).repository,
40 c = Changeset.new(:repository => Project.find(1).repository,
41 :committed_on => Time.now,
41 :committed_on => Time.now,
42 :comments => 'New commit (#2). Fixes #1',
42 :comments => 'New commit (#2). Fixes #1',
43 :revision => '12345')
43 :revision => '12345')
44 assert c.save
44 assert c.save
45 assert_equal [1, 2], c.issue_ids.sort
45 assert_equal [1, 2], c.issue_ids.sort
46 fixed = Issue.find(1)
46 fixed = Issue.find(1)
47 assert fixed.closed?
47 assert fixed.closed?
48 assert_equal 90, fixed.done_ratio
48 assert_equal 90, fixed.done_ratio
49 assert_equal 1, ActionMailer::Base.deliveries.size
49 assert_equal 1, ActionMailer::Base.deliveries.size
50 end
50 end
51
51
52 def test_ref_keywords
52 def test_ref_keywords
53 Setting.commit_ref_keywords = 'refs'
53 Setting.commit_ref_keywords = 'refs'
54 Setting.commit_update_keywords = ''
54 Setting.commit_update_keywords = ''
55 c = Changeset.new(:repository => Project.find(1).repository,
55 c = Changeset.new(:repository => Project.find(1).repository,
56 :committed_on => Time.now,
56 :committed_on => Time.now,
57 :comments => 'Ignores #2. Refs #1',
57 :comments => 'Ignores #2. Refs #1',
58 :revision => '12345')
58 :revision => '12345')
59 assert c.save
59 assert c.save
60 assert_equal [1], c.issue_ids.sort
60 assert_equal [1], c.issue_ids.sort
61 end
61 end
62
62
63 def test_ref_keywords_any_only
63 def test_ref_keywords_any_only
64 Setting.commit_ref_keywords = '*'
64 Setting.commit_ref_keywords = '*'
65 Setting.commit_update_keywords = ''
65 Setting.commit_update_keywords = ''
66 c = Changeset.new(:repository => Project.find(1).repository,
66 c = Changeset.new(:repository => Project.find(1).repository,
67 :committed_on => Time.now,
67 :committed_on => Time.now,
68 :comments => 'Ignores #2. Refs #1',
68 :comments => 'Ignores #2. Refs #1',
69 :revision => '12345')
69 :revision => '12345')
70 assert c.save
70 assert c.save
71 assert_equal [1, 2], c.issue_ids.sort
71 assert_equal [1, 2], c.issue_ids.sort
72 end
72 end
73
73
74 def test_ref_keywords_any_with_timelog
74 def test_ref_keywords_any_with_timelog
75 Setting.commit_ref_keywords = '*'
75 Setting.commit_ref_keywords = '*'
76 Setting.commit_logtime_enabled = '1'
76 Setting.commit_logtime_enabled = '1'
77
77
78 {
78 {
79 '2' => 2.0,
79 '2' => 2.0,
80 '2h' => 2.0,
80 '2h' => 2.0,
81 '2hours' => 2.0,
81 '2hours' => 2.0,
82 '15m' => 0.25,
82 '15m' => 0.25,
83 '15min' => 0.25,
83 '15min' => 0.25,
84 '3h15' => 3.25,
84 '3h15' => 3.25,
85 '3h15m' => 3.25,
85 '3h15m' => 3.25,
86 '3h15min' => 3.25,
86 '3h15min' => 3.25,
87 '3:15' => 3.25,
87 '3:15' => 3.25,
88 '3.25' => 3.25,
88 '3.25' => 3.25,
89 '3.25h' => 3.25,
89 '3.25h' => 3.25,
90 '3,25' => 3.25,
90 '3,25' => 3.25,
91 '3,25h' => 3.25,
91 '3,25h' => 3.25,
92 }.each do |syntax, expected_hours|
92 }.each do |syntax, expected_hours|
93 c = Changeset.new(:repository => Project.find(1).repository,
93 c = Changeset.new(:repository => Project.find(1).repository,
94 :committed_on => 24.hours.ago,
94 :committed_on => 24.hours.ago,
95 :comments => "Worked on this issue #1 @#{syntax}",
95 :comments => "Worked on this issue #1 @#{syntax}",
96 :revision => '520',
96 :revision => '520',
97 :user => User.find(2))
97 :user => User.find(2))
98 assert_difference 'TimeEntry.count' do
98 assert_difference 'TimeEntry.count' do
99 c.scan_comment_for_issue_ids
99 c.scan_comment_for_issue_ids
100 end
100 end
101 assert_equal [1], c.issue_ids.sort
101 assert_equal [1], c.issue_ids.sort
102
102
103 time = TimeEntry.order('id desc').first
103 time = TimeEntry.order('id desc').first
104 assert_equal 1, time.issue_id
104 assert_equal 1, time.issue_id
105 assert_equal 1, time.project_id
105 assert_equal 1, time.project_id
106 assert_equal 2, time.user_id
106 assert_equal 2, time.user_id
107 assert_equal expected_hours, time.hours,
107 assert_equal expected_hours, time.hours,
108 "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
108 "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
109 assert_equal Date.yesterday, time.spent_on
109 assert_equal Date.yesterday, time.spent_on
110 assert time.activity.is_default?
110 assert time.activity.is_default?
111 assert time.comments.include?('r520'),
111 assert time.comments.include?('r520'),
112 "r520 was expected in time_entry comments: #{time.comments}"
112 "r520 was expected in time_entry comments: #{time.comments}"
113 end
113 end
114 end
114 end
115
115
116 def test_ref_keywords_closing_with_timelog
116 def test_ref_keywords_closing_with_timelog
117 Setting.commit_ref_keywords = '*'
117 Setting.commit_ref_keywords = '*'
118 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes',
118 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes',
119 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s}]
119 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s}]
120 Setting.commit_logtime_enabled = '1'
120 Setting.commit_logtime_enabled = '1'
121
121
122 c = Changeset.new(:repository => Project.find(1).repository,
122 c = Changeset.new(:repository => Project.find(1).repository,
123 :committed_on => Time.now,
123 :committed_on => Time.now,
124 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
124 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
125 :user => User.find(2))
125 :user => User.find(2))
126 assert_difference 'TimeEntry.count', 2 do
126 assert_difference 'TimeEntry.count', 2 do
127 c.scan_comment_for_issue_ids
127 c.scan_comment_for_issue_ids
128 end
128 end
129
129
130 assert_equal [1, 2], c.issue_ids.sort
130 assert_equal [1, 2], c.issue_ids.sort
131 assert Issue.find(1).closed?
131 assert Issue.find(1).closed?
132 assert Issue.find(2).closed?
132 assert Issue.find(2).closed?
133
133
134 times = TimeEntry.order('id desc').limit(2)
134 times = TimeEntry.order('id desc').limit(2)
135 assert_equal [1, 2], times.collect(&:issue_id).sort
135 assert_equal [1, 2], times.collect(&:issue_id).sort
136 end
136 end
137
137
138 def test_ref_keywords_any_line_start
138 def test_ref_keywords_any_line_start
139 Setting.commit_ref_keywords = '*'
139 Setting.commit_ref_keywords = '*'
140 c = Changeset.new(:repository => Project.find(1).repository,
140 c = Changeset.new(:repository => Project.find(1).repository,
141 :committed_on => Time.now,
141 :committed_on => Time.now,
142 :comments => '#1 is the reason of this commit',
142 :comments => '#1 is the reason of this commit',
143 :revision => '12345')
143 :revision => '12345')
144 assert c.save
144 assert c.save
145 assert_equal [1], c.issue_ids.sort
145 assert_equal [1], c.issue_ids.sort
146 end
146 end
147
147
148 def test_ref_keywords_allow_brackets_around_a_issue_number
148 def test_ref_keywords_allow_brackets_around_a_issue_number
149 Setting.commit_ref_keywords = '*'
149 Setting.commit_ref_keywords = '*'
150 c = Changeset.new(:repository => Project.find(1).repository,
150 c = Changeset.new(:repository => Project.find(1).repository,
151 :committed_on => Time.now,
151 :committed_on => Time.now,
152 :comments => '[#1] Worked on this issue',
152 :comments => '[#1] Worked on this issue',
153 :revision => '12345')
153 :revision => '12345')
154 assert c.save
154 assert c.save
155 assert_equal [1], c.issue_ids.sort
155 assert_equal [1], c.issue_ids.sort
156 end
156 end
157
157
158 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
158 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
159 Setting.commit_ref_keywords = '*'
159 Setting.commit_ref_keywords = '*'
160 c = Changeset.new(:repository => Project.find(1).repository,
160 c = Changeset.new(:repository => Project.find(1).repository,
161 :committed_on => Time.now,
161 :committed_on => Time.now,
162 :comments => '[#1 #2, #3] Worked on these',
162 :comments => '[#1 #2, #3] Worked on these',
163 :revision => '12345')
163 :revision => '12345')
164 assert c.save
164 assert c.save
165 assert_equal [1,2,3], c.issue_ids.sort
165 assert_equal [1,2,3], c.issue_ids.sort
166 end
166 end
167
167
168 def test_ref_keywords_with_large_number_should_not_error
168 def test_ref_keywords_with_large_number_should_not_error
169 Setting.commit_ref_keywords = '*'
169 Setting.commit_ref_keywords = '*'
170 c = Changeset.new(:repository => Project.find(1).repository,
170 c = Changeset.new(:repository => Project.find(1).repository,
171 :committed_on => Time.now,
171 :committed_on => Time.now,
172 :comments => 'Out of range #2010021810000121',
172 :comments => 'Out of range #2010021810000121',
173 :revision => '12345')
173 :revision => '12345')
174 assert_nothing_raised do
174 assert_nothing_raised do
175 assert c.save
175 assert c.save
176 end
176 end
177 assert_equal [], c.issue_ids.sort
177 assert_equal [], c.issue_ids.sort
178 end
178 end
179
179
180 def test_update_keywords_with_changes_should_create_journal
180 def test_update_keywords_with_changes_should_create_journal
181 issue = Issue.generate!(:project_id => 1, :status_id => 1)
181 issue = Issue.generate!(:project_id => 1, :status_id => 1)
182
182
183 with_settings :commit_update_keywords => [{'keywords' => 'fixes', 'status_id' => '3'}] do
183 with_settings :commit_update_keywords => [{'keywords' => 'fixes', 'status_id' => '3'}] do
184 assert_difference 'Journal.count' do
184 assert_difference 'Journal.count' do
185 c = Changeset.generate!(:repository => Project.find(1).repository,:comments => "Fixes ##{issue.id}")
185 c = Changeset.generate!(:repository => Project.find(1).repository,:comments => "Fixes ##{issue.id}")
186 assert_include c.id, issue.reload.changeset_ids
186 assert_include c.id, issue.reload.changeset_ids
187 journal = Journal.order('id DESC').first
187 journal = Journal.order('id DESC').first
188 assert_equal 1, journal.details.count
188 assert_equal 1, journal.details.count
189 end
189 end
190 end
190 end
191 end
191 end
192
192
193 def test_update_keywords_without_change_should_not_create_journal
193 def test_update_keywords_without_change_should_not_create_journal
194 issue = Issue.generate!(:project_id => 1, :status_id => 3)
194 issue = Issue.generate!(:project_id => 1, :status_id => 3)
195
195
196 with_settings :commit_update_keywords => [{'keywords' => 'fixes', 'status_id' => '3'}] do
196 with_settings :commit_update_keywords => [{'keywords' => 'fixes', 'status_id' => '3'}] do
197 assert_no_difference 'Journal.count' do
197 assert_no_difference 'Journal.count' do
198 c = Changeset.generate!(:repository => Project.find(1).repository,:comments => "Fixes ##{issue.id}")
198 c = Changeset.generate!(:repository => Project.find(1).repository,:comments => "Fixes ##{issue.id}")
199 assert_include c.id, issue.reload.changeset_ids
199 assert_include c.id, issue.reload.changeset_ids
200 end
200 end
201 end
201 end
202 end
202 end
203
203
204 def test_update_keywords_with_multiple_rules
204 def test_update_keywords_with_multiple_rules
205 with_settings :commit_update_keywords => [
205 with_settings :commit_update_keywords => [
206 {'keywords' => 'fixes, closes', 'status_id' => '5'},
206 {'keywords' => 'fixes, closes', 'status_id' => '5'},
207 {'keywords' => 'resolves', 'status_id' => '3'}
207 {'keywords' => 'resolves', 'status_id' => '3'}
208 ] do
208 ] do
209
209
210 issue1 = Issue.generate!
210 issue1 = Issue.generate!
211 issue2 = Issue.generate!
211 issue2 = Issue.generate!
212 Changeset.generate!(:comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}")
212 Changeset.generate!(:comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}")
213 assert_equal 5, issue1.reload.status_id
213 assert_equal 5, issue1.reload.status_id
214 assert_equal 3, issue2.reload.status_id
214 assert_equal 3, issue2.reload.status_id
215 end
215 end
216 end
216 end
217
217
218 def test_update_keywords_with_multiple_rules_for_the_same_keyword_should_match_tracker
218 def test_update_keywords_with_multiple_rules_for_the_same_keyword_should_match_tracker
219 with_settings :commit_update_keywords => [
219 with_settings :commit_update_keywords => [
220 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
220 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
221 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => ''}
221 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => ''}
222 ] do
222 ] do
223
223
224 issue1 = Issue.generate!(:tracker_id => 2)
224 issue1 = Issue.generate!(:tracker_id => 2)
225 issue2 = Issue.generate!
225 issue2 = Issue.generate!
226 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
226 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
227 assert_equal 5, issue1.reload.status_id
227 assert_equal 5, issue1.reload.status_id
228 assert_equal 3, issue2.reload.status_id
228 assert_equal 3, issue2.reload.status_id
229 end
229 end
230 end
230 end
231
231
232 def test_update_keywords_with_multiple_rules_for_the_same_tracker_should_match_keyword
232 def test_update_keywords_with_multiple_rules_for_the_same_tracker_should_match_keyword
233 with_settings :commit_update_keywords => [
233 with_settings :commit_update_keywords => [
234 {'keywords' => 'Fixes, Closes', 'status_id' => '5', 'done_ratio' => '100', 'if_tracker_id' => '2'},
234 {'keywords' => 'Fixes, Closes', 'status_id' => '5', 'done_ratio' => '100', 'if_tracker_id' => '2'},
235 {'keywords' => 'Testing', 'status_id' => '3', 'done_ratio' => '90', 'if_tracker_id' => '2'}
235 {'keywords' => 'Testing', 'status_id' => '3', 'done_ratio' => '90', 'if_tracker_id' => '2'}
236 ] do
236 ] do
237
237
238 issue1 = Issue.generate!(:tracker_id => 2)
238 issue1 = Issue.generate!(:tracker_id => 2)
239 issue2 = Issue.generate!(:tracker_id => 2)
239 issue2 = Issue.generate!(:tracker_id => 2)
240 Changeset.generate!(:comments => "Testing ##{issue1.id}, Fixes ##{issue2.id}")
240 Changeset.generate!(:comments => "Testing ##{issue1.id}, Fixes ##{issue2.id}")
241 issue1.reload
241 issue1.reload
242 assert_equal 3, issue1.status_id
242 assert_equal 3, issue1.status_id
243 assert_equal 90, issue1.done_ratio
243 assert_equal 90, issue1.done_ratio
244 issue2.reload
244 issue2.reload
245 assert_equal 5, issue2.status_id
245 assert_equal 5, issue2.status_id
246 assert_equal 100, issue2.done_ratio
246 assert_equal 100, issue2.done_ratio
247 end
247 end
248 end
248 end
249
249
250 def test_update_keywords_with_multiple_rules_and_no_match
250 def test_update_keywords_with_multiple_rules_and_no_match
251 with_settings :commit_update_keywords => [
251 with_settings :commit_update_keywords => [
252 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
252 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
253 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => '3'}
253 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => '3'}
254 ] do
254 ] do
255
255
256 issue1 = Issue.generate!(:tracker_id => 2)
256 issue1 = Issue.generate!(:tracker_id => 2)
257 issue2 = Issue.generate!
257 issue2 = Issue.generate!
258 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
258 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
259 assert_equal 5, issue1.reload.status_id
259 assert_equal 5, issue1.reload.status_id
260 assert_equal 1, issue2.reload.status_id # no updates
260 assert_equal 1, issue2.reload.status_id # no updates
261 end
261 end
262 end
262 end
263
263
264 def test_commit_referencing_a_subproject_issue
264 def test_commit_referencing_a_subproject_issue
265 c = Changeset.new(:repository => Project.find(1).repository,
265 c = Changeset.new(:repository => Project.find(1).repository,
266 :committed_on => Time.now,
266 :committed_on => Time.now,
267 :comments => 'refs #5, a subproject issue',
267 :comments => 'refs #5, a subproject issue',
268 :revision => '12345')
268 :revision => '12345')
269 assert c.save
269 assert c.save
270 assert_equal [5], c.issue_ids.sort
270 assert_equal [5], c.issue_ids.sort
271 assert c.issues.first.project != c.project
271 assert c.issues.first.project != c.project
272 end
272 end
273
273
274 def test_commit_closing_a_subproject_issue
274 def test_commit_closing_a_subproject_issue
275 with_settings :commit_update_keywords => [{'keywords' => 'closes', 'status_id' => '5'}],
275 with_settings :commit_update_keywords => [{'keywords' => 'closes', 'status_id' => '5'}],
276 :default_language => 'en' do
276 :default_language => 'en' do
277 issue = Issue.find(5)
277 issue = Issue.find(5)
278 assert !issue.closed?
278 assert !issue.closed?
279 assert_difference 'Journal.count' do
279 assert_difference 'Journal.count' do
280 c = Changeset.new(:repository => Project.find(1).repository,
280 c = Changeset.new(:repository => Project.find(1).repository,
281 :committed_on => Time.now,
281 :committed_on => Time.now,
282 :comments => 'closes #5, a subproject issue',
282 :comments => 'closes #5, a subproject issue',
283 :revision => '12345')
283 :revision => '12345')
284 assert c.save
284 assert c.save
285 end
285 end
286 assert issue.reload.closed?
286 assert issue.reload.closed?
287 journal = Journal.order('id DESC').first
287 journal = Journal.order('id DESC').first
288 assert_equal issue, journal.issue
288 assert_equal issue, journal.issue
289 assert_include "Applied in changeset ecookbook:r12345.", journal.notes
289 assert_include "Applied in changeset ecookbook:r12345.", journal.notes
290 end
290 end
291 end
291 end
292
292
293 def test_commit_referencing_a_parent_project_issue
293 def test_commit_referencing_a_parent_project_issue
294 # repository of child project
294 # repository of child project
295 r = Repository::Subversion.create!(
295 r = Repository::Subversion.create!(
296 :project => Project.find(3),
296 :project => Project.find(3),
297 :url => 'svn://localhost/test')
297 :url => 'svn://localhost/test')
298 c = Changeset.new(:repository => r,
298 c = Changeset.new(:repository => r,
299 :committed_on => Time.now,
299 :committed_on => Time.now,
300 :comments => 'refs #2, an issue of a parent project',
300 :comments => 'refs #2, an issue of a parent project',
301 :revision => '12345')
301 :revision => '12345')
302 assert c.save
302 assert c.save
303 assert_equal [2], c.issue_ids.sort
303 assert_equal [2], c.issue_ids.sort
304 assert c.issues.first.project != c.project
304 assert c.issues.first.project != c.project
305 end
305 end
306
306
307 def test_commit_referencing_a_project_with_commit_cross_project_ref_disabled
307 def test_commit_referencing_a_project_with_commit_cross_project_ref_disabled
308 r = Repository::Subversion.create!(
308 r = Repository::Subversion.create!(
309 :project => Project.find(3),
309 :project => Project.find(3),
310 :url => 'svn://localhost/test')
310 :url => 'svn://localhost/test')
311 with_settings :commit_cross_project_ref => '0' do
311 with_settings :commit_cross_project_ref => '0' do
312 c = Changeset.new(:repository => r,
312 c = Changeset.new(:repository => r,
313 :committed_on => Time.now,
313 :committed_on => Time.now,
314 :comments => 'refs #4, an issue of a different project',
314 :comments => 'refs #4, an issue of a different project',
315 :revision => '12345')
315 :revision => '12345')
316 assert c.save
316 assert c.save
317 assert_equal [], c.issue_ids
317 assert_equal [], c.issue_ids
318 end
318 end
319 end
319 end
320
320
321 def test_commit_referencing_a_project_with_commit_cross_project_ref_enabled
321 def test_commit_referencing_a_project_with_commit_cross_project_ref_enabled
322 r = Repository::Subversion.create!(
322 r = Repository::Subversion.create!(
323 :project => Project.find(3),
323 :project => Project.find(3),
324 :url => 'svn://localhost/test')
324 :url => 'svn://localhost/test')
325 with_settings :commit_cross_project_ref => '1' do
325 with_settings :commit_cross_project_ref => '1' do
326 c = Changeset.new(:repository => r,
326 c = Changeset.new(:repository => r,
327 :committed_on => Time.now,
327 :committed_on => Time.now,
328 :comments => 'refs #4, an issue of a different project',
328 :comments => 'refs #4, an issue of a different project',
329 :revision => '12345')
329 :revision => '12345')
330 assert c.save
330 assert c.save
331 assert_equal [4], c.issue_ids
331 assert_equal [4], c.issue_ids
332 end
332 end
333 end
333 end
334
334
335 def test_old_commits_should_not_update_issues_nor_log_time
335 def test_old_commits_should_not_update_issues_nor_log_time
336 Setting.commit_ref_keywords = '*'
336 Setting.commit_ref_keywords = '*'
337 Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
337 Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
338 Setting.commit_logtime_enabled = '1'
338 Setting.commit_logtime_enabled = '1'
339
339
340 repository = Project.find(1).repository
340 repository = Project.find(1).repository
341 repository.created_on = Time.now
341 repository.created_on = Time.now
342 repository.save!
342 repository.save!
343
343
344 c = Changeset.new(:repository => repository,
344 c = Changeset.new(:repository => repository,
345 :committed_on => 1.month.ago,
345 :committed_on => 1.month.ago,
346 :comments => 'New commit (#2). Fixes #1 @1h',
346 :comments => 'New commit (#2). Fixes #1 @1h',
347 :revision => '12345')
347 :revision => '12345')
348 assert_no_difference 'TimeEntry.count' do
348 assert_no_difference 'TimeEntry.count' do
349 assert c.save
349 assert c.save
350 end
350 end
351 assert_equal [1, 2], c.issue_ids.sort
351 assert_equal [1, 2], c.issue_ids.sort
352 issue = Issue.find(1)
352 issue = Issue.find(1)
353 assert_equal 1, issue.status_id
353 assert_equal 1, issue.status_id
354 assert_equal 0, issue.done_ratio
354 assert_equal 0, issue.done_ratio
355 end
355 end
356
356
357 def test_2_repositories_with_same_backend_should_not_link_issue_multiple_times
357 def test_2_repositories_with_same_backend_should_not_link_issue_multiple_times
358 Setting.commit_ref_keywords = '*'
358 Setting.commit_ref_keywords = '*'
359 r1 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///svn1')
359 r1 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///svn1')
360 r2 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn2', :url => 'file:///svn1')
360 r2 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn2', :url => 'file:///svn1')
361 now = Time.now
361 now = Time.now
362 assert_difference 'Issue.find(1).changesets.count' do
362 assert_difference 'Issue.find(1).changesets.count' do
363 c1 = Changeset.create!(:repository => r1, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
363 c1 = Changeset.create!(:repository => r1, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
364 c1 = Changeset.create!(:repository => r2, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
364 c1 = Changeset.create!(:repository => r2, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
365 end
365 end
366 end
366 end
367
367
368 def test_text_tag_revision
368 def test_text_tag_revision
369 c = Changeset.new(:revision => '520')
369 c = Changeset.new(:revision => '520')
370 assert_equal 'r520', c.text_tag
370 assert_equal 'r520', c.text_tag
371 end
371 end
372
372
373 def test_text_tag_revision_with_same_project
373 def test_text_tag_revision_with_same_project
374 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
374 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
375 assert_equal 'r520', c.text_tag(Project.find(1))
375 assert_equal 'r520', c.text_tag(Project.find(1))
376 end
376 end
377
377
378 def test_text_tag_revision_with_different_project
378 def test_text_tag_revision_with_different_project
379 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
379 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
380 assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
380 assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
381 end
381 end
382
382
383 def test_text_tag_revision_with_repository_identifier
383 def test_text_tag_revision_with_repository_identifier
384 r = Repository::Subversion.create!(
384 r = Repository::Subversion.create!(
385 :project_id => 1,
385 :project_id => 1,
386 :url => 'svn://localhost/test',
386 :url => 'svn://localhost/test',
387 :identifier => 'documents')
387 :identifier => 'documents')
388 c = Changeset.new(:revision => '520', :repository => r)
388 c = Changeset.new(:revision => '520', :repository => r)
389 assert_equal 'documents|r520', c.text_tag
389 assert_equal 'documents|r520', c.text_tag
390 assert_equal 'ecookbook:documents|r520', c.text_tag(Project.find(2))
390 assert_equal 'ecookbook:documents|r520', c.text_tag(Project.find(2))
391 end
391 end
392
392
393 def test_text_tag_hash
393 def test_text_tag_hash
394 c = Changeset.new(
394 c = Changeset.new(
395 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
395 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
396 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
396 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
397 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
397 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
398 end
398 end
399
399
400 def test_text_tag_hash_with_same_project
400 def test_text_tag_hash_with_same_project
401 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
401 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
402 assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
402 assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
403 end
403 end
404
404
405 def test_text_tag_hash_with_different_project
405 def test_text_tag_hash_with_different_project
406 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
406 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
407 assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
407 assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
408 end
408 end
409
409
410 def test_text_tag_hash_all_number
410 def test_text_tag_hash_all_number
411 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
411 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
412 assert_equal 'commit:0123456789', c.text_tag
412 assert_equal 'commit:0123456789', c.text_tag
413 end
413 end
414
414
415 def test_text_tag_hash_with_repository_identifier
415 def test_text_tag_hash_with_repository_identifier
416 r = Repository::Subversion.new(
416 r = Repository::Subversion.new(
417 :project_id => 1,
417 :project_id => 1,
418 :url => 'svn://localhost/test',
418 :url => 'svn://localhost/test',
419 :identifier => 'documents')
419 :identifier => 'documents')
420 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => r)
420 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => r)
421 assert_equal 'commit:documents|7234cb27', c.text_tag
421 assert_equal 'commit:documents|7234cb27', c.text_tag
422 assert_equal 'ecookbook:commit:documents|7234cb27', c.text_tag(Project.find(2))
422 assert_equal 'ecookbook:commit:documents|7234cb27', c.text_tag(Project.find(2))
423 end
423 end
424
424
425 def test_previous
425 def test_previous
426 changeset = Changeset.find_by_revision('3')
426 changeset = Changeset.find_by_revision('3')
427 assert_equal Changeset.find_by_revision('2'), changeset.previous
427 assert_equal Changeset.find_by_revision('2'), changeset.previous
428 end
428 end
429
429
430 def test_previous_nil
430 def test_previous_nil
431 changeset = Changeset.find_by_revision('1')
431 changeset = Changeset.find_by_revision('1')
432 assert_nil changeset.previous
432 assert_nil changeset.previous
433 end
433 end
434
434
435 def test_next
435 def test_next
436 changeset = Changeset.find_by_revision('2')
436 changeset = Changeset.find_by_revision('2')
437 assert_equal Changeset.find_by_revision('3'), changeset.next
437 assert_equal Changeset.find_by_revision('3'), changeset.next
438 end
438 end
439
439
440 def test_next_nil
440 def test_next_nil
441 changeset = Changeset.find_by_revision('10')
441 changeset = Changeset.find_by_revision('10')
442 assert_nil changeset.next
442 assert_nil changeset.next
443 end
443 end
444
444
445 def test_comments_should_be_converted_to_utf8
445 def test_comments_should_be_converted_to_utf8
446 proj = Project.find(3)
446 proj = Project.find(3)
447 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
447 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
448 str = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
448 str = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
449 r = Repository::Bazaar.create!(
449 r = Repository::Bazaar.create!(
450 :project => proj,
450 :project => proj,
451 :url => '/tmp/test/bazaar',
451 :url => '/tmp/test/bazaar',
452 :log_encoding => 'ISO-8859-1' )
452 :log_encoding => 'ISO-8859-1' )
453 assert r
453 assert r
454 c = Changeset.new(:repository => r,
454 c = Changeset.new(:repository => r,
455 :committed_on => Time.now,
455 :committed_on => Time.now,
456 :revision => '123',
456 :revision => '123',
457 :scmid => '12345',
457 :scmid => '12345',
458 :comments => str)
458 :comments => str)
459 assert( c.save )
459 assert( c.save )
460 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1.".force_encoding("UTF-8")
460 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1.".force_encoding("UTF-8")
461 assert_equal str_utf8, c.comments
461 assert_equal str_utf8, c.comments
462 end
462 end
463
463
464 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
464 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
465 proj = Project.find(3)
465 proj = Project.find(3)
466 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
466 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
467 str1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("UTF-8")
467 str1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("UTF-8")
468 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
468 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
469 r = Repository::Bazaar.create!(
469 r = Repository::Bazaar.create!(
470 :project => proj,
470 :project => proj,
471 :url => '/tmp/test/bazaar',
471 :url => '/tmp/test/bazaar',
472 :log_encoding => 'UTF-8' )
472 :log_encoding => 'UTF-8' )
473 assert r
473 assert r
474 c = Changeset.new(:repository => r,
474 c = Changeset.new(:repository => r,
475 :committed_on => Time.now,
475 :committed_on => Time.now,
476 :revision => '123',
476 :revision => '123',
477 :scmid => '12345',
477 :scmid => '12345',
478 :comments => str1,
478 :comments => str1,
479 :committer => str2)
479 :committer => str2)
480 assert( c.save )
480 assert( c.save )
481 assert_equal "Texte encod? en ISO-8859-1.", c.comments
481 assert_equal "Texte encod? en ISO-8859-1.", c.comments
482 assert_equal "?a?b?c?d?e test", c.committer
482 assert_equal "?a?b?c?d?e test", c.committer
483 end
483 end
484
484
485 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
485 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
486 proj = Project.find(3)
486 proj = Project.find(3)
487 str = "test\xb5\xfetest\xb5\xfe".force_encoding('ASCII-8BIT')
487 str = "test\xb5\xfetest\xb5\xfe".force_encoding('ASCII-8BIT')
488 r = Repository::Bazaar.create!(
488 r = Repository::Bazaar.create!(
489 :project => proj,
489 :project => proj,
490 :url => '/tmp/test/bazaar',
490 :url => '/tmp/test/bazaar',
491 :log_encoding => 'ISO-2022-JP' )
491 :log_encoding => 'ISO-2022-JP' )
492 assert r
492 assert r
493 c = Changeset.new(:repository => r,
493 c = Changeset.new(:repository => r,
494 :committed_on => Time.now,
494 :committed_on => Time.now,
495 :revision => '123',
495 :revision => '123',
496 :scmid => '12345',
496 :scmid => '12345',
497 :comments => str)
497 :comments => str)
498 assert( c.save )
498 assert( c.save )
499 assert_equal "test??test??", c.comments
499 assert_equal "test??test??", c.comments
500 end
500 end
501
501
502 def test_comments_should_be_converted_all_latin1_to_utf8
502 def test_comments_should_be_converted_all_latin1_to_utf8
503 s1 = "\xC2\x80"
503 s1 = "\xC2\x80"
504 s2 = "\xc3\x82\xc2\x80"
504 s2 = "\xc3\x82\xc2\x80"
505 s4 = s2.dup
505 s4 = s2.dup
506 s3 = s1.dup
506 s3 = s1.dup
507 s1.force_encoding('ASCII-8BIT')
507 s1.force_encoding('ASCII-8BIT')
508 s2.force_encoding('ASCII-8BIT')
508 s2.force_encoding('ASCII-8BIT')
509 s3.force_encoding('ISO-8859-1')
509 s3.force_encoding('ISO-8859-1')
510 s4.force_encoding('UTF-8')
510 s4.force_encoding('UTF-8')
511 assert_equal s3.encode('UTF-8'), s4
511 assert_equal s3.encode('UTF-8'), s4
512 proj = Project.find(3)
512 proj = Project.find(3)
513 r = Repository::Bazaar.create!(
513 r = Repository::Bazaar.create!(
514 :project => proj,
514 :project => proj,
515 :url => '/tmp/test/bazaar',
515 :url => '/tmp/test/bazaar',
516 :log_encoding => 'ISO-8859-1' )
516 :log_encoding => 'ISO-8859-1' )
517 assert r
517 assert r
518 c = Changeset.new(:repository => r,
518 c = Changeset.new(:repository => r,
519 :committed_on => Time.now,
519 :committed_on => Time.now,
520 :revision => '123',
520 :revision => '123',
521 :scmid => '12345',
521 :scmid => '12345',
522 :comments => s1)
522 :comments => s1)
523 assert( c.save )
523 assert( c.save )
524 assert_equal s4, c.comments
524 assert_equal s4, c.comments
525 end
525 end
526
526
527 def test_invalid_utf8_sequences_in_paths_should_be_replaced
527 def test_invalid_utf8_sequences_in_paths_should_be_replaced
528 proj = Project.find(3)
528 proj = Project.find(3)
529 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
529 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
530 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
530 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
531 r = Repository::Bazaar.create!(
531 r = Repository::Bazaar.create!(
532 :project => proj,
532 :project => proj,
533 :url => '/tmp/test/bazaar',
533 :url => '/tmp/test/bazaar',
534 :log_encoding => 'UTF-8' )
534 :log_encoding => 'UTF-8' )
535 assert r
535 assert r
536 cs = Changeset.new(
536 cs = Changeset.new(
537 :repository => r,
537 :repository => r,
538 :committed_on => Time.now,
538 :committed_on => Time.now,
539 :revision => '123',
539 :revision => '123',
540 :scmid => '12345',
540 :scmid => '12345',
541 :comments => "test")
541 :comments => "test")
542 assert(cs.save)
542 assert(cs.save)
543 ch = Change.new(
543 ch = Change.new(
544 :changeset => cs,
544 :changeset => cs,
545 :action => "A",
545 :action => "A",
546 :path => str1,
546 :path => str1,
547 :from_path => str2,
547 :from_path => str2,
548 :from_revision => "345")
548 :from_revision => "345")
549 assert(ch.save)
549 assert(ch.save)
550 assert_equal "Texte encod? en ISO-8859-1", ch.path
550 assert_equal "Texte encod? en ISO-8859-1", ch.path
551 assert_equal "?a?b?c?d?e test", ch.from_path
551 assert_equal "?a?b?c?d?e test", ch.from_path
552 end
552 end
553
553
554 def test_comments_nil
554 def test_comments_nil
555 proj = Project.find(3)
555 proj = Project.find(3)
556 r = Repository::Bazaar.create!(
556 r = Repository::Bazaar.create!(
557 :project => proj,
557 :project => proj,
558 :url => '/tmp/test/bazaar',
558 :url => '/tmp/test/bazaar',
559 :log_encoding => 'ISO-8859-1' )
559 :log_encoding => 'ISO-8859-1' )
560 assert r
560 assert r
561 c = Changeset.new(:repository => r,
561 c = Changeset.new(:repository => r,
562 :committed_on => Time.now,
562 :committed_on => Time.now,
563 :revision => '123',
563 :revision => '123',
564 :scmid => '12345',
564 :scmid => '12345',
565 :comments => nil,
565 :comments => nil,
566 :committer => nil)
566 :committer => nil)
567 assert( c.save )
567 assert( c.save )
568 assert_equal "", c.comments
568 assert_equal "", c.comments
569 assert_equal nil, c.committer
569 assert_nil c.committer
570 assert_equal "UTF-8", c.comments.encoding.to_s
570 assert_equal "UTF-8", c.comments.encoding.to_s
571 end
571 end
572
572
573 def test_comments_empty
573 def test_comments_empty
574 proj = Project.find(3)
574 proj = Project.find(3)
575 r = Repository::Bazaar.create!(
575 r = Repository::Bazaar.create!(
576 :project => proj,
576 :project => proj,
577 :url => '/tmp/test/bazaar',
577 :url => '/tmp/test/bazaar',
578 :log_encoding => 'ISO-8859-1' )
578 :log_encoding => 'ISO-8859-1' )
579 assert r
579 assert r
580 c = Changeset.new(:repository => r,
580 c = Changeset.new(:repository => r,
581 :committed_on => Time.now,
581 :committed_on => Time.now,
582 :revision => '123',
582 :revision => '123',
583 :scmid => '12345',
583 :scmid => '12345',
584 :comments => "",
584 :comments => "",
585 :committer => "")
585 :committer => "")
586 assert( c.save )
586 assert( c.save )
587 assert_equal "", c.comments
587 assert_equal "", c.comments
588 assert_equal "", c.committer
588 assert_equal "", c.committer
589 assert_equal "UTF-8", c.comments.encoding.to_s
589 assert_equal "UTF-8", c.comments.encoding.to_s
590 assert_equal "UTF-8", c.committer.encoding.to_s
590 assert_equal "UTF-8", c.committer.encoding.to_s
591 end
591 end
592
592
593 def test_comments_should_accept_more_than_64k
593 def test_comments_should_accept_more_than_64k
594 c = Changeset.new(:repository => Repository.first,
594 c = Changeset.new(:repository => Repository.first,
595 :committed_on => Time.now,
595 :committed_on => Time.now,
596 :revision => '123',
596 :revision => '123',
597 :scmid => '12345',
597 :scmid => '12345',
598 :comments => "a" * 500.kilobyte)
598 :comments => "a" * 500.kilobyte)
599 assert c.save
599 assert c.save
600 c.reload
600 c.reload
601 assert_equal 500.kilobyte, c.comments.size
601 assert_equal 500.kilobyte, c.comments.size
602 end
602 end
603
603
604 def test_identifier
604 def test_identifier
605 c = Changeset.find_by_revision('1')
605 c = Changeset.find_by_revision('1')
606 assert_equal c.revision, c.identifier
606 assert_equal c.revision, c.identifier
607 end
607 end
608 end
608 end
@@ -1,335 +1,335
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldTest < ActiveSupport::TestCase
20 class CustomFieldTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :roles, :projects,
21 fixtures :custom_fields, :roles, :projects,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :issues
23 :issues
24
24
25 def test_create
25 def test_create
26 field = UserCustomField.new(:name => 'Money money money', :field_format => 'float')
26 field = UserCustomField.new(:name => 'Money money money', :field_format => 'float')
27 assert field.save
27 assert field.save
28 end
28 end
29
29
30 def test_before_validation
30 def test_before_validation
31 field = CustomField.new(:name => 'test_before_validation', :field_format => 'int')
31 field = CustomField.new(:name => 'test_before_validation', :field_format => 'int')
32 field.searchable = true
32 field.searchable = true
33 assert field.save
33 assert field.save
34 assert_equal false, field.searchable
34 assert_equal false, field.searchable
35 field.searchable = true
35 field.searchable = true
36 assert field.save
36 assert field.save
37 assert_equal false, field.searchable
37 assert_equal false, field.searchable
38 end
38 end
39
39
40 def test_regexp_validation
40 def test_regexp_validation
41 field = IssueCustomField.new(:name => 'regexp', :field_format => 'text', :regexp => '[a-z0-9')
41 field = IssueCustomField.new(:name => 'regexp', :field_format => 'text', :regexp => '[a-z0-9')
42 assert !field.save
42 assert !field.save
43 assert_include I18n.t('activerecord.errors.messages.invalid'),
43 assert_include I18n.t('activerecord.errors.messages.invalid'),
44 field.errors[:regexp]
44 field.errors[:regexp]
45 field.regexp = '[a-z0-9]'
45 field.regexp = '[a-z0-9]'
46 assert field.save
46 assert field.save
47 end
47 end
48
48
49 def test_default_value_should_be_validated
49 def test_default_value_should_be_validated
50 field = CustomField.new(:name => 'Test', :field_format => 'int')
50 field = CustomField.new(:name => 'Test', :field_format => 'int')
51 field.default_value = 'abc'
51 field.default_value = 'abc'
52 assert !field.valid?
52 assert !field.valid?
53 field.default_value = '6'
53 field.default_value = '6'
54 assert field.valid?
54 assert field.valid?
55 end
55 end
56
56
57 def test_default_value_should_not_be_validated_when_blank
57 def test_default_value_should_not_be_validated_when_blank
58 field = CustomField.new(:name => 'Test', :field_format => 'list', :possible_values => ['a', 'b'], :is_required => true, :default_value => '')
58 field = CustomField.new(:name => 'Test', :field_format => 'list', :possible_values => ['a', 'b'], :is_required => true, :default_value => '')
59 assert field.valid?
59 assert field.valid?
60 end
60 end
61
61
62 def test_field_format_should_be_validated
62 def test_field_format_should_be_validated
63 field = CustomField.new(:name => 'Test', :field_format => 'foo')
63 field = CustomField.new(:name => 'Test', :field_format => 'foo')
64 assert !field.valid?
64 assert !field.valid?
65 end
65 end
66
66
67 def test_field_format_validation_should_accept_formats_added_at_runtime
67 def test_field_format_validation_should_accept_formats_added_at_runtime
68 Redmine::FieldFormat.add 'foobar', Class.new(Redmine::FieldFormat::Base)
68 Redmine::FieldFormat.add 'foobar', Class.new(Redmine::FieldFormat::Base)
69
69
70 field = CustomField.new(:name => 'Some Custom Field', :field_format => 'foobar')
70 field = CustomField.new(:name => 'Some Custom Field', :field_format => 'foobar')
71 assert field.valid?, 'field should be valid'
71 assert field.valid?, 'field should be valid'
72 ensure
72 ensure
73 Redmine::FieldFormat.delete 'foobar'
73 Redmine::FieldFormat.delete 'foobar'
74 end
74 end
75
75
76 def test_should_not_change_field_format_of_existing_custom_field
76 def test_should_not_change_field_format_of_existing_custom_field
77 field = CustomField.find(1)
77 field = CustomField.find(1)
78 field.field_format = 'int'
78 field.field_format = 'int'
79 assert_equal 'list', field.field_format
79 assert_equal 'list', field.field_format
80 end
80 end
81
81
82 def test_possible_values_should_accept_an_array
82 def test_possible_values_should_accept_an_array
83 field = CustomField.new
83 field = CustomField.new
84 field.possible_values = ["One value", ""]
84 field.possible_values = ["One value", ""]
85 assert_equal ["One value"], field.possible_values
85 assert_equal ["One value"], field.possible_values
86 end
86 end
87
87
88 def test_possible_values_should_stringify_values
88 def test_possible_values_should_stringify_values
89 field = CustomField.new
89 field = CustomField.new
90 field.possible_values = [1, 2]
90 field.possible_values = [1, 2]
91 assert_equal ["1", "2"], field.possible_values
91 assert_equal ["1", "2"], field.possible_values
92 end
92 end
93
93
94 def test_possible_values_should_accept_a_string
94 def test_possible_values_should_accept_a_string
95 field = CustomField.new
95 field = CustomField.new
96 field.possible_values = "One value"
96 field.possible_values = "One value"
97 assert_equal ["One value"], field.possible_values
97 assert_equal ["One value"], field.possible_values
98 end
98 end
99
99
100 def test_possible_values_should_return_utf8_encoded_strings
100 def test_possible_values_should_return_utf8_encoded_strings
101 field = CustomField.new
101 field = CustomField.new
102 s = "Value".force_encoding('BINARY')
102 s = "Value".force_encoding('BINARY')
103 field.possible_values = s
103 field.possible_values = s
104 assert_equal [s], field.possible_values
104 assert_equal [s], field.possible_values
105 assert_equal 'UTF-8', field.possible_values.first.encoding.name
105 assert_equal 'UTF-8', field.possible_values.first.encoding.name
106 end
106 end
107
107
108 def test_possible_values_should_accept_a_multiline_string
108 def test_possible_values_should_accept_a_multiline_string
109 field = CustomField.new
109 field = CustomField.new
110 field.possible_values = "One value\nAnd another one \r\n \n"
110 field.possible_values = "One value\nAnd another one \r\n \n"
111 assert_equal ["One value", "And another one"], field.possible_values
111 assert_equal ["One value", "And another one"], field.possible_values
112 end
112 end
113
113
114 def test_possible_values_stored_as_binary_should_be_utf8_encoded
114 def test_possible_values_stored_as_binary_should_be_utf8_encoded
115 field = CustomField.find(11)
115 field = CustomField.find(11)
116 assert_kind_of Array, field.possible_values
116 assert_kind_of Array, field.possible_values
117 assert field.possible_values.size > 0
117 assert field.possible_values.size > 0
118 field.possible_values.each do |value|
118 field.possible_values.each do |value|
119 assert_equal "UTF-8", value.encoding.name
119 assert_equal "UTF-8", value.encoding.name
120 end
120 end
121 end
121 end
122
122
123 def test_destroy
123 def test_destroy
124 field = CustomField.find(1)
124 field = CustomField.find(1)
125 assert field.destroy
125 assert field.destroy
126 end
126 end
127
127
128 def test_new_subclass_instance_should_return_an_instance
128 def test_new_subclass_instance_should_return_an_instance
129 f = CustomField.new_subclass_instance('IssueCustomField')
129 f = CustomField.new_subclass_instance('IssueCustomField')
130 assert_kind_of IssueCustomField, f
130 assert_kind_of IssueCustomField, f
131 end
131 end
132
132
133 def test_new_subclass_instance_should_set_attributes
133 def test_new_subclass_instance_should_set_attributes
134 f = CustomField.new_subclass_instance('IssueCustomField', :name => 'Test')
134 f = CustomField.new_subclass_instance('IssueCustomField', :name => 'Test')
135 assert_kind_of IssueCustomField, f
135 assert_kind_of IssueCustomField, f
136 assert_equal 'Test', f.name
136 assert_equal 'Test', f.name
137 end
137 end
138
138
139 def test_new_subclass_instance_with_invalid_class_name_should_return_nil
139 def test_new_subclass_instance_with_invalid_class_name_should_return_nil
140 assert_nil CustomField.new_subclass_instance('WrongClassName')
140 assert_nil CustomField.new_subclass_instance('WrongClassName')
141 end
141 end
142
142
143 def test_new_subclass_instance_with_non_subclass_name_should_return_nil
143 def test_new_subclass_instance_with_non_subclass_name_should_return_nil
144 assert_nil CustomField.new_subclass_instance('Project')
144 assert_nil CustomField.new_subclass_instance('Project')
145 end
145 end
146
146
147 def test_string_field_validation_with_blank_value
147 def test_string_field_validation_with_blank_value
148 f = CustomField.new(:field_format => 'string')
148 f = CustomField.new(:field_format => 'string')
149
149
150 assert f.valid_field_value?(nil)
150 assert f.valid_field_value?(nil)
151 assert f.valid_field_value?('')
151 assert f.valid_field_value?('')
152
152
153 f.is_required = true
153 f.is_required = true
154 assert !f.valid_field_value?(nil)
154 assert !f.valid_field_value?(nil)
155 assert !f.valid_field_value?('')
155 assert !f.valid_field_value?('')
156 end
156 end
157
157
158 def test_string_field_validation_with_min_and_max_lengths
158 def test_string_field_validation_with_min_and_max_lengths
159 f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5)
159 f = CustomField.new(:field_format => 'string', :min_length => 2, :max_length => 5)
160
160
161 assert f.valid_field_value?(nil)
161 assert f.valid_field_value?(nil)
162 assert f.valid_field_value?('')
162 assert f.valid_field_value?('')
163 assert !f.valid_field_value?(' ')
163 assert !f.valid_field_value?(' ')
164 assert f.valid_field_value?('a' * 2)
164 assert f.valid_field_value?('a' * 2)
165 assert !f.valid_field_value?('a')
165 assert !f.valid_field_value?('a')
166 assert !f.valid_field_value?('a' * 6)
166 assert !f.valid_field_value?('a' * 6)
167 end
167 end
168
168
169 def test_string_field_validation_with_regexp
169 def test_string_field_validation_with_regexp
170 f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$')
170 f = CustomField.new(:field_format => 'string', :regexp => '^[A-Z0-9]*$')
171
171
172 assert f.valid_field_value?(nil)
172 assert f.valid_field_value?(nil)
173 assert f.valid_field_value?('')
173 assert f.valid_field_value?('')
174 assert !f.valid_field_value?(' ')
174 assert !f.valid_field_value?(' ')
175 assert f.valid_field_value?('ABC')
175 assert f.valid_field_value?('ABC')
176 assert !f.valid_field_value?('abc')
176 assert !f.valid_field_value?('abc')
177 end
177 end
178
178
179 def test_date_field_validation
179 def test_date_field_validation
180 f = CustomField.new(:field_format => 'date')
180 f = CustomField.new(:field_format => 'date')
181
181
182 assert f.valid_field_value?(nil)
182 assert f.valid_field_value?(nil)
183 assert f.valid_field_value?('')
183 assert f.valid_field_value?('')
184 assert !f.valid_field_value?(' ')
184 assert !f.valid_field_value?(' ')
185 assert f.valid_field_value?('1975-07-14')
185 assert f.valid_field_value?('1975-07-14')
186 assert !f.valid_field_value?('1975-07-33')
186 assert !f.valid_field_value?('1975-07-33')
187 assert !f.valid_field_value?('abc')
187 assert !f.valid_field_value?('abc')
188 end
188 end
189
189
190 def test_list_field_validation
190 def test_list_field_validation
191 f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2'])
191 f = CustomField.new(:field_format => 'list', :possible_values => ['value1', 'value2'])
192
192
193 assert f.valid_field_value?(nil)
193 assert f.valid_field_value?(nil)
194 assert f.valid_field_value?('')
194 assert f.valid_field_value?('')
195 assert !f.valid_field_value?(' ')
195 assert !f.valid_field_value?(' ')
196 assert f.valid_field_value?('value2')
196 assert f.valid_field_value?('value2')
197 assert !f.valid_field_value?('abc')
197 assert !f.valid_field_value?('abc')
198 end
198 end
199
199
200 def test_int_field_validation
200 def test_int_field_validation
201 f = CustomField.new(:field_format => 'int')
201 f = CustomField.new(:field_format => 'int')
202
202
203 assert f.valid_field_value?(nil)
203 assert f.valid_field_value?(nil)
204 assert f.valid_field_value?('')
204 assert f.valid_field_value?('')
205 assert !f.valid_field_value?(' ')
205 assert !f.valid_field_value?(' ')
206 assert f.valid_field_value?('123')
206 assert f.valid_field_value?('123')
207 assert f.valid_field_value?('+123')
207 assert f.valid_field_value?('+123')
208 assert f.valid_field_value?('-123')
208 assert f.valid_field_value?('-123')
209 assert !f.valid_field_value?('6abc')
209 assert !f.valid_field_value?('6abc')
210 assert f.valid_field_value?(123)
210 assert f.valid_field_value?(123)
211 end
211 end
212
212
213 def test_float_field_validation
213 def test_float_field_validation
214 f = CustomField.new(:field_format => 'float')
214 f = CustomField.new(:field_format => 'float')
215
215
216 assert f.valid_field_value?(nil)
216 assert f.valid_field_value?(nil)
217 assert f.valid_field_value?('')
217 assert f.valid_field_value?('')
218 assert !f.valid_field_value?(' ')
218 assert !f.valid_field_value?(' ')
219 assert f.valid_field_value?('11.2')
219 assert f.valid_field_value?('11.2')
220 assert f.valid_field_value?('-6.250')
220 assert f.valid_field_value?('-6.250')
221 assert f.valid_field_value?('5')
221 assert f.valid_field_value?('5')
222 assert !f.valid_field_value?('6abc')
222 assert !f.valid_field_value?('6abc')
223 assert f.valid_field_value?(11.2)
223 assert f.valid_field_value?(11.2)
224 end
224 end
225
225
226 def test_multi_field_validation
226 def test_multi_field_validation
227 f = CustomField.new(:field_format => 'list', :multiple => 'true', :possible_values => ['value1', 'value2'])
227 f = CustomField.new(:field_format => 'list', :multiple => 'true', :possible_values => ['value1', 'value2'])
228
228
229 assert f.valid_field_value?(nil)
229 assert f.valid_field_value?(nil)
230 assert f.valid_field_value?('')
230 assert f.valid_field_value?('')
231 assert !f.valid_field_value?(' ')
231 assert !f.valid_field_value?(' ')
232 assert f.valid_field_value?([])
232 assert f.valid_field_value?([])
233 assert f.valid_field_value?([nil])
233 assert f.valid_field_value?([nil])
234 assert f.valid_field_value?([''])
234 assert f.valid_field_value?([''])
235 assert !f.valid_field_value?([' '])
235 assert !f.valid_field_value?([' '])
236
236
237 assert f.valid_field_value?('value2')
237 assert f.valid_field_value?('value2')
238 assert !f.valid_field_value?('abc')
238 assert !f.valid_field_value?('abc')
239
239
240 assert f.valid_field_value?(['value2'])
240 assert f.valid_field_value?(['value2'])
241 assert !f.valid_field_value?(['abc'])
241 assert !f.valid_field_value?(['abc'])
242
242
243 assert f.valid_field_value?(['', 'value2'])
243 assert f.valid_field_value?(['', 'value2'])
244 assert !f.valid_field_value?(['', 'abc'])
244 assert !f.valid_field_value?(['', 'abc'])
245
245
246 assert f.valid_field_value?(['value1', 'value2'])
246 assert f.valid_field_value?(['value1', 'value2'])
247 assert !f.valid_field_value?(['value1', 'abc'])
247 assert !f.valid_field_value?(['value1', 'abc'])
248 end
248 end
249
249
250 def test_changing_multiple_to_false_should_delete_multiple_values
250 def test_changing_multiple_to_false_should_delete_multiple_values
251 field = ProjectCustomField.create!(:name => 'field', :field_format => 'list', :multiple => 'true', :possible_values => ['field1', 'field2'])
251 field = ProjectCustomField.create!(:name => 'field', :field_format => 'list', :multiple => 'true', :possible_values => ['field1', 'field2'])
252 other = ProjectCustomField.create!(:name => 'other', :field_format => 'list', :multiple => 'true', :possible_values => ['other1', 'other2'])
252 other = ProjectCustomField.create!(:name => 'other', :field_format => 'list', :multiple => 'true', :possible_values => ['other1', 'other2'])
253
253
254 item_with_multiple_values = Project.generate!(:custom_field_values => {field.id => ['field1', 'field2'], other.id => ['other1', 'other2']})
254 item_with_multiple_values = Project.generate!(:custom_field_values => {field.id => ['field1', 'field2'], other.id => ['other1', 'other2']})
255 item_with_single_values = Project.generate!(:custom_field_values => {field.id => ['field1'], other.id => ['other2']})
255 item_with_single_values = Project.generate!(:custom_field_values => {field.id => ['field1'], other.id => ['other2']})
256
256
257 assert_difference 'CustomValue.count', -1 do
257 assert_difference 'CustomValue.count', -1 do
258 field.multiple = false
258 field.multiple = false
259 field.save!
259 field.save!
260 end
260 end
261
261
262 item_with_multiple_values = Project.find(item_with_multiple_values.id)
262 item_with_multiple_values = Project.find(item_with_multiple_values.id)
263 assert_kind_of String, item_with_multiple_values.custom_field_value(field)
263 assert_kind_of String, item_with_multiple_values.custom_field_value(field)
264 assert_kind_of Array, item_with_multiple_values.custom_field_value(other)
264 assert_kind_of Array, item_with_multiple_values.custom_field_value(other)
265 assert_equal 2, item_with_multiple_values.custom_field_value(other).size
265 assert_equal 2, item_with_multiple_values.custom_field_value(other).size
266 end
266 end
267
267
268 def test_value_class_should_return_the_class_used_for_fields_values
268 def test_value_class_should_return_the_class_used_for_fields_values
269 assert_equal User, CustomField.new(:field_format => 'user').value_class
269 assert_equal User, CustomField.new(:field_format => 'user').value_class
270 assert_equal Version, CustomField.new(:field_format => 'version').value_class
270 assert_equal Version, CustomField.new(:field_format => 'version').value_class
271 end
271 end
272
272
273 def test_value_class_should_return_nil_for_other_fields
273 def test_value_class_should_return_nil_for_other_fields
274 assert_nil CustomField.new(:field_format => 'text').value_class
274 assert_nil CustomField.new(:field_format => 'text').value_class
275 assert_nil CustomField.new.value_class
275 assert_nil CustomField.new.value_class
276 end
276 end
277
277
278 def test_value_from_keyword_for_list_custom_field
278 def test_value_from_keyword_for_list_custom_field
279 field = CustomField.find(1)
279 field = CustomField.find(1)
280 assert_equal 'PostgreSQL', field.value_from_keyword('postgresql', Issue.find(1))
280 assert_equal 'PostgreSQL', field.value_from_keyword('postgresql', Issue.find(1))
281 end
281 end
282
282
283 def test_visibile_scope_with_admin_should_return_all_custom_fields
283 def test_visibile_scope_with_admin_should_return_all_custom_fields
284 admin = User.generate! {|user| user.admin = true}
284 admin = User.generate! {|user| user.admin = true}
285 CustomField.delete_all
285 CustomField.delete_all
286 fields = [
286 fields = [
287 CustomField.generate!(:visible => true),
287 CustomField.generate!(:visible => true),
288 CustomField.generate!(:visible => false),
288 CustomField.generate!(:visible => false),
289 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
289 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
290 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
290 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
291 ]
291 ]
292
292
293 assert_equal 4, CustomField.visible(admin).count
293 assert_equal 4, CustomField.visible(admin).count
294 end
294 end
295
295
296 def test_visibile_scope_with_non_admin_user_should_return_visible_custom_fields
296 def test_visibile_scope_with_non_admin_user_should_return_visible_custom_fields
297 CustomField.delete_all
297 CustomField.delete_all
298 fields = [
298 fields = [
299 CustomField.generate!(:visible => true),
299 CustomField.generate!(:visible => true),
300 CustomField.generate!(:visible => false),
300 CustomField.generate!(:visible => false),
301 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
301 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
302 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
302 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
303 ]
303 ]
304 user = User.generate!
304 user = User.generate!
305 User.add_to_project(user, Project.first, Role.find(3))
305 User.add_to_project(user, Project.first, Role.find(3))
306
306
307 assert_equal [fields[0], fields[2]], CustomField.visible(user).order("id").to_a
307 assert_equal [fields[0], fields[2]], CustomField.visible(user).order("id").to_a
308 end
308 end
309
309
310 def test_visibile_scope_with_anonymous_user_should_return_visible_custom_fields
310 def test_visibile_scope_with_anonymous_user_should_return_visible_custom_fields
311 CustomField.delete_all
311 CustomField.delete_all
312 fields = [
312 fields = [
313 CustomField.generate!(:visible => true),
313 CustomField.generate!(:visible => true),
314 CustomField.generate!(:visible => false),
314 CustomField.generate!(:visible => false),
315 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
315 CustomField.generate!(:visible => false, :role_ids => [1, 3]),
316 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
316 CustomField.generate!(:visible => false, :role_ids => [1, 2]),
317 ]
317 ]
318
318
319 assert_equal [fields[0]], CustomField.visible(User.anonymous).order("id").to_a
319 assert_equal [fields[0]], CustomField.visible(User.anonymous).order("id").to_a
320 end
320 end
321
321
322 def test_float_cast_blank_value_should_return_nil
322 def test_float_cast_blank_value_should_return_nil
323 field = CustomField.new(:field_format => 'float')
323 field = CustomField.new(:field_format => 'float')
324 assert_equal nil, field.cast_value(nil)
324 assert_nil field.cast_value(nil)
325 assert_equal nil, field.cast_value('')
325 assert_nil field.cast_value('')
326 end
326 end
327
327
328 def test_float_cast_valid_value_should_return_float
328 def test_float_cast_valid_value_should_return_float
329 field = CustomField.new(:field_format => 'float')
329 field = CustomField.new(:field_format => 'float')
330 assert_equal 12.0, field.cast_value('12')
330 assert_equal 12.0, field.cast_value('12')
331 assert_equal 12.5, field.cast_value('12.5')
331 assert_equal 12.5, field.cast_value('12.5')
332 assert_equal 12.5, field.cast_value('+12.5')
332 assert_equal 12.5, field.cast_value('+12.5')
333 assert_equal -12.5, field.cast_value('-12.5')
333 assert_equal -12.5, field.cast_value('-12.5')
334 end
334 end
335 end
335 end
@@ -1,78 +1,78
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldUserFormatTest < ActiveSupport::TestCase
20 class CustomFieldUserFormatTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues
22
22
23 def setup
23 def setup
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user')
25 end
25 end
26
26
27 def test_possible_values_options_with_no_arguments
27 def test_possible_values_options_with_no_arguments
28 assert_equal [], @field.possible_values_options
28 assert_equal [], @field.possible_values_options
29 assert_equal [], @field.possible_values_options(nil)
29 assert_equal [], @field.possible_values_options(nil)
30 end
30 end
31
31
32 def test_possible_values_options_with_project_resource
32 def test_possible_values_options_with_project_resource
33 project = Project.find(1)
33 project = Project.find(1)
34 possible_values_options = @field.possible_values_options(project.issues.first)
34 possible_values_options = @field.possible_values_options(project.issues.first)
35 assert possible_values_options.any?
35 assert possible_values_options.any?
36 assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
36 assert_equal project.users.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
37 end
37 end
38
38
39 def test_possible_values_options_with_array
39 def test_possible_values_options_with_array
40 projects = Project.find([1, 2])
40 projects = Project.find([1, 2])
41 possible_values_options = @field.possible_values_options(projects)
41 possible_values_options = @field.possible_values_options(projects)
42 assert possible_values_options.any?
42 assert possible_values_options.any?
43 assert_equal (projects.first.users & projects.last.users).sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
43 assert_equal (projects.first.users & projects.last.users).sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
44 end
44 end
45
45
46 def test_possible_custom_value_options_should_not_include_locked_users
46 def test_possible_custom_value_options_should_not_include_locked_users
47 custom_value = CustomValue.new(:customized => Issue.find(1), :custom_field => @field)
47 custom_value = CustomValue.new(:customized => Issue.find(1), :custom_field => @field)
48 assert_include '2', @field.possible_custom_value_options(custom_value).map(&:last)
48 assert_include '2', @field.possible_custom_value_options(custom_value).map(&:last)
49
49
50 assert User.find(2).lock!
50 assert User.find(2).lock!
51 assert_not_include '2', @field.possible_custom_value_options(custom_value).map(&:last)
51 assert_not_include '2', @field.possible_custom_value_options(custom_value).map(&:last)
52 end
52 end
53
53
54 def test_possible_custom_value_options_should_include_user_that_was_assigned_to_the_custom_value
54 def test_possible_custom_value_options_should_include_user_that_was_assigned_to_the_custom_value
55 user = User.generate!
55 user = User.generate!
56 custom_value = CustomValue.new(:customized => Issue.find(1), :custom_field => @field)
56 custom_value = CustomValue.new(:customized => Issue.find(1), :custom_field => @field)
57 assert_not_include user.id.to_s, @field.possible_custom_value_options(custom_value).map(&:last)
57 assert_not_include user.id.to_s, @field.possible_custom_value_options(custom_value).map(&:last)
58
58
59 custom_value.value = user.id
59 custom_value.value = user.id
60 custom_value.save!
60 custom_value.save!
61 assert_include user.id.to_s, @field.possible_custom_value_options(custom_value).map(&:last)
61 assert_include user.id.to_s, @field.possible_custom_value_options(custom_value).map(&:last)
62 end
62 end
63
63
64 def test_cast_blank_value
64 def test_cast_blank_value
65 assert_equal nil, @field.cast_value(nil)
65 assert_nil @field.cast_value(nil)
66 assert_equal nil, @field.cast_value("")
66 assert_nil @field.cast_value("")
67 end
67 end
68
68
69 def test_cast_valid_value
69 def test_cast_valid_value
70 user = @field.cast_value("2")
70 user = @field.cast_value("2")
71 assert_kind_of User, user
71 assert_kind_of User, user
72 assert_equal User.find(2), user
72 assert_equal User.find(2), user
73 end
73 end
74
74
75 def test_cast_invalid_value
75 def test_cast_invalid_value
76 assert_equal nil, @field.cast_value("187")
76 assert_nil @field.cast_value("187")
77 end
77 end
78 end
78 end
@@ -1,61 +1,61
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CustomFieldVersionFormatTest < ActiveSupport::TestCase
20 class CustomFieldVersionFormatTest < ActiveSupport::TestCase
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues, :versions
21 fixtures :custom_fields, :projects, :members, :users, :member_roles, :trackers, :issues, :versions
22
22
23 def setup
23 def setup
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'version')
24 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'version')
25 end
25 end
26
26
27 def test_possible_values_options_with_no_arguments
27 def test_possible_values_options_with_no_arguments
28 Version.delete_all
28 Version.delete_all
29 assert_equal [], @field.possible_values_options
29 assert_equal [], @field.possible_values_options
30 assert_equal [], @field.possible_values_options(nil)
30 assert_equal [], @field.possible_values_options(nil)
31 end
31 end
32
32
33 def test_possible_values_options_with_project_resource
33 def test_possible_values_options_with_project_resource
34 project = Project.find(1)
34 project = Project.find(1)
35 possible_values_options = @field.possible_values_options(project.issues.first)
35 possible_values_options = @field.possible_values_options(project.issues.first)
36 assert possible_values_options.any?
36 assert possible_values_options.any?
37 assert_equal project.shared_versions.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
37 assert_equal project.shared_versions.sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
38 end
38 end
39
39
40 def test_possible_values_options_with_array
40 def test_possible_values_options_with_array
41 projects = Project.find([1, 2])
41 projects = Project.find([1, 2])
42 possible_values_options = @field.possible_values_options(projects)
42 possible_values_options = @field.possible_values_options(projects)
43 assert possible_values_options.any?
43 assert possible_values_options.any?
44 assert_equal (projects.first.shared_versions & projects.last.shared_versions).sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
44 assert_equal (projects.first.shared_versions & projects.last.shared_versions).sort.map {|u| [u.name, u.id.to_s]}, possible_values_options
45 end
45 end
46
46
47 def test_cast_blank_value
47 def test_cast_blank_value
48 assert_equal nil, @field.cast_value(nil)
48 assert_nil @field.cast_value(nil)
49 assert_equal nil, @field.cast_value("")
49 assert_nil @field.cast_value("")
50 end
50 end
51
51
52 def test_cast_valid_value
52 def test_cast_valid_value
53 version = @field.cast_value("2")
53 version = @field.cast_value("2")
54 assert_kind_of Version, version
54 assert_kind_of Version, version
55 assert_equal Version.find(2), version
55 assert_equal Version.find(2), version
56 end
56 end
57
57
58 def test_cast_invalid_value
58 def test_cast_invalid_value
59 assert_equal nil, @field.cast_value("187")
59 assert_nil @field.cast_value("187")
60 end
60 end
61 end
61 end
@@ -1,169 +1,169
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class GroupTest < ActiveSupport::TestCase
20 class GroupTest < ActiveSupport::TestCase
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users,
22 :enumerations, :users,
23 :projects_trackers,
23 :projects_trackers,
24 :roles,
24 :roles,
25 :member_roles,
25 :member_roles,
26 :members,
26 :members,
27 :groups_users
27 :groups_users
28
28
29 include Redmine::I18n
29 include Redmine::I18n
30
30
31 def test_create
31 def test_create
32 g = Group.new(:name => 'New group')
32 g = Group.new(:name => 'New group')
33 assert g.save
33 assert g.save
34 g.reload
34 g.reload
35 assert_equal 'New group', g.name
35 assert_equal 'New group', g.name
36 end
36 end
37
37
38 def test_name_should_accept_255_characters
38 def test_name_should_accept_255_characters
39 name = 'a' * 255
39 name = 'a' * 255
40 g = Group.new(:name => name)
40 g = Group.new(:name => name)
41 assert g.save
41 assert g.save
42 g.reload
42 g.reload
43 assert_equal name, g.name
43 assert_equal name, g.name
44 end
44 end
45
45
46 def test_blank_name_error_message
46 def test_blank_name_error_message
47 set_language_if_valid 'en'
47 set_language_if_valid 'en'
48 g = Group.new
48 g = Group.new
49 assert !g.save
49 assert !g.save
50 assert_include "Name cannot be blank", g.errors.full_messages
50 assert_include "Name cannot be blank", g.errors.full_messages
51 end
51 end
52
52
53 def test_blank_name_error_message_fr
53 def test_blank_name_error_message_fr
54 set_language_if_valid 'fr'
54 set_language_if_valid 'fr'
55 str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
55 str = "Nom doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
56 g = Group.new
56 g = Group.new
57 assert !g.save
57 assert !g.save
58 assert_include str, g.errors.full_messages
58 assert_include str, g.errors.full_messages
59 end
59 end
60
60
61 def test_group_roles_should_be_given_to_added_user
61 def test_group_roles_should_be_given_to_added_user
62 group = Group.find(11)
62 group = Group.find(11)
63 user = User.find(9)
63 user = User.find(9)
64 project = Project.first
64 project = Project.first
65
65
66 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
66 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
67 group.users << user
67 group.users << user
68 assert user.member_of?(project)
68 assert user.member_of?(project)
69 end
69 end
70
70
71 def test_new_roles_should_be_given_to_existing_user
71 def test_new_roles_should_be_given_to_existing_user
72 group = Group.find(11)
72 group = Group.find(11)
73 user = User.find(9)
73 user = User.find(9)
74 project = Project.first
74 project = Project.first
75
75
76 group.users << user
76 group.users << user
77 m = Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
77 m = Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
78 assert user.member_of?(project)
78 assert user.member_of?(project)
79 end
79 end
80
80
81 def test_user_roles_should_updated_when_updating_user_ids
81 def test_user_roles_should_updated_when_updating_user_ids
82 group = Group.find(11)
82 group = Group.find(11)
83 user = User.find(9)
83 user = User.find(9)
84 project = Project.first
84 project = Project.first
85
85
86 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
86 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
87 group.user_ids = [user.id]
87 group.user_ids = [user.id]
88 group.save!
88 group.save!
89 assert User.find(9).member_of?(project)
89 assert User.find(9).member_of?(project)
90
90
91 group.user_ids = [1]
91 group.user_ids = [1]
92 group.save!
92 group.save!
93 assert !User.find(9).member_of?(project)
93 assert !User.find(9).member_of?(project)
94 end
94 end
95
95
96 def test_user_roles_should_updated_when_updating_group_roles
96 def test_user_roles_should_updated_when_updating_group_roles
97 group = Group.find(11)
97 group = Group.find(11)
98 user = User.find(9)
98 user = User.find(9)
99 project = Project.first
99 project = Project.first
100 group.users << user
100 group.users << user
101 m = Member.create!(:principal => group, :project => project, :role_ids => [1])
101 m = Member.create!(:principal => group, :project => project, :role_ids => [1])
102 assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort
102 assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort
103
103
104 m.role_ids = [1, 2]
104 m.role_ids = [1, 2]
105 assert_equal [1, 2], user.reload.roles_for_project(project).collect(&:id).sort
105 assert_equal [1, 2], user.reload.roles_for_project(project).collect(&:id).sort
106
106
107 m.role_ids = [2]
107 m.role_ids = [2]
108 assert_equal [2], user.reload.roles_for_project(project).collect(&:id).sort
108 assert_equal [2], user.reload.roles_for_project(project).collect(&:id).sort
109
109
110 m.role_ids = [1]
110 m.role_ids = [1]
111 assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort
111 assert_equal [1], user.reload.roles_for_project(project).collect(&:id).sort
112 end
112 end
113
113
114 def test_user_memberships_should_be_removed_when_removing_group_membership
114 def test_user_memberships_should_be_removed_when_removing_group_membership
115 assert User.find(8).member_of?(Project.find(5))
115 assert User.find(8).member_of?(Project.find(5))
116 Member.find_by_project_id_and_user_id(5, 10).destroy
116 Member.find_by_project_id_and_user_id(5, 10).destroy
117 assert !User.find(8).member_of?(Project.find(5))
117 assert !User.find(8).member_of?(Project.find(5))
118 end
118 end
119
119
120 def test_user_roles_should_be_removed_when_removing_user_from_group
120 def test_user_roles_should_be_removed_when_removing_user_from_group
121 assert User.find(8).member_of?(Project.find(5))
121 assert User.find(8).member_of?(Project.find(5))
122 User.find(8).groups = []
122 User.find(8).groups = []
123 assert !User.find(8).member_of?(Project.find(5))
123 assert !User.find(8).member_of?(Project.find(5))
124 end
124 end
125
125
126 def test_destroy_should_unassign_issues
126 def test_destroy_should_unassign_issues
127 group = Group.find(10)
127 group = Group.find(10)
128 Issue.where(:id => 1).update_all(["assigned_to_id = ?", group.id])
128 Issue.where(:id => 1).update_all(["assigned_to_id = ?", group.id])
129
129
130 assert group.destroy
130 assert group.destroy
131 assert group.destroyed?
131 assert group.destroyed?
132
132
133 assert_equal nil, Issue.find(1).assigned_to_id
133 assert_nil Issue.find(1).assigned_to_id
134 end
134 end
135
135
136 def test_builtin_groups_should_be_created_if_missing
136 def test_builtin_groups_should_be_created_if_missing
137 Group.delete_all
137 Group.delete_all
138
138
139 assert_difference 'Group.count', 2 do
139 assert_difference 'Group.count', 2 do
140 group = Group.anonymous
140 group = Group.anonymous
141 assert_equal GroupAnonymous, group.class
141 assert_equal GroupAnonymous, group.class
142
142
143 group = Group.non_member
143 group = Group.non_member
144 assert_equal GroupNonMember, group.class
144 assert_equal GroupNonMember, group.class
145 end
145 end
146 end
146 end
147
147
148 def test_builtin_in_group_should_be_uniq
148 def test_builtin_in_group_should_be_uniq
149 group = GroupAnonymous.new
149 group = GroupAnonymous.new
150 group.name = 'Foo'
150 group.name = 'Foo'
151 assert !group.save
151 assert !group.save
152 end
152 end
153
153
154 def test_builtin_in_group_should_not_accept_users
154 def test_builtin_in_group_should_not_accept_users
155 group = Group.anonymous
155 group = Group.anonymous
156 assert_raise RuntimeError do
156 assert_raise RuntimeError do
157 group.users << User.find(1)
157 group.users << User.find(1)
158 end
158 end
159 assert_equal 0, group.reload.users.count
159 assert_equal 0, group.reload.users.count
160 end
160 end
161
161
162 def test_sorted_scope_should_sort_groups_alphabetically
162 def test_sorted_scope_should_sort_groups_alphabetically
163 Group.delete_all
163 Group.delete_all
164 b = Group.generate!(:name => 'B')
164 b = Group.generate!(:name => 'B')
165 a = Group.generate!(:name => 'A')
165 a = Group.generate!(:name => 'A')
166
166
167 assert_equal %w(A B), Group.sorted.to_a.map(&:name)
167 assert_equal %w(A B), Group.sorted.to_a.map(&:name)
168 end
168 end
169 end
169 end
@@ -1,1576 +1,1576
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../../test_helper', __FILE__)
20 require File.expand_path('../../../test_helper', __FILE__)
21
21
22 class ApplicationHelperTest < Redmine::HelperTest
22 class ApplicationHelperTest < Redmine::HelperTest
23 include Redmine::I18n
23 include Redmine::I18n
24 include ERB::Util
24 include ERB::Util
25 include Rails.application.routes.url_helpers
25 include Rails.application.routes.url_helpers
26
26
27 fixtures :projects, :enabled_modules,
27 fixtures :projects, :enabled_modules,
28 :users, :email_addresses,
28 :users, :email_addresses,
29 :members, :member_roles, :roles,
29 :members, :member_roles, :roles,
30 :repositories, :changesets,
30 :repositories, :changesets,
31 :projects_trackers,
31 :projects_trackers,
32 :trackers, :issue_statuses, :issues, :versions, :documents,
32 :trackers, :issue_statuses, :issues, :versions, :documents,
33 :wikis, :wiki_pages, :wiki_contents,
33 :wikis, :wiki_pages, :wiki_contents,
34 :boards, :messages, :news,
34 :boards, :messages, :news,
35 :attachments, :enumerations
35 :attachments, :enumerations
36
36
37 def setup
37 def setup
38 super
38 super
39 set_tmp_attachments_directory
39 set_tmp_attachments_directory
40 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8')
40 @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8')
41 end
41 end
42
42
43 test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do
43 test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do
44 User.current = User.find_by_login('admin')
44 User.current = User.find_by_login('admin')
45
45
46 @project = Issue.first.project # Used by helper
46 @project = Issue.first.project # Used by helper
47 response = link_to_if_authorized('By controller/actionr',
47 response = link_to_if_authorized('By controller/actionr',
48 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
48 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
49 assert_match /href/, response
49 assert_match /href/, response
50 end
50 end
51
51
52 test "#link_to_if_authorized for unauthorized user should display nothing if user isn't authorized" do
52 test "#link_to_if_authorized for unauthorized user should display nothing if user isn't authorized" do
53 User.current = User.find_by_login('dlopper')
53 User.current = User.find_by_login('dlopper')
54 @project = Project.find('private-child')
54 @project = Project.find('private-child')
55 issue = @project.issues.first
55 issue = @project.issues.first
56 assert !issue.visible?
56 assert !issue.visible?
57
57
58 response = link_to_if_authorized('Never displayed',
58 response = link_to_if_authorized('Never displayed',
59 {:controller => 'issues', :action => 'show', :id => issue})
59 {:controller => 'issues', :action => 'show', :id => issue})
60 assert_nil response
60 assert_nil response
61 end
61 end
62
62
63 def test_auto_links
63 def test_auto_links
64 to_test = {
64 to_test = {
65 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
65 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
66 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
66 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
67 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
67 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
68 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
68 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
69 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
69 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
70 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
70 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
71 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
71 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
72 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
72 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
73 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
73 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
74 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
74 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
75 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
75 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
76 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
76 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
77 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
77 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
78 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
78 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
79 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
79 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
80 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
80 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
81 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
81 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
82 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
82 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
83 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
83 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
84 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
84 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
85 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
85 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
86 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
86 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
87 # two exclamation marks
87 # two exclamation marks
88 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
88 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
89 # escaping
89 # escaping
90 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
90 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo&quot;bar</a>',
91 # wrap in angle brackets
91 # wrap in angle brackets
92 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;',
92 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;',
93 # invalid urls
93 # invalid urls
94 'http://' => 'http://',
94 'http://' => 'http://',
95 'www.' => 'www.',
95 'www.' => 'www.',
96 'test-www.bar.com' => 'test-www.bar.com',
96 'test-www.bar.com' => 'test-www.bar.com',
97 }
97 }
98 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
98 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
99 end
99 end
100
100
101 def test_auto_links_with_non_ascii_characters
101 def test_auto_links_with_non_ascii_characters
102 to_test = {
102 to_test = {
103 "http://foo.bar/#{@russian_test}" =>
103 "http://foo.bar/#{@russian_test}" =>
104 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
104 %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>|
105 }
105 }
106 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
106 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
107 end
107 end
108
108
109 def test_auto_mailto
109 def test_auto_mailto
110 to_test = {
110 to_test = {
111 'test@foo.bar' => '<a class="email" href="mailto:test@foo.bar">test@foo.bar</a>',
111 'test@foo.bar' => '<a class="email" href="mailto:test@foo.bar">test@foo.bar</a>',
112 'test@www.foo.bar' => '<a class="email" href="mailto:test@www.foo.bar">test@www.foo.bar</a>',
112 'test@www.foo.bar' => '<a class="email" href="mailto:test@www.foo.bar">test@www.foo.bar</a>',
113 }
113 }
114 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
114 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
115 end
115 end
116
116
117 def test_inline_images
117 def test_inline_images
118 to_test = {
118 to_test = {
119 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
119 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
120 'floating !>http://foo.bar/image.jpg!' => 'floating <span style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></span>',
120 'floating !>http://foo.bar/image.jpg!' => 'floating <span style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></span>',
121 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
121 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
122 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height:100px;" alt="" />',
122 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height:100px;" alt="" />',
123 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
123 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
124 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
124 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
125 }
125 }
126 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
126 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
127 end
127 end
128
128
129 def test_inline_images_inside_tags
129 def test_inline_images_inside_tags
130 raw = <<-RAW
130 raw = <<-RAW
131 h1. !foo.png! Heading
131 h1. !foo.png! Heading
132
132
133 Centered image:
133 Centered image:
134
134
135 p=. !bar.gif!
135 p=. !bar.gif!
136 RAW
136 RAW
137
137
138 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
138 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
139 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
139 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
140 end
140 end
141
141
142 def test_attached_images
142 def test_attached_images
143 to_test = {
143 to_test = {
144 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
144 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
145 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
145 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />',
146 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
146 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
147 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
147 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
148 # link image
148 # link image
149 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" /></a>',
149 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" /></a>',
150 }
150 }
151 attachments = Attachment.all
151 attachments = Attachment.all
152 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
152 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
153 end
153 end
154
154
155 def test_attached_images_with_textile_and_non_ascii_filename
155 def test_attached_images_with_textile_and_non_ascii_filename
156 attachment = Attachment.generate!(:filename => 'cafΓ©.jpg')
156 attachment = Attachment.generate!(:filename => 'cafΓ©.jpg')
157 with_settings :text_formatting => 'textile' do
157 with_settings :text_formatting => 'textile' do
158 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
158 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
159 textilizable("!cafΓ©.jpg!)", :attachments => [attachment])
159 textilizable("!cafΓ©.jpg!)", :attachments => [attachment])
160 end
160 end
161 end
161 end
162
162
163 def test_attached_images_with_markdown_and_non_ascii_filename
163 def test_attached_images_with_markdown_and_non_ascii_filename
164 skip unless Object.const_defined?(:Redcarpet)
164 skip unless Object.const_defined?(:Redcarpet)
165
165
166 attachment = Attachment.generate!(:filename => 'cafΓ©.jpg')
166 attachment = Attachment.generate!(:filename => 'cafΓ©.jpg')
167 with_settings :text_formatting => 'markdown' do
167 with_settings :text_formatting => 'markdown' do
168 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
168 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
169 textilizable("![](cafΓ©.jpg)", :attachments => [attachment])
169 textilizable("![](cafΓ©.jpg)", :attachments => [attachment])
170 end
170 end
171 end
171 end
172
172
173 def test_attached_images_filename_extension
173 def test_attached_images_filename_extension
174 set_tmp_attachments_directory
174 set_tmp_attachments_directory
175 a1 = Attachment.new(
175 a1 = Attachment.new(
176 :container => Issue.find(1),
176 :container => Issue.find(1),
177 :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
177 :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
178 :author => User.find(1))
178 :author => User.find(1))
179 assert a1.save
179 assert a1.save
180 assert_equal "testtest.JPG", a1.filename
180 assert_equal "testtest.JPG", a1.filename
181 assert_equal "image/jpeg", a1.content_type
181 assert_equal "image/jpeg", a1.content_type
182 assert a1.image?
182 assert a1.image?
183
183
184 a2 = Attachment.new(
184 a2 = Attachment.new(
185 :container => Issue.find(1),
185 :container => Issue.find(1),
186 :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
186 :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
187 :author => User.find(1))
187 :author => User.find(1))
188 assert a2.save
188 assert a2.save
189 assert_equal "testtest.jpeg", a2.filename
189 assert_equal "testtest.jpeg", a2.filename
190 assert_equal "image/jpeg", a2.content_type
190 assert_equal "image/jpeg", a2.content_type
191 assert a2.image?
191 assert a2.image?
192
192
193 a3 = Attachment.new(
193 a3 = Attachment.new(
194 :container => Issue.find(1),
194 :container => Issue.find(1),
195 :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
195 :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
196 :author => User.find(1))
196 :author => User.find(1))
197 assert a3.save
197 assert a3.save
198 assert_equal "testtest.JPE", a3.filename
198 assert_equal "testtest.JPE", a3.filename
199 assert_equal "image/jpeg", a3.content_type
199 assert_equal "image/jpeg", a3.content_type
200 assert a3.image?
200 assert a3.image?
201
201
202 a4 = Attachment.new(
202 a4 = Attachment.new(
203 :container => Issue.find(1),
203 :container => Issue.find(1),
204 :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
204 :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
205 :author => User.find(1))
205 :author => User.find(1))
206 assert a4.save
206 assert a4.save
207 assert_equal "Testtest.BMP", a4.filename
207 assert_equal "Testtest.BMP", a4.filename
208 assert_equal "image/x-ms-bmp", a4.content_type
208 assert_equal "image/x-ms-bmp", a4.content_type
209 assert a4.image?
209 assert a4.image?
210
210
211 to_test = {
211 to_test = {
212 'Inline image: !testtest.jpg!' =>
212 'Inline image: !testtest.jpg!' =>
213 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />',
213 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />',
214 'Inline image: !testtest.jpeg!' =>
214 'Inline image: !testtest.jpeg!' =>
215 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />',
215 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />',
216 'Inline image: !testtest.jpe!' =>
216 'Inline image: !testtest.jpe!' =>
217 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />',
217 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />',
218 'Inline image: !testtest.bmp!' =>
218 'Inline image: !testtest.bmp!' =>
219 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />',
219 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />',
220 }
220 }
221
221
222 attachments = [a1, a2, a3, a4]
222 attachments = [a1, a2, a3, a4]
223 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
223 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
224 end
224 end
225
225
226 def test_attached_images_should_read_later
226 def test_attached_images_should_read_later
227 set_fixtures_attachments_directory
227 set_fixtures_attachments_directory
228 a1 = Attachment.find(16)
228 a1 = Attachment.find(16)
229 assert_equal "testfile.png", a1.filename
229 assert_equal "testfile.png", a1.filename
230 assert a1.readable?
230 assert a1.readable?
231 assert (! a1.visible?(User.anonymous))
231 assert (! a1.visible?(User.anonymous))
232 assert a1.visible?(User.find(2))
232 assert a1.visible?(User.find(2))
233 a2 = Attachment.find(17)
233 a2 = Attachment.find(17)
234 assert_equal "testfile.PNG", a2.filename
234 assert_equal "testfile.PNG", a2.filename
235 assert a2.readable?
235 assert a2.readable?
236 assert (! a2.visible?(User.anonymous))
236 assert (! a2.visible?(User.anonymous))
237 assert a2.visible?(User.find(2))
237 assert a2.visible?(User.find(2))
238 assert a1.created_on < a2.created_on
238 assert a1.created_on < a2.created_on
239
239
240 to_test = {
240 to_test = {
241 'Inline image: !testfile.png!' =>
241 'Inline image: !testfile.png!' =>
242 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
242 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
243 'Inline image: !Testfile.PNG!' =>
243 'Inline image: !Testfile.PNG!' =>
244 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
244 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />',
245 }
245 }
246 attachments = [a1, a2]
246 attachments = [a1, a2]
247 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
247 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
248 set_tmp_attachments_directory
248 set_tmp_attachments_directory
249 end
249 end
250
250
251 def test_textile_external_links
251 def test_textile_external_links
252 to_test = {
252 to_test = {
253 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
253 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
254 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
254 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
255 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
255 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
256 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
256 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
257 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
257 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
258 # no multiline link text
258 # no multiline link text
259 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test",
259 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test",
260 # mailto link
260 # mailto link
261 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
261 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
262 # two exclamation marks
262 # two exclamation marks
263 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
263 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
264 # escaping
264 # escaping
265 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
265 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
266 }
266 }
267 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
267 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
268 end
268 end
269
269
270 def test_textile_external_links_with_non_ascii_characters
270 def test_textile_external_links_with_non_ascii_characters
271 to_test = {
271 to_test = {
272 %|This is a "link":http://foo.bar/#{@russian_test}| =>
272 %|This is a "link":http://foo.bar/#{@russian_test}| =>
273 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
273 %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>|
274 }
274 }
275 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
275 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
276 end
276 end
277
277
278 def test_redmine_links
278 def test_redmine_links
279 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
279 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
280 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
280 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
281 note_link = link_to('#3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
281 note_link = link_to('#3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
282 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
282 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
283 note_link2 = link_to('#3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
283 note_link2 = link_to('#3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'},
284 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
284 :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)')
285
285
286 revision_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
286 revision_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
287 :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
287 :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
288 revision_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
288 revision_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
289 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
289 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
290
290
291 changeset_link2 = link_to('691322a8eb01e11fd7',
291 changeset_link2 = link_to('691322a8eb01e11fd7',
292 {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
292 {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
293 :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
293 :class => 'changeset', :title => 'My very first commit do not escaping #<>&')
294
294
295 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
295 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
296 :class => 'document')
296 :class => 'document')
297
297
298 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
298 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
299 :class => 'version')
299 :class => 'version')
300
300
301 board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
301 board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
302
302
303 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
303 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
304
304
305 news_url = {:controller => 'news', :action => 'show', :id => 1}
305 news_url = {:controller => 'news', :action => 'show', :id => 1}
306
306
307 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
307 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
308
308
309 source_url = '/projects/ecookbook/repository/entry/some/file'
309 source_url = '/projects/ecookbook/repository/entry/some/file'
310 source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
310 source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file'
311 source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
311 source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext'
312 source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
312 source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext'
313 source_url_with_branch = '/projects/ecookbook/repository/revisions/branch/entry/some/file'
313 source_url_with_branch = '/projects/ecookbook/repository/revisions/branch/entry/some/file'
314
314
315 export_url = '/projects/ecookbook/repository/raw/some/file'
315 export_url = '/projects/ecookbook/repository/raw/some/file'
316 export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
316 export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file'
317 export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
317 export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext'
318 export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
318 export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext'
319 export_url_with_branch = '/projects/ecookbook/repository/revisions/branch/raw/some/file'
319 export_url_with_branch = '/projects/ecookbook/repository/revisions/branch/raw/some/file'
320
320
321 to_test = {
321 to_test = {
322 # tickets
322 # tickets
323 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
323 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
324 # ticket notes
324 # ticket notes
325 '#3-14' => note_link,
325 '#3-14' => note_link,
326 '#3#note-14' => note_link2,
326 '#3#note-14' => note_link2,
327 # should not ignore leading zero
327 # should not ignore leading zero
328 '#03' => '#03',
328 '#03' => '#03',
329 # changesets
329 # changesets
330 'r1' => revision_link,
330 'r1' => revision_link,
331 'r1.' => "#{revision_link}.",
331 'r1.' => "#{revision_link}.",
332 'r1, r2' => "#{revision_link}, #{revision_link2}",
332 'r1, r2' => "#{revision_link}, #{revision_link2}",
333 'r1,r2' => "#{revision_link},#{revision_link2}",
333 'r1,r2' => "#{revision_link},#{revision_link2}",
334 'commit:691322a8eb01e11fd7' => changeset_link2,
334 'commit:691322a8eb01e11fd7' => changeset_link2,
335 # documents
335 # documents
336 'document#1' => document_link,
336 'document#1' => document_link,
337 'document:"Test document"' => document_link,
337 'document:"Test document"' => document_link,
338 # versions
338 # versions
339 'version#2' => version_link,
339 'version#2' => version_link,
340 'version:1.0' => version_link,
340 'version:1.0' => version_link,
341 'version:"1.0"' => version_link,
341 'version:"1.0"' => version_link,
342 # source
342 # source
343 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'),
343 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'),
344 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
344 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
345 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
345 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
346 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
346 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
347 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
347 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
348 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
348 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
349 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
349 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
350 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
350 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'),
351 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'),
351 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'),
352 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
352 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'),
353 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
353 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'),
354 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
354 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'),
355 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
355 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'),
356 # export
356 # export
357 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'),
357 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'),
358 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
358 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'),
359 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
359 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'),
360 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
360 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'),
361 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'),
361 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'),
362 # forum
362 # forum
363 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
363 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
364 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
364 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
365 # message
365 # message
366 'message#4' => link_to('Post 2', message_url, :class => 'message'),
366 'message#4' => link_to('Post 2', message_url, :class => 'message'),
367 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
367 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
368 # news
368 # news
369 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
369 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
370 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
370 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
371 # project
371 # project
372 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
372 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
373 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
373 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
374 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
374 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
375 # not found
375 # not found
376 '#0123456789' => '#0123456789',
376 '#0123456789' => '#0123456789',
377 # invalid expressions
377 # invalid expressions
378 'source:' => 'source:',
378 'source:' => 'source:',
379 # url hash
379 # url hash
380 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
380 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
381 }
381 }
382 @project = Project.find(1)
382 @project = Project.find(1)
383 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
383 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
384 end
384 end
385
385
386 def test_should_not_parse_redmine_links_inside_link
386 def test_should_not_parse_redmine_links_inside_link
387 raw = "r1 should not be parsed in http://example.com/url-r1/"
387 raw = "r1 should not be parsed in http://example.com/url-r1/"
388 assert_match %r{<p><a class="changeset".*>r1</a> should not be parsed in <a class="external" href="http://example.com/url-r1/">http://example.com/url-r1/</a></p>},
388 assert_match %r{<p><a class="changeset".*>r1</a> should not be parsed in <a class="external" href="http://example.com/url-r1/">http://example.com/url-r1/</a></p>},
389 textilizable(raw, :project => Project.find(1))
389 textilizable(raw, :project => Project.find(1))
390 end
390 end
391
391
392 def test_redmine_links_with_a_different_project_before_current_project
392 def test_redmine_links_with_a_different_project_before_current_project
393 vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
393 vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
394 vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
394 vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
395 @project = Project.find(3)
395 @project = Project.find(3)
396 result1 = link_to("1.4.4", "/versions/#{vp1.id}", :class => "version")
396 result1 = link_to("1.4.4", "/versions/#{vp1.id}", :class => "version")
397 result2 = link_to("1.4.4", "/versions/#{vp3.id}", :class => "version")
397 result2 = link_to("1.4.4", "/versions/#{vp3.id}", :class => "version")
398 assert_equal "<p>#{result1} #{result2}</p>",
398 assert_equal "<p>#{result1} #{result2}</p>",
399 textilizable("ecookbook:version:1.4.4 version:1.4.4")
399 textilizable("ecookbook:version:1.4.4 version:1.4.4")
400 end
400 end
401
401
402 def test_escaped_redmine_links_should_not_be_parsed
402 def test_escaped_redmine_links_should_not_be_parsed
403 to_test = [
403 to_test = [
404 '#3.',
404 '#3.',
405 '#3-14.',
405 '#3-14.',
406 '#3#-note14.',
406 '#3#-note14.',
407 'r1',
407 'r1',
408 'document#1',
408 'document#1',
409 'document:"Test document"',
409 'document:"Test document"',
410 'version#2',
410 'version#2',
411 'version:1.0',
411 'version:1.0',
412 'version:"1.0"',
412 'version:"1.0"',
413 'source:/some/file'
413 'source:/some/file'
414 ]
414 ]
415 @project = Project.find(1)
415 @project = Project.find(1)
416 to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
416 to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" }
417 end
417 end
418
418
419 def test_cross_project_redmine_links
419 def test_cross_project_redmine_links
420 source_link = link_to('ecookbook:source:/some/file',
420 source_link = link_to('ecookbook:source:/some/file',
421 {:controller => 'repositories', :action => 'entry',
421 {:controller => 'repositories', :action => 'entry',
422 :id => 'ecookbook', :path => ['some', 'file']},
422 :id => 'ecookbook', :path => ['some', 'file']},
423 :class => 'source')
423 :class => 'source')
424 changeset_link = link_to('ecookbook:r2',
424 changeset_link = link_to('ecookbook:r2',
425 {:controller => 'repositories', :action => 'revision',
425 {:controller => 'repositories', :action => 'revision',
426 :id => 'ecookbook', :rev => 2},
426 :id => 'ecookbook', :rev => 2},
427 :class => 'changeset',
427 :class => 'changeset',
428 :title => 'This commit fixes #1, #2 and references #1 & #3')
428 :title => 'This commit fixes #1, #2 and references #1 & #3')
429 to_test = {
429 to_test = {
430 # documents
430 # documents
431 'document:"Test document"' => 'document:"Test document"',
431 'document:"Test document"' => 'document:"Test document"',
432 'ecookbook:document:"Test document"' =>
432 'ecookbook:document:"Test document"' =>
433 link_to("Test document", "/documents/1", :class => "document"),
433 link_to("Test document", "/documents/1", :class => "document"),
434 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
434 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
435 # versions
435 # versions
436 'version:"1.0"' => 'version:"1.0"',
436 'version:"1.0"' => 'version:"1.0"',
437 'ecookbook:version:"1.0"' =>
437 'ecookbook:version:"1.0"' =>
438 link_to("1.0", "/versions/2", :class => "version"),
438 link_to("1.0", "/versions/2", :class => "version"),
439 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
439 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
440 # changeset
440 # changeset
441 'r2' => 'r2',
441 'r2' => 'r2',
442 'ecookbook:r2' => changeset_link,
442 'ecookbook:r2' => changeset_link,
443 'invalid:r2' => 'invalid:r2',
443 'invalid:r2' => 'invalid:r2',
444 # source
444 # source
445 'source:/some/file' => 'source:/some/file',
445 'source:/some/file' => 'source:/some/file',
446 'ecookbook:source:/some/file' => source_link,
446 'ecookbook:source:/some/file' => source_link,
447 'invalid:source:/some/file' => 'invalid:source:/some/file',
447 'invalid:source:/some/file' => 'invalid:source:/some/file',
448 }
448 }
449 @project = Project.find(3)
449 @project = Project.find(3)
450 to_test.each do |text, result|
450 to_test.each do |text, result|
451 assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed"
451 assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed"
452 end
452 end
453 end
453 end
454
454
455 def test_redmine_links_by_name_should_work_with_html_escaped_characters
455 def test_redmine_links_by_name_should_work_with_html_escaped_characters
456 v = Version.generate!(:name => "Test & Show.txt", :project_id => 1)
456 v = Version.generate!(:name => "Test & Show.txt", :project_id => 1)
457 link = link_to("Test & Show.txt", "/versions/#{v.id}", :class => "version")
457 link = link_to("Test & Show.txt", "/versions/#{v.id}", :class => "version")
458
458
459 @project = v.project
459 @project = v.project
460 assert_equal "<p>#{link}</p>", textilizable('version:"Test & Show.txt"')
460 assert_equal "<p>#{link}</p>", textilizable('version:"Test & Show.txt"')
461 end
461 end
462
462
463 def test_link_to_issue_subject
463 def test_link_to_issue_subject
464 issue = Issue.generate!(:subject => "01234567890123456789")
464 issue = Issue.generate!(:subject => "01234567890123456789")
465 str = link_to_issue(issue, :truncate => 10)
465 str = link_to_issue(issue, :truncate => 10)
466 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
466 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
467 assert_equal "#{result}: 0123456...", str
467 assert_equal "#{result}: 0123456...", str
468
468
469 issue = Issue.generate!(:subject => "<&>")
469 issue = Issue.generate!(:subject => "<&>")
470 str = link_to_issue(issue)
470 str = link_to_issue(issue)
471 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
471 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
472 assert_equal "#{result}: &lt;&amp;&gt;", str
472 assert_equal "#{result}: &lt;&amp;&gt;", str
473
473
474 issue = Issue.generate!(:subject => "<&>0123456789012345")
474 issue = Issue.generate!(:subject => "<&>0123456789012345")
475 str = link_to_issue(issue, :truncate => 10)
475 str = link_to_issue(issue, :truncate => 10)
476 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
476 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes)
477 assert_equal "#{result}: &lt;&amp;&gt;0123...", str
477 assert_equal "#{result}: &lt;&amp;&gt;0123...", str
478 end
478 end
479
479
480 def test_link_to_issue_title
480 def test_link_to_issue_title
481 long_str = "0123456789" * 5
481 long_str = "0123456789" * 5
482
482
483 issue = Issue.generate!(:subject => "#{long_str}01234567890123456789")
483 issue = Issue.generate!(:subject => "#{long_str}01234567890123456789")
484 str = link_to_issue(issue, :subject => false)
484 str = link_to_issue(issue, :subject => false)
485 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
485 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
486 :class => issue.css_classes,
486 :class => issue.css_classes,
487 :title => "#{long_str}0123456...")
487 :title => "#{long_str}0123456...")
488 assert_equal result, str
488 assert_equal result, str
489
489
490 issue = Issue.generate!(:subject => "<&>#{long_str}01234567890123456789")
490 issue = Issue.generate!(:subject => "<&>#{long_str}01234567890123456789")
491 str = link_to_issue(issue, :subject => false)
491 str = link_to_issue(issue, :subject => false)
492 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
492 result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}",
493 :class => issue.css_classes,
493 :class => issue.css_classes,
494 :title => "<&>#{long_str}0123...")
494 :title => "<&>#{long_str}0123...")
495 assert_equal result, str
495 assert_equal result, str
496 end
496 end
497
497
498 def test_multiple_repositories_redmine_links
498 def test_multiple_repositories_redmine_links
499 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg')
499 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg')
500 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
500 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
501 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
501 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
502 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
502 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
503
503
504 changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
504 changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
505 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
505 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
506 svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123},
506 svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123},
507 :class => 'changeset', :title => '')
507 :class => 'changeset', :title => '')
508 hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
508 hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
509 :class => 'changeset', :title => '')
509 :class => 'changeset', :title => '')
510
510
511 source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
511 source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
512 hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
512 hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
513
513
514 to_test = {
514 to_test = {
515 'r2' => changeset_link,
515 'r2' => changeset_link,
516 'svn_repo-1|r123' => svn_changeset_link,
516 'svn_repo-1|r123' => svn_changeset_link,
517 'invalid|r123' => 'invalid|r123',
517 'invalid|r123' => 'invalid|r123',
518 'commit:hg1|abcd' => hg_changeset_link,
518 'commit:hg1|abcd' => hg_changeset_link,
519 'commit:invalid|abcd' => 'commit:invalid|abcd',
519 'commit:invalid|abcd' => 'commit:invalid|abcd',
520 # source
520 # source
521 'source:some/file' => source_link,
521 'source:some/file' => source_link,
522 'source:hg1|some/file' => hg_source_link,
522 'source:hg1|some/file' => hg_source_link,
523 'source:invalid|some/file' => 'source:invalid|some/file',
523 'source:invalid|some/file' => 'source:invalid|some/file',
524 }
524 }
525
525
526 @project = Project.find(1)
526 @project = Project.find(1)
527 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
527 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
528 end
528 end
529
529
530 def test_cross_project_multiple_repositories_redmine_links
530 def test_cross_project_multiple_repositories_redmine_links
531 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
531 svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
532 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
532 Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
533 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
533 hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
534 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
534 Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
535
535
536 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
536 changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
537 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
537 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
538 svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
538 svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
539 :class => 'changeset', :title => '')
539 :class => 'changeset', :title => '')
540 hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
540 hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
541 :class => 'changeset', :title => '')
541 :class => 'changeset', :title => '')
542
542
543 source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
543 source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
544 hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
544 hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
545
545
546 to_test = {
546 to_test = {
547 'ecookbook:r2' => changeset_link,
547 'ecookbook:r2' => changeset_link,
548 'ecookbook:svn1|r123' => svn_changeset_link,
548 'ecookbook:svn1|r123' => svn_changeset_link,
549 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123',
549 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123',
550 'ecookbook:commit:hg1|abcd' => hg_changeset_link,
550 'ecookbook:commit:hg1|abcd' => hg_changeset_link,
551 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd',
551 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd',
552 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd',
552 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd',
553 # source
553 # source
554 'ecookbook:source:some/file' => source_link,
554 'ecookbook:source:some/file' => source_link,
555 'ecookbook:source:hg1|some/file' => hg_source_link,
555 'ecookbook:source:hg1|some/file' => hg_source_link,
556 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file',
556 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file',
557 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file',
557 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file',
558 }
558 }
559
559
560 @project = Project.find(3)
560 @project = Project.find(3)
561 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
561 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
562 end
562 end
563
563
564 def test_redmine_links_git_commit
564 def test_redmine_links_git_commit
565 changeset_link = link_to('abcd',
565 changeset_link = link_to('abcd',
566 {
566 {
567 :controller => 'repositories',
567 :controller => 'repositories',
568 :action => 'revision',
568 :action => 'revision',
569 :id => 'subproject1',
569 :id => 'subproject1',
570 :rev => 'abcd',
570 :rev => 'abcd',
571 },
571 },
572 :class => 'changeset', :title => 'test commit')
572 :class => 'changeset', :title => 'test commit')
573 to_test = {
573 to_test = {
574 'commit:abcd' => changeset_link,
574 'commit:abcd' => changeset_link,
575 }
575 }
576 @project = Project.find(3)
576 @project = Project.find(3)
577 r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
577 r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git')
578 assert r
578 assert r
579 c = Changeset.new(:repository => r,
579 c = Changeset.new(:repository => r,
580 :committed_on => Time.now,
580 :committed_on => Time.now,
581 :revision => 'abcd',
581 :revision => 'abcd',
582 :scmid => 'abcd',
582 :scmid => 'abcd',
583 :comments => 'test commit')
583 :comments => 'test commit')
584 assert( c.save )
584 assert( c.save )
585 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
585 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
586 end
586 end
587
587
588 # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
588 # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'.
589 def test_redmine_links_darcs_commit
589 def test_redmine_links_darcs_commit
590 changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
590 changeset_link = link_to('20080308225258-98289-abcd456efg.gz',
591 {
591 {
592 :controller => 'repositories',
592 :controller => 'repositories',
593 :action => 'revision',
593 :action => 'revision',
594 :id => 'subproject1',
594 :id => 'subproject1',
595 :rev => '123',
595 :rev => '123',
596 },
596 },
597 :class => 'changeset', :title => 'test commit')
597 :class => 'changeset', :title => 'test commit')
598 to_test = {
598 to_test = {
599 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
599 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link,
600 }
600 }
601 @project = Project.find(3)
601 @project = Project.find(3)
602 r = Repository::Darcs.create!(
602 r = Repository::Darcs.create!(
603 :project => @project, :url => '/tmp/test/darcs',
603 :project => @project, :url => '/tmp/test/darcs',
604 :log_encoding => 'UTF-8')
604 :log_encoding => 'UTF-8')
605 assert r
605 assert r
606 c = Changeset.new(:repository => r,
606 c = Changeset.new(:repository => r,
607 :committed_on => Time.now,
607 :committed_on => Time.now,
608 :revision => '123',
608 :revision => '123',
609 :scmid => '20080308225258-98289-abcd456efg.gz',
609 :scmid => '20080308225258-98289-abcd456efg.gz',
610 :comments => 'test commit')
610 :comments => 'test commit')
611 assert( c.save )
611 assert( c.save )
612 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
612 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
613 end
613 end
614
614
615 def test_redmine_links_mercurial_commit
615 def test_redmine_links_mercurial_commit
616 changeset_link_rev = link_to('r123',
616 changeset_link_rev = link_to('r123',
617 {
617 {
618 :controller => 'repositories',
618 :controller => 'repositories',
619 :action => 'revision',
619 :action => 'revision',
620 :id => 'subproject1',
620 :id => 'subproject1',
621 :rev => '123' ,
621 :rev => '123' ,
622 },
622 },
623 :class => 'changeset', :title => 'test commit')
623 :class => 'changeset', :title => 'test commit')
624 changeset_link_commit = link_to('abcd',
624 changeset_link_commit = link_to('abcd',
625 {
625 {
626 :controller => 'repositories',
626 :controller => 'repositories',
627 :action => 'revision',
627 :action => 'revision',
628 :id => 'subproject1',
628 :id => 'subproject1',
629 :rev => 'abcd' ,
629 :rev => 'abcd' ,
630 },
630 },
631 :class => 'changeset', :title => 'test commit')
631 :class => 'changeset', :title => 'test commit')
632 to_test = {
632 to_test = {
633 'r123' => changeset_link_rev,
633 'r123' => changeset_link_rev,
634 'commit:abcd' => changeset_link_commit,
634 'commit:abcd' => changeset_link_commit,
635 }
635 }
636 @project = Project.find(3)
636 @project = Project.find(3)
637 r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
637 r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test')
638 assert r
638 assert r
639 c = Changeset.new(:repository => r,
639 c = Changeset.new(:repository => r,
640 :committed_on => Time.now,
640 :committed_on => Time.now,
641 :revision => '123',
641 :revision => '123',
642 :scmid => 'abcd',
642 :scmid => 'abcd',
643 :comments => 'test commit')
643 :comments => 'test commit')
644 assert( c.save )
644 assert( c.save )
645 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
645 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
646 end
646 end
647
647
648 def test_attachment_links
648 def test_attachment_links
649 text = 'attachment:error281.txt'
649 text = 'attachment:error281.txt'
650 result = link_to("error281.txt", "/attachments/download/1/error281.txt",
650 result = link_to("error281.txt", "/attachments/download/1/error281.txt",
651 :class => "attachment")
651 :class => "attachment")
652 assert_equal "<p>#{result}</p>",
652 assert_equal "<p>#{result}</p>",
653 textilizable(text,
653 textilizable(text,
654 :attachments => Issue.find(3).attachments),
654 :attachments => Issue.find(3).attachments),
655 "#{text} failed"
655 "#{text} failed"
656 end
656 end
657
657
658 def test_attachment_link_should_link_to_latest_attachment
658 def test_attachment_link_should_link_to_latest_attachment
659 set_tmp_attachments_directory
659 set_tmp_attachments_directory
660 a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
660 a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
661 a2 = Attachment.generate!(:filename => "test.txt")
661 a2 = Attachment.generate!(:filename => "test.txt")
662 result = link_to("test.txt", "/attachments/download/#{a2.id}/test.txt",
662 result = link_to("test.txt", "/attachments/download/#{a2.id}/test.txt",
663 :class => "attachment")
663 :class => "attachment")
664 assert_equal "<p>#{result}</p>",
664 assert_equal "<p>#{result}</p>",
665 textilizable('attachment:test.txt', :attachments => [a1, a2])
665 textilizable('attachment:test.txt', :attachments => [a1, a2])
666 end
666 end
667
667
668 def test_wiki_links
668 def test_wiki_links
669 russian_eacape = CGI.escape(@russian_test)
669 russian_eacape = CGI.escape(@russian_test)
670 to_test = {
670 to_test = {
671 '[[CookBook documentation]]' =>
671 '[[CookBook documentation]]' =>
672 link_to("CookBook documentation",
672 link_to("CookBook documentation",
673 "/projects/ecookbook/wiki/CookBook_documentation",
673 "/projects/ecookbook/wiki/CookBook_documentation",
674 :class => "wiki-page"),
674 :class => "wiki-page"),
675 '[[Another page|Page]]' =>
675 '[[Another page|Page]]' =>
676 link_to("Page",
676 link_to("Page",
677 "/projects/ecookbook/wiki/Another_page",
677 "/projects/ecookbook/wiki/Another_page",
678 :class => "wiki-page"),
678 :class => "wiki-page"),
679 # title content should be formatted
679 # title content should be formatted
680 '[[Another page|With _styled_ *title*]]' =>
680 '[[Another page|With _styled_ *title*]]' =>
681 link_to("With <em>styled</em> <strong>title</strong>".html_safe,
681 link_to("With <em>styled</em> <strong>title</strong>".html_safe,
682 "/projects/ecookbook/wiki/Another_page",
682 "/projects/ecookbook/wiki/Another_page",
683 :class => "wiki-page"),
683 :class => "wiki-page"),
684 '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' =>
684 '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' =>
685 link_to("With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;".html_safe,
685 link_to("With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;".html_safe,
686 "/projects/ecookbook/wiki/Another_page",
686 "/projects/ecookbook/wiki/Another_page",
687 :class => "wiki-page"),
687 :class => "wiki-page"),
688 # link with anchor
688 # link with anchor
689 '[[CookBook documentation#One-section]]' =>
689 '[[CookBook documentation#One-section]]' =>
690 link_to("CookBook documentation",
690 link_to("CookBook documentation",
691 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
691 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
692 :class => "wiki-page"),
692 :class => "wiki-page"),
693 '[[Another page#anchor|Page]]' =>
693 '[[Another page#anchor|Page]]' =>
694 link_to("Page",
694 link_to("Page",
695 "/projects/ecookbook/wiki/Another_page#anchor",
695 "/projects/ecookbook/wiki/Another_page#anchor",
696 :class => "wiki-page"),
696 :class => "wiki-page"),
697 # UTF8 anchor
697 # UTF8 anchor
698 "[[Another_page##{@russian_test}|#{@russian_test}]]" =>
698 "[[Another_page##{@russian_test}|#{@russian_test}]]" =>
699 link_to(@russian_test,
699 link_to(@russian_test,
700 "/projects/ecookbook/wiki/Another_page##{russian_eacape}",
700 "/projects/ecookbook/wiki/Another_page##{russian_eacape}",
701 :class => "wiki-page"),
701 :class => "wiki-page"),
702 # page that doesn't exist
702 # page that doesn't exist
703 '[[Unknown page]]' =>
703 '[[Unknown page]]' =>
704 link_to("Unknown page",
704 link_to("Unknown page",
705 "/projects/ecookbook/wiki/Unknown_page",
705 "/projects/ecookbook/wiki/Unknown_page",
706 :class => "wiki-page new"),
706 :class => "wiki-page new"),
707 '[[Unknown page|404]]' =>
707 '[[Unknown page|404]]' =>
708 link_to("404",
708 link_to("404",
709 "/projects/ecookbook/wiki/Unknown_page",
709 "/projects/ecookbook/wiki/Unknown_page",
710 :class => "wiki-page new"),
710 :class => "wiki-page new"),
711 # link to another project wiki
711 # link to another project wiki
712 '[[onlinestore:]]' =>
712 '[[onlinestore:]]' =>
713 link_to("onlinestore",
713 link_to("onlinestore",
714 "/projects/onlinestore/wiki",
714 "/projects/onlinestore/wiki",
715 :class => "wiki-page"),
715 :class => "wiki-page"),
716 '[[onlinestore:|Wiki]]' =>
716 '[[onlinestore:|Wiki]]' =>
717 link_to("Wiki",
717 link_to("Wiki",
718 "/projects/onlinestore/wiki",
718 "/projects/onlinestore/wiki",
719 :class => "wiki-page"),
719 :class => "wiki-page"),
720 '[[onlinestore:Start page]]' =>
720 '[[onlinestore:Start page]]' =>
721 link_to("Start page",
721 link_to("Start page",
722 "/projects/onlinestore/wiki/Start_page",
722 "/projects/onlinestore/wiki/Start_page",
723 :class => "wiki-page"),
723 :class => "wiki-page"),
724 '[[onlinestore:Start page|Text]]' =>
724 '[[onlinestore:Start page|Text]]' =>
725 link_to("Text",
725 link_to("Text",
726 "/projects/onlinestore/wiki/Start_page",
726 "/projects/onlinestore/wiki/Start_page",
727 :class => "wiki-page"),
727 :class => "wiki-page"),
728 '[[onlinestore:Unknown page]]' =>
728 '[[onlinestore:Unknown page]]' =>
729 link_to("Unknown page",
729 link_to("Unknown page",
730 "/projects/onlinestore/wiki/Unknown_page",
730 "/projects/onlinestore/wiki/Unknown_page",
731 :class => "wiki-page new"),
731 :class => "wiki-page new"),
732 # struck through link
732 # struck through link
733 '-[[Another page|Page]]-' =>
733 '-[[Another page|Page]]-' =>
734 "<del>".html_safe +
734 "<del>".html_safe +
735 link_to("Page",
735 link_to("Page",
736 "/projects/ecookbook/wiki/Another_page",
736 "/projects/ecookbook/wiki/Another_page",
737 :class => "wiki-page").html_safe +
737 :class => "wiki-page").html_safe +
738 "</del>".html_safe,
738 "</del>".html_safe,
739 '-[[Another page|Page]] link-' =>
739 '-[[Another page|Page]] link-' =>
740 "<del>".html_safe +
740 "<del>".html_safe +
741 link_to("Page",
741 link_to("Page",
742 "/projects/ecookbook/wiki/Another_page",
742 "/projects/ecookbook/wiki/Another_page",
743 :class => "wiki-page").html_safe +
743 :class => "wiki-page").html_safe +
744 " link</del>".html_safe,
744 " link</del>".html_safe,
745 # escaping
745 # escaping
746 '![[Another page|Page]]' => '[[Another page|Page]]',
746 '![[Another page|Page]]' => '[[Another page|Page]]',
747 # project does not exist
747 # project does not exist
748 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
748 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
749 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
749 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
750 }
750 }
751 @project = Project.find(1)
751 @project = Project.find(1)
752 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
752 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
753 end
753 end
754
754
755 def test_wiki_links_within_local_file_generation_context
755 def test_wiki_links_within_local_file_generation_context
756 to_test = {
756 to_test = {
757 # link to a page
757 # link to a page
758 '[[CookBook documentation]]' =>
758 '[[CookBook documentation]]' =>
759 link_to("CookBook documentation", "CookBook_documentation.html",
759 link_to("CookBook documentation", "CookBook_documentation.html",
760 :class => "wiki-page"),
760 :class => "wiki-page"),
761 '[[CookBook documentation|documentation]]' =>
761 '[[CookBook documentation|documentation]]' =>
762 link_to("documentation", "CookBook_documentation.html",
762 link_to("documentation", "CookBook_documentation.html",
763 :class => "wiki-page"),
763 :class => "wiki-page"),
764 '[[CookBook documentation#One-section]]' =>
764 '[[CookBook documentation#One-section]]' =>
765 link_to("CookBook documentation", "CookBook_documentation.html#One-section",
765 link_to("CookBook documentation", "CookBook_documentation.html#One-section",
766 :class => "wiki-page"),
766 :class => "wiki-page"),
767 '[[CookBook documentation#One-section|documentation]]' =>
767 '[[CookBook documentation#One-section|documentation]]' =>
768 link_to("documentation", "CookBook_documentation.html#One-section",
768 link_to("documentation", "CookBook_documentation.html#One-section",
769 :class => "wiki-page"),
769 :class => "wiki-page"),
770 # page that doesn't exist
770 # page that doesn't exist
771 '[[Unknown page]]' =>
771 '[[Unknown page]]' =>
772 link_to("Unknown page", "Unknown_page.html",
772 link_to("Unknown page", "Unknown_page.html",
773 :class => "wiki-page new"),
773 :class => "wiki-page new"),
774 '[[Unknown page|404]]' =>
774 '[[Unknown page|404]]' =>
775 link_to("404", "Unknown_page.html",
775 link_to("404", "Unknown_page.html",
776 :class => "wiki-page new"),
776 :class => "wiki-page new"),
777 '[[Unknown page#anchor]]' =>
777 '[[Unknown page#anchor]]' =>
778 link_to("Unknown page", "Unknown_page.html#anchor",
778 link_to("Unknown page", "Unknown_page.html#anchor",
779 :class => "wiki-page new"),
779 :class => "wiki-page new"),
780 '[[Unknown page#anchor|404]]' =>
780 '[[Unknown page#anchor|404]]' =>
781 link_to("404", "Unknown_page.html#anchor",
781 link_to("404", "Unknown_page.html#anchor",
782 :class => "wiki-page new"),
782 :class => "wiki-page new"),
783 }
783 }
784 @project = Project.find(1)
784 @project = Project.find(1)
785 to_test.each do |text, result|
785 to_test.each do |text, result|
786 assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local)
786 assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local)
787 end
787 end
788 end
788 end
789
789
790 def test_wiki_links_within_wiki_page_context
790 def test_wiki_links_within_wiki_page_context
791 page = WikiPage.find_by_title('Another_page' )
791 page = WikiPage.find_by_title('Another_page' )
792 to_test = {
792 to_test = {
793 '[[CookBook documentation]]' =>
793 '[[CookBook documentation]]' =>
794 link_to("CookBook documentation",
794 link_to("CookBook documentation",
795 "/projects/ecookbook/wiki/CookBook_documentation",
795 "/projects/ecookbook/wiki/CookBook_documentation",
796 :class => "wiki-page"),
796 :class => "wiki-page"),
797 '[[CookBook documentation|documentation]]' =>
797 '[[CookBook documentation|documentation]]' =>
798 link_to("documentation",
798 link_to("documentation",
799 "/projects/ecookbook/wiki/CookBook_documentation",
799 "/projects/ecookbook/wiki/CookBook_documentation",
800 :class => "wiki-page"),
800 :class => "wiki-page"),
801 '[[CookBook documentation#One-section]]' =>
801 '[[CookBook documentation#One-section]]' =>
802 link_to("CookBook documentation",
802 link_to("CookBook documentation",
803 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
803 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
804 :class => "wiki-page"),
804 :class => "wiki-page"),
805 '[[CookBook documentation#One-section|documentation]]' =>
805 '[[CookBook documentation#One-section|documentation]]' =>
806 link_to("documentation",
806 link_to("documentation",
807 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
807 "/projects/ecookbook/wiki/CookBook_documentation#One-section",
808 :class => "wiki-page"),
808 :class => "wiki-page"),
809 # link to the current page
809 # link to the current page
810 '[[Another page]]' =>
810 '[[Another page]]' =>
811 link_to("Another page",
811 link_to("Another page",
812 "/projects/ecookbook/wiki/Another_page",
812 "/projects/ecookbook/wiki/Another_page",
813 :class => "wiki-page"),
813 :class => "wiki-page"),
814 '[[Another page|Page]]' =>
814 '[[Another page|Page]]' =>
815 link_to("Page",
815 link_to("Page",
816 "/projects/ecookbook/wiki/Another_page",
816 "/projects/ecookbook/wiki/Another_page",
817 :class => "wiki-page"),
817 :class => "wiki-page"),
818 '[[Another page#anchor]]' =>
818 '[[Another page#anchor]]' =>
819 link_to("Another page",
819 link_to("Another page",
820 "#anchor",
820 "#anchor",
821 :class => "wiki-page"),
821 :class => "wiki-page"),
822 '[[Another page#anchor|Page]]' =>
822 '[[Another page#anchor|Page]]' =>
823 link_to("Page",
823 link_to("Page",
824 "#anchor",
824 "#anchor",
825 :class => "wiki-page"),
825 :class => "wiki-page"),
826 # page that doesn't exist
826 # page that doesn't exist
827 '[[Unknown page]]' =>
827 '[[Unknown page]]' =>
828 link_to("Unknown page",
828 link_to("Unknown page",
829 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
829 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
830 :class => "wiki-page new"),
830 :class => "wiki-page new"),
831 '[[Unknown page|404]]' =>
831 '[[Unknown page|404]]' =>
832 link_to("404",
832 link_to("404",
833 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
833 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page",
834 :class => "wiki-page new"),
834 :class => "wiki-page new"),
835 '[[Unknown page#anchor]]' =>
835 '[[Unknown page#anchor]]' =>
836 link_to("Unknown page",
836 link_to("Unknown page",
837 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
837 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
838 :class => "wiki-page new"),
838 :class => "wiki-page new"),
839 '[[Unknown page#anchor|404]]' =>
839 '[[Unknown page#anchor|404]]' =>
840 link_to("404",
840 link_to("404",
841 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
841 "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor",
842 :class => "wiki-page new"),
842 :class => "wiki-page new"),
843 }
843 }
844 @project = Project.find(1)
844 @project = Project.find(1)
845 to_test.each do |text, result|
845 to_test.each do |text, result|
846 assert_equal "<p>#{result}</p>",
846 assert_equal "<p>#{result}</p>",
847 textilizable(WikiContent.new( :text => text, :page => page ), :text)
847 textilizable(WikiContent.new( :text => text, :page => page ), :text)
848 end
848 end
849 end
849 end
850
850
851 def test_wiki_links_anchor_option_should_prepend_page_title_to_href
851 def test_wiki_links_anchor_option_should_prepend_page_title_to_href
852 to_test = {
852 to_test = {
853 # link to a page
853 # link to a page
854 '[[CookBook documentation]]' =>
854 '[[CookBook documentation]]' =>
855 link_to("CookBook documentation",
855 link_to("CookBook documentation",
856 "#CookBook_documentation",
856 "#CookBook_documentation",
857 :class => "wiki-page"),
857 :class => "wiki-page"),
858 '[[CookBook documentation|documentation]]' =>
858 '[[CookBook documentation|documentation]]' =>
859 link_to("documentation",
859 link_to("documentation",
860 "#CookBook_documentation",
860 "#CookBook_documentation",
861 :class => "wiki-page"),
861 :class => "wiki-page"),
862 '[[CookBook documentation#One-section]]' =>
862 '[[CookBook documentation#One-section]]' =>
863 link_to("CookBook documentation",
863 link_to("CookBook documentation",
864 "#CookBook_documentation_One-section",
864 "#CookBook_documentation_One-section",
865 :class => "wiki-page"),
865 :class => "wiki-page"),
866 '[[CookBook documentation#One-section|documentation]]' =>
866 '[[CookBook documentation#One-section|documentation]]' =>
867 link_to("documentation",
867 link_to("documentation",
868 "#CookBook_documentation_One-section",
868 "#CookBook_documentation_One-section",
869 :class => "wiki-page"),
869 :class => "wiki-page"),
870 # page that doesn't exist
870 # page that doesn't exist
871 '[[Unknown page]]' =>
871 '[[Unknown page]]' =>
872 link_to("Unknown page",
872 link_to("Unknown page",
873 "#Unknown_page",
873 "#Unknown_page",
874 :class => "wiki-page new"),
874 :class => "wiki-page new"),
875 '[[Unknown page|404]]' =>
875 '[[Unknown page|404]]' =>
876 link_to("404",
876 link_to("404",
877 "#Unknown_page",
877 "#Unknown_page",
878 :class => "wiki-page new"),
878 :class => "wiki-page new"),
879 '[[Unknown page#anchor]]' =>
879 '[[Unknown page#anchor]]' =>
880 link_to("Unknown page",
880 link_to("Unknown page",
881 "#Unknown_page_anchor",
881 "#Unknown_page_anchor",
882 :class => "wiki-page new"),
882 :class => "wiki-page new"),
883 '[[Unknown page#anchor|404]]' =>
883 '[[Unknown page#anchor|404]]' =>
884 link_to("404",
884 link_to("404",
885 "#Unknown_page_anchor",
885 "#Unknown_page_anchor",
886 :class => "wiki-page new"),
886 :class => "wiki-page new"),
887 }
887 }
888 @project = Project.find(1)
888 @project = Project.find(1)
889 to_test.each do |text, result|
889 to_test.each do |text, result|
890 assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor)
890 assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor)
891 end
891 end
892 end
892 end
893
893
894 def test_html_tags
894 def test_html_tags
895 to_test = {
895 to_test = {
896 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
896 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
897 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
897 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
898 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
898 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
899 # do not escape pre/code tags
899 # do not escape pre/code tags
900 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
900 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
901 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
901 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
902 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
902 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
903 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
903 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
904 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
904 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
905 # remove attributes except class
905 # remove attributes except class
906 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
906 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
907 '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
907 '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>',
908 "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
908 "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>",
909 '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
909 '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>',
910 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
910 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
911 # xss
911 # xss
912 '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
912 '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>',
913 '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
913 '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>',
914 }
914 }
915 to_test.each { |text, result| assert_equal result, textilizable(text) }
915 to_test.each { |text, result| assert_equal result, textilizable(text) }
916 end
916 end
917
917
918 def test_allowed_html_tags
918 def test_allowed_html_tags
919 to_test = {
919 to_test = {
920 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
920 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
921 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
921 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
922 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
922 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
923 }
923 }
924 to_test.each { |text, result| assert_equal result, textilizable(text) }
924 to_test.each { |text, result| assert_equal result, textilizable(text) }
925 end
925 end
926
926
927 def test_pre_tags
927 def test_pre_tags
928 raw = <<-RAW
928 raw = <<-RAW
929 Before
929 Before
930
930
931 <pre>
931 <pre>
932 <prepared-statement-cache-size>32</prepared-statement-cache-size>
932 <prepared-statement-cache-size>32</prepared-statement-cache-size>
933 </pre>
933 </pre>
934
934
935 After
935 After
936 RAW
936 RAW
937
937
938 expected = <<-EXPECTED
938 expected = <<-EXPECTED
939 <p>Before</p>
939 <p>Before</p>
940 <pre>
940 <pre>
941 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
941 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
942 </pre>
942 </pre>
943 <p>After</p>
943 <p>After</p>
944 EXPECTED
944 EXPECTED
945
945
946 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
946 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
947 end
947 end
948
948
949 def test_pre_content_should_not_parse_wiki_and_redmine_links
949 def test_pre_content_should_not_parse_wiki_and_redmine_links
950 raw = <<-RAW
950 raw = <<-RAW
951 [[CookBook documentation]]
951 [[CookBook documentation]]
952
952
953 #1
953 #1
954
954
955 <pre>
955 <pre>
956 [[CookBook documentation]]
956 [[CookBook documentation]]
957
957
958 #1
958 #1
959 </pre>
959 </pre>
960 RAW
960 RAW
961
961
962 result1 = link_to("CookBook documentation",
962 result1 = link_to("CookBook documentation",
963 "/projects/ecookbook/wiki/CookBook_documentation",
963 "/projects/ecookbook/wiki/CookBook_documentation",
964 :class => "wiki-page")
964 :class => "wiki-page")
965 result2 = link_to('#1',
965 result2 = link_to('#1',
966 "/issues/1",
966 "/issues/1",
967 :class => Issue.find(1).css_classes,
967 :class => Issue.find(1).css_classes,
968 :title => "Bug: Cannot print recipes (New)")
968 :title => "Bug: Cannot print recipes (New)")
969
969
970 expected = <<-EXPECTED
970 expected = <<-EXPECTED
971 <p>#{result1}</p>
971 <p>#{result1}</p>
972 <p>#{result2}</p>
972 <p>#{result2}</p>
973 <pre>
973 <pre>
974 [[CookBook documentation]]
974 [[CookBook documentation]]
975
975
976 #1
976 #1
977 </pre>
977 </pre>
978 EXPECTED
978 EXPECTED
979
979
980 @project = Project.find(1)
980 @project = Project.find(1)
981 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
981 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
982 end
982 end
983
983
984 def test_non_closing_pre_blocks_should_be_closed
984 def test_non_closing_pre_blocks_should_be_closed
985 raw = <<-RAW
985 raw = <<-RAW
986 <pre><code>
986 <pre><code>
987 RAW
987 RAW
988
988
989 expected = <<-EXPECTED
989 expected = <<-EXPECTED
990 <pre><code>
990 <pre><code>
991 </code></pre>
991 </code></pre>
992 EXPECTED
992 EXPECTED
993
993
994 @project = Project.find(1)
994 @project = Project.find(1)
995 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
995 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
996 end
996 end
997
997
998 def test_unbalanced_closing_pre_tag_should_not_error
998 def test_unbalanced_closing_pre_tag_should_not_error
999 assert_nothing_raised do
999 assert_nothing_raised do
1000 textilizable("unbalanced</pre>")
1000 textilizable("unbalanced</pre>")
1001 end
1001 end
1002 end
1002 end
1003
1003
1004 def test_syntax_highlight
1004 def test_syntax_highlight
1005 raw = <<-RAW
1005 raw = <<-RAW
1006 <pre><code class="ruby">
1006 <pre><code class="ruby">
1007 # Some ruby code here
1007 # Some ruby code here
1008 </code></pre>
1008 </code></pre>
1009 RAW
1009 RAW
1010
1010
1011 expected = <<-EXPECTED
1011 expected = <<-EXPECTED
1012 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span>
1012 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span>
1013 </code></pre>
1013 </code></pre>
1014 EXPECTED
1014 EXPECTED
1015
1015
1016 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
1016 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
1017 end
1017 end
1018
1018
1019 def test_to_path_param
1019 def test_to_path_param
1020 assert_equal 'test1/test2', to_path_param('test1/test2')
1020 assert_equal 'test1/test2', to_path_param('test1/test2')
1021 assert_equal 'test1/test2', to_path_param('/test1/test2/')
1021 assert_equal 'test1/test2', to_path_param('/test1/test2/')
1022 assert_equal 'test1/test2', to_path_param('//test1/test2/')
1022 assert_equal 'test1/test2', to_path_param('//test1/test2/')
1023 assert_equal nil, to_path_param('/')
1023 assert_nil to_path_param('/')
1024 end
1024 end
1025
1025
1026 def test_wiki_links_in_tables
1026 def test_wiki_links_in_tables
1027 text = "|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|"
1027 text = "|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|"
1028 link1 = link_to("Link title", "/projects/ecookbook/wiki/Page", :class => "wiki-page new")
1028 link1 = link_to("Link title", "/projects/ecookbook/wiki/Page", :class => "wiki-page new")
1029 link2 = link_to("Other title", "/projects/ecookbook/wiki/Other_Page", :class => "wiki-page new")
1029 link2 = link_to("Other title", "/projects/ecookbook/wiki/Other_Page", :class => "wiki-page new")
1030 link3 = link_to("Last page", "/projects/ecookbook/wiki/Last_page", :class => "wiki-page new")
1030 link3 = link_to("Last page", "/projects/ecookbook/wiki/Last_page", :class => "wiki-page new")
1031 result = "<tr><td>#{link1}</td>" +
1031 result = "<tr><td>#{link1}</td>" +
1032 "<td>#{link2}</td>" +
1032 "<td>#{link2}</td>" +
1033 "</tr><tr><td>Cell 21</td><td>#{link3}</td></tr>"
1033 "</tr><tr><td>Cell 21</td><td>#{link3}</td></tr>"
1034 @project = Project.find(1)
1034 @project = Project.find(1)
1035 assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '')
1035 assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '')
1036 end
1036 end
1037
1037
1038 def test_text_formatting
1038 def test_text_formatting
1039 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
1039 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
1040 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
1040 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
1041 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
1041 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
1042 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
1042 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
1043 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
1043 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
1044 }
1044 }
1045 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
1045 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
1046 end
1046 end
1047
1047
1048 def test_wiki_horizontal_rule
1048 def test_wiki_horizontal_rule
1049 assert_equal '<hr />', textilizable('---')
1049 assert_equal '<hr />', textilizable('---')
1050 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
1050 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
1051 end
1051 end
1052
1052
1053 def test_footnotes
1053 def test_footnotes
1054 raw = <<-RAW
1054 raw = <<-RAW
1055 This is some text[1].
1055 This is some text[1].
1056
1056
1057 fn1. This is the foot note
1057 fn1. This is the foot note
1058 RAW
1058 RAW
1059
1059
1060 expected = <<-EXPECTED
1060 expected = <<-EXPECTED
1061 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
1061 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
1062 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
1062 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
1063 EXPECTED
1063 EXPECTED
1064
1064
1065 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
1065 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
1066 end
1066 end
1067
1067
1068 def test_headings
1068 def test_headings
1069 raw = 'h1. Some heading'
1069 raw = 'h1. Some heading'
1070 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
1070 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
1071
1071
1072 assert_equal expected, textilizable(raw)
1072 assert_equal expected, textilizable(raw)
1073 end
1073 end
1074
1074
1075 def test_headings_with_special_chars
1075 def test_headings_with_special_chars
1076 # This test makes sure that the generated anchor names match the expected
1076 # This test makes sure that the generated anchor names match the expected
1077 # ones even if the heading text contains unconventional characters
1077 # ones even if the heading text contains unconventional characters
1078 raw = 'h1. Some heading related to version 0.5'
1078 raw = 'h1. Some heading related to version 0.5'
1079 anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
1079 anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
1080 expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
1080 expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
1081
1081
1082 assert_equal expected, textilizable(raw)
1082 assert_equal expected, textilizable(raw)
1083 end
1083 end
1084
1084
1085 def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
1085 def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
1086 page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 )
1086 page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 )
1087 content = WikiContent.new( :text => 'h1. Some heading', :page => page )
1087 content = WikiContent.new( :text => 'h1. Some heading', :page => page )
1088
1088
1089 expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
1089 expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
1090
1090
1091 assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
1091 assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
1092 end
1092 end
1093
1093
1094 def test_table_of_content
1094 def test_table_of_content
1095 set_language_if_valid 'en'
1095 set_language_if_valid 'en'
1096
1096
1097 raw = <<-RAW
1097 raw = <<-RAW
1098 {{toc}}
1098 {{toc}}
1099
1099
1100 h1. Title
1100 h1. Title
1101
1101
1102 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
1102 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
1103
1103
1104 h2. Subtitle with a [[Wiki]] link
1104 h2. Subtitle with a [[Wiki]] link
1105
1105
1106 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
1106 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
1107
1107
1108 h2. Subtitle with [[Wiki|another Wiki]] link
1108 h2. Subtitle with [[Wiki|another Wiki]] link
1109
1109
1110 h2. Subtitle with %{color:red}red text%
1110 h2. Subtitle with %{color:red}red text%
1111
1111
1112 <pre>
1112 <pre>
1113 some code
1113 some code
1114 </pre>
1114 </pre>
1115
1115
1116 h3. Subtitle with *some* _modifiers_
1116 h3. Subtitle with *some* _modifiers_
1117
1117
1118 h3. Subtitle with @inline code@
1118 h3. Subtitle with @inline code@
1119
1119
1120 h1. Another title
1120 h1. Another title
1121
1121
1122 h3. An "Internet link":http://www.redmine.org/ inside subtitle
1122 h3. An "Internet link":http://www.redmine.org/ inside subtitle
1123
1123
1124 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
1124 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
1125
1125
1126 RAW
1126 RAW
1127
1127
1128 expected = '<ul class="toc">' +
1128 expected = '<ul class="toc">' +
1129 '<li><strong>Table of contents</strong></li>' +
1129 '<li><strong>Table of contents</strong></li>' +
1130 '<li><a href="#Title">Title</a>' +
1130 '<li><a href="#Title">Title</a>' +
1131 '<ul>' +
1131 '<ul>' +
1132 '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
1132 '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
1133 '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
1133 '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
1134 '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
1134 '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' +
1135 '<ul>' +
1135 '<ul>' +
1136 '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
1136 '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
1137 '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' +
1137 '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' +
1138 '</ul>' +
1138 '</ul>' +
1139 '</li>' +
1139 '</li>' +
1140 '</ul>' +
1140 '</ul>' +
1141 '</li>' +
1141 '</li>' +
1142 '<li><a href="#Another-title">Another title</a>' +
1142 '<li><a href="#Another-title">Another title</a>' +
1143 '<ul>' +
1143 '<ul>' +
1144 '<li>' +
1144 '<li>' +
1145 '<ul>' +
1145 '<ul>' +
1146 '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
1146 '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
1147 '</ul>' +
1147 '</ul>' +
1148 '</li>' +
1148 '</li>' +
1149 '<li><a href="#Project-Name">Project Name</a></li>' +
1149 '<li><a href="#Project-Name">Project Name</a></li>' +
1150 '</ul>' +
1150 '</ul>' +
1151 '</li>' +
1151 '</li>' +
1152 '</ul>'
1152 '</ul>'
1153
1153
1154 @project = Project.find(1)
1154 @project = Project.find(1)
1155 assert textilizable(raw).gsub("\n", "").include?(expected)
1155 assert textilizable(raw).gsub("\n", "").include?(expected)
1156 end
1156 end
1157
1157
1158 def test_table_of_content_should_generate_unique_anchors
1158 def test_table_of_content_should_generate_unique_anchors
1159 set_language_if_valid 'en'
1159 set_language_if_valid 'en'
1160
1160
1161 raw = <<-RAW
1161 raw = <<-RAW
1162 {{toc}}
1162 {{toc}}
1163
1163
1164 h1. Title
1164 h1. Title
1165
1165
1166 h2. Subtitle
1166 h2. Subtitle
1167
1167
1168 h2. Subtitle
1168 h2. Subtitle
1169 RAW
1169 RAW
1170
1170
1171 expected = '<ul class="toc">' +
1171 expected = '<ul class="toc">' +
1172 '<li><strong>Table of contents</strong></li>' +
1172 '<li><strong>Table of contents</strong></li>' +
1173 '<li><a href="#Title">Title</a>' +
1173 '<li><a href="#Title">Title</a>' +
1174 '<ul>' +
1174 '<ul>' +
1175 '<li><a href="#Subtitle">Subtitle</a></li>' +
1175 '<li><a href="#Subtitle">Subtitle</a></li>' +
1176 '<li><a href="#Subtitle-2">Subtitle</a></li>' +
1176 '<li><a href="#Subtitle-2">Subtitle</a></li>' +
1177 '</ul>' +
1177 '</ul>' +
1178 '</li>' +
1178 '</li>' +
1179 '</ul>'
1179 '</ul>'
1180
1180
1181 @project = Project.find(1)
1181 @project = Project.find(1)
1182 result = textilizable(raw).gsub("\n", "")
1182 result = textilizable(raw).gsub("\n", "")
1183 assert_include expected, result
1183 assert_include expected, result
1184 assert_include '<a name="Subtitle">', result
1184 assert_include '<a name="Subtitle">', result
1185 assert_include '<a name="Subtitle-2">', result
1185 assert_include '<a name="Subtitle-2">', result
1186 end
1186 end
1187
1187
1188 def test_table_of_content_should_contain_included_page_headings
1188 def test_table_of_content_should_contain_included_page_headings
1189 set_language_if_valid 'en'
1189 set_language_if_valid 'en'
1190
1190
1191 raw = <<-RAW
1191 raw = <<-RAW
1192 {{toc}}
1192 {{toc}}
1193
1193
1194 h1. Included
1194 h1. Included
1195
1195
1196 {{include(Child_1)}}
1196 {{include(Child_1)}}
1197 RAW
1197 RAW
1198
1198
1199 expected = '<ul class="toc">' +
1199 expected = '<ul class="toc">' +
1200 '<li><strong>Table of contents</strong></li>' +
1200 '<li><strong>Table of contents</strong></li>' +
1201 '<li><a href="#Included">Included</a></li>' +
1201 '<li><a href="#Included">Included</a></li>' +
1202 '<li><a href="#Child-page-1">Child page 1</a></li>' +
1202 '<li><a href="#Child-page-1">Child page 1</a></li>' +
1203 '</ul>'
1203 '</ul>'
1204
1204
1205 @project = Project.find(1)
1205 @project = Project.find(1)
1206 assert textilizable(raw).gsub("\n", "").include?(expected)
1206 assert textilizable(raw).gsub("\n", "").include?(expected)
1207 end
1207 end
1208
1208
1209 def test_toc_with_textile_formatting_should_be_parsed
1209 def test_toc_with_textile_formatting_should_be_parsed
1210 with_settings :text_formatting => 'textile' do
1210 with_settings :text_formatting => 'textile' do
1211 assert_select_in textilizable("{{toc}}\n\nh1. Heading"), 'ul.toc li', :text => 'Heading'
1211 assert_select_in textilizable("{{toc}}\n\nh1. Heading"), 'ul.toc li', :text => 'Heading'
1212 assert_select_in textilizable("{{<toc}}\n\nh1. Heading"), 'ul.toc.left li', :text => 'Heading'
1212 assert_select_in textilizable("{{<toc}}\n\nh1. Heading"), 'ul.toc.left li', :text => 'Heading'
1213 assert_select_in textilizable("{{>toc}}\n\nh1. Heading"), 'ul.toc.right li', :text => 'Heading'
1213 assert_select_in textilizable("{{>toc}}\n\nh1. Heading"), 'ul.toc.right li', :text => 'Heading'
1214 end
1214 end
1215 end
1215 end
1216
1216
1217 if Object.const_defined?(:Redcarpet)
1217 if Object.const_defined?(:Redcarpet)
1218 def test_toc_with_markdown_formatting_should_be_parsed
1218 def test_toc_with_markdown_formatting_should_be_parsed
1219 with_settings :text_formatting => 'markdown' do
1219 with_settings :text_formatting => 'markdown' do
1220 assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading'
1220 assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading'
1221 assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading'
1221 assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading'
1222 assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading'
1222 assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading'
1223 end
1223 end
1224 end
1224 end
1225 end
1225 end
1226
1226
1227 def test_section_edit_links
1227 def test_section_edit_links
1228 raw = <<-RAW
1228 raw = <<-RAW
1229 h1. Title
1229 h1. Title
1230
1230
1231 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
1231 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
1232
1232
1233 h2. Subtitle with a [[Wiki]] link
1233 h2. Subtitle with a [[Wiki]] link
1234
1234
1235 h2. Subtitle with *some* _modifiers_
1235 h2. Subtitle with *some* _modifiers_
1236
1236
1237 h2. Subtitle with @inline code@
1237 h2. Subtitle with @inline code@
1238
1238
1239 <pre>
1239 <pre>
1240 some code
1240 some code
1241
1241
1242 h2. heading inside pre
1242 h2. heading inside pre
1243
1243
1244 <h2>html heading inside pre</h2>
1244 <h2>html heading inside pre</h2>
1245 </pre>
1245 </pre>
1246
1246
1247 h2. Subtitle after pre tag
1247 h2. Subtitle after pre tag
1248 RAW
1248 RAW
1249
1249
1250 @project = Project.find(1)
1250 @project = Project.find(1)
1251 set_language_if_valid 'en'
1251 set_language_if_valid 'en'
1252 result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "")
1252 result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "")
1253
1253
1254 # heading that contains inline code
1254 # heading that contains inline code
1255 assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-4">' +
1255 assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-4">' +
1256 '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=4">Edit this section</a></div>' +
1256 '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=4">Edit this section</a></div>' +
1257 '<a name="Subtitle-with-inline-code"></a>' +
1257 '<a name="Subtitle-with-inline-code"></a>' +
1258 '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">&para;</a></h2>'),
1258 '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">&para;</a></h2>'),
1259 result
1259 result
1260
1260
1261 # last heading
1261 # last heading
1262 assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-5">' +
1262 assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-5">' +
1263 '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=5">Edit this section</a></div>' +
1263 '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=5">Edit this section</a></div>' +
1264 '<a name="Subtitle-after-pre-tag"></a>' +
1264 '<a name="Subtitle-after-pre-tag"></a>' +
1265 '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">&para;</a></h2>'),
1265 '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">&para;</a></h2>'),
1266 result
1266 result
1267 end
1267 end
1268
1268
1269 def test_default_formatter
1269 def test_default_formatter
1270 with_settings :text_formatting => 'unknown' do
1270 with_settings :text_formatting => 'unknown' do
1271 text = 'a *link*: http://www.example.net/'
1271 text = 'a *link*: http://www.example.net/'
1272 assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
1272 assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
1273 end
1273 end
1274 end
1274 end
1275
1275
1276 def test_parse_redmine_links_should_handle_a_tag_without_attributes
1276 def test_parse_redmine_links_should_handle_a_tag_without_attributes
1277 text = '<a>http://example.com</a>'
1277 text = '<a>http://example.com</a>'
1278 expected = text.dup
1278 expected = text.dup
1279 parse_redmine_links(text, nil, nil, nil, true, {})
1279 parse_redmine_links(text, nil, nil, nil, true, {})
1280 assert_equal expected, text
1280 assert_equal expected, text
1281 end
1281 end
1282
1282
1283 def test_due_date_distance_in_words
1283 def test_due_date_distance_in_words
1284 to_test = { Date.today => 'Due in 0 days',
1284 to_test = { Date.today => 'Due in 0 days',
1285 Date.today + 1 => 'Due in 1 day',
1285 Date.today + 1 => 'Due in 1 day',
1286 Date.today + 100 => 'Due in about 3 months',
1286 Date.today + 100 => 'Due in about 3 months',
1287 Date.today + 20000 => 'Due in over 54 years',
1287 Date.today + 20000 => 'Due in over 54 years',
1288 Date.today - 1 => '1 day late',
1288 Date.today - 1 => '1 day late',
1289 Date.today - 100 => 'about 3 months late',
1289 Date.today - 100 => 'about 3 months late',
1290 Date.today - 20000 => 'over 54 years late',
1290 Date.today - 20000 => 'over 54 years late',
1291 }
1291 }
1292 ::I18n.locale = :en
1292 ::I18n.locale = :en
1293 to_test.each do |date, expected|
1293 to_test.each do |date, expected|
1294 assert_equal expected, due_date_distance_in_words(date)
1294 assert_equal expected, due_date_distance_in_words(date)
1295 end
1295 end
1296 end
1296 end
1297
1297
1298 def test_avatar_enabled
1298 def test_avatar_enabled
1299 with_settings :gravatar_enabled => '1' do
1299 with_settings :gravatar_enabled => '1' do
1300 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1300 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1301 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1301 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
1302 # Default size is 50
1302 # Default size is 50
1303 assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50')
1303 assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50')
1304 assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24')
1304 assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24')
1305 # Non-avatar options should be considered html options
1305 # Non-avatar options should be considered html options
1306 assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
1306 assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"')
1307 # The default class of the img tag should be gravatar
1307 # The default class of the img tag should be gravatar
1308 assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
1308 assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"')
1309 assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
1309 assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"')
1310 assert_nil avatar('jsmith')
1310 assert_nil avatar('jsmith')
1311 assert_nil avatar(nil)
1311 assert_nil avatar(nil)
1312 end
1312 end
1313 end
1313 end
1314
1314
1315 def test_avatar_disabled
1315 def test_avatar_disabled
1316 with_settings :gravatar_enabled => '0' do
1316 with_settings :gravatar_enabled => '0' do
1317 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
1317 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
1318 end
1318 end
1319 end
1319 end
1320
1320
1321 def test_link_to_user
1321 def test_link_to_user
1322 user = User.find(2)
1322 user = User.find(2)
1323 result = link_to("John Smith", "/users/2", :class => "user active")
1323 result = link_to("John Smith", "/users/2", :class => "user active")
1324 assert_equal result, link_to_user(user)
1324 assert_equal result, link_to_user(user)
1325 end
1325 end
1326
1326
1327 def test_link_to_user_should_not_link_to_locked_user
1327 def test_link_to_user_should_not_link_to_locked_user
1328 with_current_user nil do
1328 with_current_user nil do
1329 user = User.find(5)
1329 user = User.find(5)
1330 assert user.locked?
1330 assert user.locked?
1331 assert_equal 'Dave2 Lopper2', link_to_user(user)
1331 assert_equal 'Dave2 Lopper2', link_to_user(user)
1332 end
1332 end
1333 end
1333 end
1334
1334
1335 def test_link_to_user_should_link_to_locked_user_if_current_user_is_admin
1335 def test_link_to_user_should_link_to_locked_user_if_current_user_is_admin
1336 with_current_user User.find(1) do
1336 with_current_user User.find(1) do
1337 user = User.find(5)
1337 user = User.find(5)
1338 assert user.locked?
1338 assert user.locked?
1339 result = link_to("Dave2 Lopper2", "/users/5", :class => "user locked")
1339 result = link_to("Dave2 Lopper2", "/users/5", :class => "user locked")
1340 assert_equal result, link_to_user(user)
1340 assert_equal result, link_to_user(user)
1341 end
1341 end
1342 end
1342 end
1343
1343
1344 def test_link_to_user_should_not_link_to_anonymous
1344 def test_link_to_user_should_not_link_to_anonymous
1345 user = User.anonymous
1345 user = User.anonymous
1346 assert user.anonymous?
1346 assert user.anonymous?
1347 t = link_to_user(user)
1347 t = link_to_user(user)
1348 assert_equal ::I18n.t(:label_user_anonymous), t
1348 assert_equal ::I18n.t(:label_user_anonymous), t
1349 end
1349 end
1350
1350
1351 def test_link_to_attachment
1351 def test_link_to_attachment
1352 a = Attachment.find(3)
1352 a = Attachment.find(3)
1353 assert_equal '<a href="/attachments/3/logo.gif">logo.gif</a>',
1353 assert_equal '<a href="/attachments/3/logo.gif">logo.gif</a>',
1354 link_to_attachment(a)
1354 link_to_attachment(a)
1355 assert_equal '<a href="/attachments/3/logo.gif">Text</a>',
1355 assert_equal '<a href="/attachments/3/logo.gif">Text</a>',
1356 link_to_attachment(a, :text => 'Text')
1356 link_to_attachment(a, :text => 'Text')
1357 result = link_to("logo.gif", "/attachments/3/logo.gif", :class => "foo")
1357 result = link_to("logo.gif", "/attachments/3/logo.gif", :class => "foo")
1358 assert_equal result,
1358 assert_equal result,
1359 link_to_attachment(a, :class => 'foo')
1359 link_to_attachment(a, :class => 'foo')
1360 assert_equal '<a href="/attachments/download/3/logo.gif">logo.gif</a>',
1360 assert_equal '<a href="/attachments/download/3/logo.gif">logo.gif</a>',
1361 link_to_attachment(a, :download => true)
1361 link_to_attachment(a, :download => true)
1362 assert_equal '<a href="http://test.host/attachments/3/logo.gif">logo.gif</a>',
1362 assert_equal '<a href="http://test.host/attachments/3/logo.gif">logo.gif</a>',
1363 link_to_attachment(a, :only_path => false)
1363 link_to_attachment(a, :only_path => false)
1364 end
1364 end
1365
1365
1366 def test_thumbnail_tag
1366 def test_thumbnail_tag
1367 a = Attachment.find(3)
1367 a = Attachment.find(3)
1368 assert_select_in thumbnail_tag(a),
1368 assert_select_in thumbnail_tag(a),
1369 'a[href=?][title=?] img[alt="3"][src=?]',
1369 'a[href=?][title=?] img[alt="3"][src=?]',
1370 "/attachments/3/logo.gif", "logo.gif", "/attachments/thumbnail/3"
1370 "/attachments/3/logo.gif", "logo.gif", "/attachments/thumbnail/3"
1371 end
1371 end
1372
1372
1373 def test_link_to_project
1373 def test_link_to_project
1374 project = Project.find(1)
1374 project = Project.find(1)
1375 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1375 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
1376 link_to_project(project)
1376 link_to_project(project)
1377 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1377 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
1378 link_to_project(project, {:only_path => false, :jump => 'blah'})
1378 link_to_project(project, {:only_path => false, :jump => 'blah'})
1379 end
1379 end
1380
1380
1381 def test_link_to_project_settings
1381 def test_link_to_project_settings
1382 project = Project.find(1)
1382 project = Project.find(1)
1383 assert_equal '<a href="/projects/ecookbook/settings">eCookbook</a>', link_to_project_settings(project)
1383 assert_equal '<a href="/projects/ecookbook/settings">eCookbook</a>', link_to_project_settings(project)
1384
1384
1385 project.status = Project::STATUS_CLOSED
1385 project.status = Project::STATUS_CLOSED
1386 assert_equal '<a href="/projects/ecookbook">eCookbook</a>', link_to_project_settings(project)
1386 assert_equal '<a href="/projects/ecookbook">eCookbook</a>', link_to_project_settings(project)
1387
1387
1388 project.status = Project::STATUS_ARCHIVED
1388 project.status = Project::STATUS_ARCHIVED
1389 assert_equal 'eCookbook', link_to_project_settings(project)
1389 assert_equal 'eCookbook', link_to_project_settings(project)
1390 end
1390 end
1391
1391
1392 def test_link_to_legacy_project_with_numerical_identifier_should_use_id
1392 def test_link_to_legacy_project_with_numerical_identifier_should_use_id
1393 # numeric identifier are no longer allowed
1393 # numeric identifier are no longer allowed
1394 Project.where(:id => 1).update_all(:identifier => 25)
1394 Project.where(:id => 1).update_all(:identifier => 25)
1395 assert_equal '<a href="/projects/1">eCookbook</a>',
1395 assert_equal '<a href="/projects/1">eCookbook</a>',
1396 link_to_project(Project.find(1))
1396 link_to_project(Project.find(1))
1397 end
1397 end
1398
1398
1399 def test_principals_options_for_select_with_users
1399 def test_principals_options_for_select_with_users
1400 User.current = nil
1400 User.current = nil
1401 users = [User.find(2), User.find(4)]
1401 users = [User.find(2), User.find(4)]
1402 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
1402 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
1403 principals_options_for_select(users)
1403 principals_options_for_select(users)
1404 end
1404 end
1405
1405
1406 def test_principals_options_for_select_with_selected
1406 def test_principals_options_for_select_with_selected
1407 User.current = nil
1407 User.current = nil
1408 users = [User.find(2), User.find(4)]
1408 users = [User.find(2), User.find(4)]
1409 assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
1409 assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
1410 principals_options_for_select(users, User.find(4))
1410 principals_options_for_select(users, User.find(4))
1411 end
1411 end
1412
1412
1413 def test_principals_options_for_select_with_users_and_groups
1413 def test_principals_options_for_select_with_users_and_groups
1414 User.current = nil
1414 User.current = nil
1415 set_language_if_valid 'en'
1415 set_language_if_valid 'en'
1416 users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
1416 users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
1417 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
1417 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
1418 %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
1418 %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
1419 principals_options_for_select(users)
1419 principals_options_for_select(users)
1420 end
1420 end
1421
1421
1422 def test_principals_options_for_select_with_empty_collection
1422 def test_principals_options_for_select_with_empty_collection
1423 assert_equal '', principals_options_for_select([])
1423 assert_equal '', principals_options_for_select([])
1424 end
1424 end
1425
1425
1426 def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection
1426 def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection
1427 set_language_if_valid 'en'
1427 set_language_if_valid 'en'
1428 users = [User.find(2), User.find(4)]
1428 users = [User.find(2), User.find(4)]
1429 User.current = User.find(4)
1429 User.current = User.find(4)
1430 assert_include '<option value="4">&lt;&lt; me &gt;&gt;</option>', principals_options_for_select(users)
1430 assert_include '<option value="4">&lt;&lt; me &gt;&gt;</option>', principals_options_for_select(users)
1431 end
1431 end
1432
1432
1433 def test_stylesheet_link_tag_should_pick_the_default_stylesheet
1433 def test_stylesheet_link_tag_should_pick_the_default_stylesheet
1434 assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles")
1434 assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles")
1435 end
1435 end
1436
1436
1437 def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet
1437 def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet
1438 assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo)
1438 assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo)
1439 end
1439 end
1440
1440
1441 def test_image_tag_should_pick_the_default_image
1441 def test_image_tag_should_pick_the_default_image
1442 assert_match 'src="/images/image.png"', image_tag("image.png")
1442 assert_match 'src="/images/image.png"', image_tag("image.png")
1443 end
1443 end
1444
1444
1445 def test_image_tag_should_pick_the_theme_image_if_it_exists
1445 def test_image_tag_should_pick_the_theme_image_if_it_exists
1446 theme = Redmine::Themes.themes.last
1446 theme = Redmine::Themes.themes.last
1447 theme.images << 'image.png'
1447 theme.images << 'image.png'
1448
1448
1449 with_settings :ui_theme => theme.id do
1449 with_settings :ui_theme => theme.id do
1450 assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png")
1450 assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png")
1451 assert_match %|src="/images/other.png"|, image_tag("other.png")
1451 assert_match %|src="/images/other.png"|, image_tag("other.png")
1452 end
1452 end
1453 ensure
1453 ensure
1454 theme.images.delete 'image.png'
1454 theme.images.delete 'image.png'
1455 end
1455 end
1456
1456
1457 def test_image_tag_sfor_plugin_should_pick_the_plugin_image
1457 def test_image_tag_sfor_plugin_should_pick_the_plugin_image
1458 assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo)
1458 assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo)
1459 end
1459 end
1460
1460
1461 def test_javascript_include_tag_should_pick_the_default_javascript
1461 def test_javascript_include_tag_should_pick_the_default_javascript
1462 assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts")
1462 assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts")
1463 end
1463 end
1464
1464
1465 def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
1465 def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript
1466 assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
1466 assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo)
1467 end
1467 end
1468
1468
1469 def test_raw_json_should_escape_closing_tags
1469 def test_raw_json_should_escape_closing_tags
1470 s = raw_json(["<foo>bar</foo>"])
1470 s = raw_json(["<foo>bar</foo>"])
1471 assert_include '\/foo', s
1471 assert_include '\/foo', s
1472 end
1472 end
1473
1473
1474 def test_raw_json_should_be_html_safe
1474 def test_raw_json_should_be_html_safe
1475 s = raw_json(["foo"])
1475 s = raw_json(["foo"])
1476 assert s.html_safe?
1476 assert s.html_safe?
1477 end
1477 end
1478
1478
1479 def test_html_title_should_app_title_if_not_set
1479 def test_html_title_should_app_title_if_not_set
1480 assert_equal 'Redmine', html_title
1480 assert_equal 'Redmine', html_title
1481 end
1481 end
1482
1482
1483 def test_html_title_should_join_items
1483 def test_html_title_should_join_items
1484 html_title 'Foo', 'Bar'
1484 html_title 'Foo', 'Bar'
1485 assert_equal 'Foo - Bar - Redmine', html_title
1485 assert_equal 'Foo - Bar - Redmine', html_title
1486 end
1486 end
1487
1487
1488 def test_html_title_should_append_current_project_name
1488 def test_html_title_should_append_current_project_name
1489 @project = Project.find(1)
1489 @project = Project.find(1)
1490 html_title 'Foo', 'Bar'
1490 html_title 'Foo', 'Bar'
1491 assert_equal 'Foo - Bar - eCookbook - Redmine', html_title
1491 assert_equal 'Foo - Bar - eCookbook - Redmine', html_title
1492 end
1492 end
1493
1493
1494 def test_title_should_return_a_h2_tag
1494 def test_title_should_return_a_h2_tag
1495 assert_equal '<h2>Foo</h2>', title('Foo')
1495 assert_equal '<h2>Foo</h2>', title('Foo')
1496 end
1496 end
1497
1497
1498 def test_title_should_set_html_title
1498 def test_title_should_set_html_title
1499 title('Foo')
1499 title('Foo')
1500 assert_equal 'Foo - Redmine', html_title
1500 assert_equal 'Foo - Redmine', html_title
1501 end
1501 end
1502
1502
1503 def test_title_should_turn_arrays_into_links
1503 def test_title_should_turn_arrays_into_links
1504 assert_equal '<h2><a href="/foo">Foo</a></h2>', title(['Foo', '/foo'])
1504 assert_equal '<h2><a href="/foo">Foo</a></h2>', title(['Foo', '/foo'])
1505 assert_equal 'Foo - Redmine', html_title
1505 assert_equal 'Foo - Redmine', html_title
1506 end
1506 end
1507
1507
1508 def test_title_should_join_items
1508 def test_title_should_join_items
1509 assert_equal '<h2>Foo &#187; Bar</h2>', title('Foo', 'Bar')
1509 assert_equal '<h2>Foo &#187; Bar</h2>', title('Foo', 'Bar')
1510 assert_equal 'Bar - Foo - Redmine', html_title
1510 assert_equal 'Bar - Foo - Redmine', html_title
1511 end
1511 end
1512
1512
1513 def test_favicon_path
1513 def test_favicon_path
1514 assert_match %r{^/favicon\.ico}, favicon_path
1514 assert_match %r{^/favicon\.ico}, favicon_path
1515 end
1515 end
1516
1516
1517 def test_favicon_path_with_suburi
1517 def test_favicon_path_with_suburi
1518 Redmine::Utils.relative_url_root = '/foo'
1518 Redmine::Utils.relative_url_root = '/foo'
1519 assert_match %r{^/foo/favicon\.ico}, favicon_path
1519 assert_match %r{^/foo/favicon\.ico}, favicon_path
1520 ensure
1520 ensure
1521 Redmine::Utils.relative_url_root = ''
1521 Redmine::Utils.relative_url_root = ''
1522 end
1522 end
1523
1523
1524 def test_favicon_url
1524 def test_favicon_url
1525 assert_match %r{^http://test\.host/favicon\.ico}, favicon_url
1525 assert_match %r{^http://test\.host/favicon\.ico}, favicon_url
1526 end
1526 end
1527
1527
1528 def test_favicon_url_with_suburi
1528 def test_favicon_url_with_suburi
1529 Redmine::Utils.relative_url_root = '/foo'
1529 Redmine::Utils.relative_url_root = '/foo'
1530 assert_match %r{^http://test\.host/foo/favicon\.ico}, favicon_url
1530 assert_match %r{^http://test\.host/foo/favicon\.ico}, favicon_url
1531 ensure
1531 ensure
1532 Redmine::Utils.relative_url_root = ''
1532 Redmine::Utils.relative_url_root = ''
1533 end
1533 end
1534
1534
1535 def test_truncate_single_line
1535 def test_truncate_single_line
1536 str = "01234"
1536 str = "01234"
1537 result = truncate_single_line_raw("#{str}\n#{str}", 10)
1537 result = truncate_single_line_raw("#{str}\n#{str}", 10)
1538 assert_equal "01234 0...", result
1538 assert_equal "01234 0...", result
1539 assert !result.html_safe?
1539 assert !result.html_safe?
1540 result = truncate_single_line_raw("#{str}<&#>\n#{str}#{str}", 16)
1540 result = truncate_single_line_raw("#{str}<&#>\n#{str}#{str}", 16)
1541 assert_equal "01234<&#> 012...", result
1541 assert_equal "01234<&#> 012...", result
1542 assert !result.html_safe?
1542 assert !result.html_safe?
1543 end
1543 end
1544
1544
1545 def test_truncate_single_line_non_ascii
1545 def test_truncate_single_line_non_ascii
1546 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
1546 ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8')
1547 result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10)
1547 result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10)
1548 assert_equal "#{ja} #{ja}...", result
1548 assert_equal "#{ja} #{ja}...", result
1549 assert !result.html_safe?
1549 assert !result.html_safe?
1550 end
1550 end
1551
1551
1552 def test_back_url_should_remove_utf8_checkmark_from_referer
1552 def test_back_url_should_remove_utf8_checkmark_from_referer
1553 stubs(:request).returns(stub(:env => {'HTTP_REFERER' => "/path?utf8=\u2713&foo=bar"}))
1553 stubs(:request).returns(stub(:env => {'HTTP_REFERER' => "/path?utf8=\u2713&foo=bar"}))
1554 assert_equal "/path?foo=bar", back_url
1554 assert_equal "/path?foo=bar", back_url
1555 end
1555 end
1556
1556
1557 def test_hours_formatting
1557 def test_hours_formatting
1558 set_language_if_valid 'en'
1558 set_language_if_valid 'en'
1559
1559
1560 with_settings :timespan_format => 'minutes' do
1560 with_settings :timespan_format => 'minutes' do
1561 assert_equal '0:45', format_hours(0.75)
1561 assert_equal '0:45', format_hours(0.75)
1562 assert_equal '0:45 h', l_hours_short(0.75)
1562 assert_equal '0:45 h', l_hours_short(0.75)
1563 assert_equal '0:45 hour', l_hours(0.75)
1563 assert_equal '0:45 hour', l_hours(0.75)
1564 end
1564 end
1565 with_settings :timespan_format => 'decimal' do
1565 with_settings :timespan_format => 'decimal' do
1566 assert_equal '0.75', format_hours(0.75)
1566 assert_equal '0.75', format_hours(0.75)
1567 assert_equal '0.75 h', l_hours_short(0.75)
1567 assert_equal '0.75 h', l_hours_short(0.75)
1568 assert_equal '0.75 hour', l_hours(0.75)
1568 assert_equal '0.75 hour', l_hours(0.75)
1569 end
1569 end
1570 end
1570 end
1571
1571
1572 def test_html_hours
1572 def test_html_hours
1573 assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">:45</span>', html_hours('0:45')
1573 assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">:45</span>', html_hours('0:45')
1574 assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">.75</span>', html_hours('0.75')
1574 assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">.75</span>', html_hours('0.75')
1575 end
1575 end
1576 end
1576 end
@@ -1,3000 +1,3000
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssueTest < ActiveSupport::TestCase
20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles,
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles,
22 :groups_users,
22 :groups_users,
23 :trackers, :projects_trackers,
23 :trackers, :projects_trackers,
24 :enabled_modules,
24 :enabled_modules,
25 :versions,
25 :versions,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 :enumerations,
27 :enumerations,
28 :issues, :journals, :journal_details,
28 :issues, :journals, :journal_details,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 :time_entries
30 :time_entries
31
31
32 include Redmine::I18n
32 include Redmine::I18n
33
33
34 def setup
34 def setup
35 set_language_if_valid 'en'
35 set_language_if_valid 'en'
36 end
36 end
37
37
38 def teardown
38 def teardown
39 User.current = nil
39 User.current = nil
40 end
40 end
41
41
42 def test_initialize
42 def test_initialize
43 issue = Issue.new
43 issue = Issue.new
44
44
45 assert_nil issue.project_id
45 assert_nil issue.project_id
46 assert_nil issue.tracker_id
46 assert_nil issue.tracker_id
47 assert_nil issue.status_id
47 assert_nil issue.status_id
48 assert_nil issue.author_id
48 assert_nil issue.author_id
49 assert_nil issue.assigned_to_id
49 assert_nil issue.assigned_to_id
50 assert_nil issue.category_id
50 assert_nil issue.category_id
51
51
52 assert_equal IssuePriority.default, issue.priority
52 assert_equal IssuePriority.default, issue.priority
53 end
53 end
54
54
55 def test_create
55 def test_create
56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
57 :status_id => 1, :priority => IssuePriority.all.first,
57 :status_id => 1, :priority => IssuePriority.all.first,
58 :subject => 'test_create',
58 :subject => 'test_create',
59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
60 assert issue.save
60 assert issue.save
61 issue.reload
61 issue.reload
62 assert_equal 1.5, issue.estimated_hours
62 assert_equal 1.5, issue.estimated_hours
63 end
63 end
64
64
65 def test_create_minimal
65 def test_create_minimal
66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
67 assert issue.save
67 assert issue.save
68 assert_equal issue.tracker.default_status, issue.status
68 assert_equal issue.tracker.default_status, issue.status
69 assert issue.description.nil?
69 assert issue.description.nil?
70 assert_nil issue.estimated_hours
70 assert_nil issue.estimated_hours
71 end
71 end
72
72
73 def test_create_with_all_fields_disabled
73 def test_create_with_all_fields_disabled
74 tracker = Tracker.find(1)
74 tracker = Tracker.find(1)
75 tracker.core_fields = []
75 tracker.core_fields = []
76 tracker.save!
76 tracker.save!
77
77
78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
79 assert_save issue
79 assert_save issue
80 end
80 end
81
81
82 def test_start_date_format_should_be_validated
82 def test_start_date_format_should_be_validated
83 set_language_if_valid 'en'
83 set_language_if_valid 'en'
84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
85 issue = Issue.new(:start_date => invalid_date)
85 issue = Issue.new(:start_date => invalid_date)
86 assert !issue.valid?
86 assert !issue.valid?
87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
88 end
88 end
89 end
89 end
90
90
91 def test_due_date_format_should_be_validated
91 def test_due_date_format_should_be_validated
92 set_language_if_valid 'en'
92 set_language_if_valid 'en'
93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
94 issue = Issue.new(:due_date => invalid_date)
94 issue = Issue.new(:due_date => invalid_date)
95 assert !issue.valid?
95 assert !issue.valid?
96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
97 end
97 end
98 end
98 end
99
99
100 def test_due_date_lesser_than_start_date_should_not_validate
100 def test_due_date_lesser_than_start_date_should_not_validate
101 set_language_if_valid 'en'
101 set_language_if_valid 'en'
102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
103 assert !issue.valid?
103 assert !issue.valid?
104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
105 end
105 end
106
106
107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
108 issue = Issue.generate(:start_date => '2013-06-04')
108 issue = Issue.generate(:start_date => '2013-06-04')
109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
110 assert !issue.valid?
110 assert !issue.valid?
111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
112 end
112 end
113
113
114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
115 issue = Issue.generate!(:start_date => '2013-06-04')
115 issue = Issue.generate!(:start_date => '2013-06-04')
116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
117 issue.start_date = '2013-06-07'
117 issue.start_date = '2013-06-07'
118 assert !issue.valid?
118 assert !issue.valid?
119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
120 end
120 end
121
121
122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
123 issue = Issue.generate!(:start_date => '2013-06-04')
123 issue = Issue.generate!(:start_date => '2013-06-04')
124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
125 assert issue.valid?
125 assert issue.valid?
126 end
126 end
127
127
128 def test_estimated_hours_should_be_validated
128 def test_estimated_hours_should_be_validated
129 set_language_if_valid 'en'
129 set_language_if_valid 'en'
130 ['-2'].each do |invalid|
130 ['-2'].each do |invalid|
131 issue = Issue.new(:estimated_hours => invalid)
131 issue = Issue.new(:estimated_hours => invalid)
132 assert !issue.valid?
132 assert !issue.valid?
133 assert_include 'Estimated time is invalid', issue.errors.full_messages
133 assert_include 'Estimated time is invalid', issue.errors.full_messages
134 end
134 end
135 end
135 end
136
136
137 def test_create_with_required_custom_field
137 def test_create_with_required_custom_field
138 set_language_if_valid 'en'
138 set_language_if_valid 'en'
139 field = IssueCustomField.find_by_name('Database')
139 field = IssueCustomField.find_by_name('Database')
140 field.update!(:is_required => true)
140 field.update!(:is_required => true)
141
141
142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
143 :status_id => 1, :subject => 'test_create',
143 :status_id => 1, :subject => 'test_create',
144 :description => 'IssueTest#test_create_with_required_custom_field')
144 :description => 'IssueTest#test_create_with_required_custom_field')
145 assert issue.available_custom_fields.include?(field)
145 assert issue.available_custom_fields.include?(field)
146 # No value for the custom field
146 # No value for the custom field
147 assert !issue.save
147 assert !issue.save
148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
149 # Blank value
149 # Blank value
150 issue.custom_field_values = { field.id => '' }
150 issue.custom_field_values = { field.id => '' }
151 assert !issue.save
151 assert !issue.save
152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
153 # Invalid value
153 # Invalid value
154 issue.custom_field_values = { field.id => 'SQLServer' }
154 issue.custom_field_values = { field.id => 'SQLServer' }
155 assert !issue.save
155 assert !issue.save
156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
157 # Valid value
157 # Valid value
158 issue.custom_field_values = { field.id => 'PostgreSQL' }
158 issue.custom_field_values = { field.id => 'PostgreSQL' }
159 assert issue.save
159 assert issue.save
160 issue.reload
160 issue.reload
161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
162 end
162 end
163
163
164 def test_create_with_group_assignment
164 def test_create_with_group_assignment
165 with_settings :issue_group_assignment => '1' do
165 with_settings :issue_group_assignment => '1' do
166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
167 :subject => 'Group assignment',
167 :subject => 'Group assignment',
168 :assigned_to_id => 11).save
168 :assigned_to_id => 11).save
169 issue = Issue.order('id DESC').first
169 issue = Issue.order('id DESC').first
170 assert_kind_of Group, issue.assigned_to
170 assert_kind_of Group, issue.assigned_to
171 assert_equal Group.find(11), issue.assigned_to
171 assert_equal Group.find(11), issue.assigned_to
172 end
172 end
173 end
173 end
174
174
175 def test_create_with_parent_issue_id
175 def test_create_with_parent_issue_id
176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
177 :author_id => 1, :subject => 'Group assignment',
177 :author_id => 1, :subject => 'Group assignment',
178 :parent_issue_id => 1)
178 :parent_issue_id => 1)
179 assert_save issue
179 assert_save issue
180 assert_equal 1, issue.parent_issue_id
180 assert_equal 1, issue.parent_issue_id
181 assert_equal Issue.find(1), issue.parent
181 assert_equal Issue.find(1), issue.parent
182 end
182 end
183
183
184 def test_create_with_sharp_parent_issue_id
184 def test_create_with_sharp_parent_issue_id
185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
186 :author_id => 1, :subject => 'Group assignment',
186 :author_id => 1, :subject => 'Group assignment',
187 :parent_issue_id => "#1")
187 :parent_issue_id => "#1")
188 assert_save issue
188 assert_save issue
189 assert_equal 1, issue.parent_issue_id
189 assert_equal 1, issue.parent_issue_id
190 assert_equal Issue.find(1), issue.parent
190 assert_equal Issue.find(1), issue.parent
191 end
191 end
192
192
193 def test_create_with_invalid_parent_issue_id
193 def test_create_with_invalid_parent_issue_id
194 set_language_if_valid 'en'
194 set_language_if_valid 'en'
195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
196 :author_id => 1, :subject => 'Group assignment',
196 :author_id => 1, :subject => 'Group assignment',
197 :parent_issue_id => '01ABC')
197 :parent_issue_id => '01ABC')
198 assert !issue.save
198 assert !issue.save
199 assert_equal '01ABC', issue.parent_issue_id
199 assert_equal '01ABC', issue.parent_issue_id
200 assert_include 'Parent task is invalid', issue.errors.full_messages
200 assert_include 'Parent task is invalid', issue.errors.full_messages
201 end
201 end
202
202
203 def test_create_with_invalid_sharp_parent_issue_id
203 def test_create_with_invalid_sharp_parent_issue_id
204 set_language_if_valid 'en'
204 set_language_if_valid 'en'
205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
206 :author_id => 1, :subject => 'Group assignment',
206 :author_id => 1, :subject => 'Group assignment',
207 :parent_issue_id => '#01ABC')
207 :parent_issue_id => '#01ABC')
208 assert !issue.save
208 assert !issue.save
209 assert_equal '#01ABC', issue.parent_issue_id
209 assert_equal '#01ABC', issue.parent_issue_id
210 assert_include 'Parent task is invalid', issue.errors.full_messages
210 assert_include 'Parent task is invalid', issue.errors.full_messages
211 end
211 end
212
212
213 def assert_visibility_match(user, issues)
213 def assert_visibility_match(user, issues)
214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
215 end
215 end
216
216
217 def test_visible_scope_for_anonymous
217 def test_visible_scope_for_anonymous
218 # Anonymous user should see issues of public projects only
218 # Anonymous user should see issues of public projects only
219 issues = Issue.visible(User.anonymous).to_a
219 issues = Issue.visible(User.anonymous).to_a
220 assert issues.any?
220 assert issues.any?
221 assert_nil issues.detect {|issue| !issue.project.is_public?}
221 assert_nil issues.detect {|issue| !issue.project.is_public?}
222 assert_nil issues.detect {|issue| issue.is_private?}
222 assert_nil issues.detect {|issue| issue.is_private?}
223 assert_visibility_match User.anonymous, issues
223 assert_visibility_match User.anonymous, issues
224 end
224 end
225
225
226 def test_visible_scope_for_anonymous_without_view_issues_permissions
226 def test_visible_scope_for_anonymous_without_view_issues_permissions
227 # Anonymous user should not see issues without permission
227 # Anonymous user should not see issues without permission
228 Role.anonymous.remove_permission!(:view_issues)
228 Role.anonymous.remove_permission!(:view_issues)
229 issues = Issue.visible(User.anonymous).to_a
229 issues = Issue.visible(User.anonymous).to_a
230 assert issues.empty?
230 assert issues.empty?
231 assert_visibility_match User.anonymous, issues
231 assert_visibility_match User.anonymous, issues
232 end
232 end
233
233
234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
235 Role.anonymous.remove_permission!(:view_issues)
235 Role.anonymous.remove_permission!(:view_issues)
236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
237
237
238 issues = Issue.visible(User.anonymous).all
238 issues = Issue.visible(User.anonymous).all
239 assert issues.any?
239 assert issues.any?
240 assert_equal [1], issues.map(&:project_id).uniq.sort
240 assert_equal [1], issues.map(&:project_id).uniq.sort
241 assert_visibility_match User.anonymous, issues
241 assert_visibility_match User.anonymous, issues
242 end
242 end
243
243
244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
245 Role.anonymous.update!(:issues_visibility => 'default')
245 Role.anonymous.update!(:issues_visibility => 'default')
246 issue = Issue.generate!(:author => User.anonymous, :is_private => true)
246 issue = Issue.generate!(:author => User.anonymous, :is_private => true)
247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
248 assert !issue.visible?(User.anonymous)
248 assert !issue.visible?(User.anonymous)
249 end
249 end
250
250
251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
252 assert Role.anonymous.update!(:issues_visibility => 'own')
252 assert Role.anonymous.update!(:issues_visibility => 'own')
253 issue = Issue.generate!(:author => User.anonymous, :is_private => true)
253 issue = Issue.generate!(:author => User.anonymous, :is_private => true)
254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
255 assert !issue.visible?(User.anonymous)
255 assert !issue.visible?(User.anonymous)
256 end
256 end
257
257
258 def test_visible_scope_for_non_member
258 def test_visible_scope_for_non_member
259 user = User.find(9)
259 user = User.find(9)
260 assert user.projects.empty?
260 assert user.projects.empty?
261 # Non member user should see issues of public projects only
261 # Non member user should see issues of public projects only
262 issues = Issue.visible(user).to_a
262 issues = Issue.visible(user).to_a
263 assert issues.any?
263 assert issues.any?
264 assert_nil issues.detect {|issue| !issue.project.is_public?}
264 assert_nil issues.detect {|issue| !issue.project.is_public?}
265 assert_nil issues.detect {|issue| issue.is_private?}
265 assert_nil issues.detect {|issue| issue.is_private?}
266 assert_visibility_match user, issues
266 assert_visibility_match user, issues
267 end
267 end
268
268
269 def test_visible_scope_for_non_member_with_own_issues_visibility
269 def test_visible_scope_for_non_member_with_own_issues_visibility
270 Role.non_member.update! :issues_visibility => 'own'
270 Role.non_member.update! :issues_visibility => 'own'
271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
272 user = User.find(9)
272 user = User.find(9)
273
273
274 issues = Issue.visible(user).to_a
274 issues = Issue.visible(user).to_a
275 assert issues.any?
275 assert issues.any?
276 assert_nil issues.detect {|issue| issue.author != user}
276 assert_nil issues.detect {|issue| issue.author != user}
277 assert_visibility_match user, issues
277 assert_visibility_match user, issues
278 end
278 end
279
279
280 def test_visible_scope_for_non_member_without_view_issues_permissions
280 def test_visible_scope_for_non_member_without_view_issues_permissions
281 # Non member user should not see issues without permission
281 # Non member user should not see issues without permission
282 Role.non_member.remove_permission!(:view_issues)
282 Role.non_member.remove_permission!(:view_issues)
283 user = User.find(9)
283 user = User.find(9)
284 assert user.projects.empty?
284 assert user.projects.empty?
285 issues = Issue.visible(user).to_a
285 issues = Issue.visible(user).to_a
286 assert issues.empty?
286 assert issues.empty?
287 assert_visibility_match user, issues
287 assert_visibility_match user, issues
288 end
288 end
289
289
290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
291 Role.non_member.remove_permission!(:view_issues)
291 Role.non_member.remove_permission!(:view_issues)
292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
293 user = User.find(9)
293 user = User.find(9)
294
294
295 issues = Issue.visible(user).all
295 issues = Issue.visible(user).all
296 assert issues.any?
296 assert issues.any?
297 assert_equal [1], issues.map(&:project_id).uniq.sort
297 assert_equal [1], issues.map(&:project_id).uniq.sort
298 assert_visibility_match user, issues
298 assert_visibility_match user, issues
299 end
299 end
300
300
301 def test_visible_scope_for_member
301 def test_visible_scope_for_member
302 user = User.find(9)
302 user = User.find(9)
303 # User should see issues of projects for which user has view_issues permissions only
303 # User should see issues of projects for which user has view_issues permissions only
304 Role.non_member.remove_permission!(:view_issues)
304 Role.non_member.remove_permission!(:view_issues)
305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
306 issues = Issue.visible(user).to_a
306 issues = Issue.visible(user).to_a
307 assert issues.any?
307 assert issues.any?
308 assert_nil issues.detect {|issue| issue.project_id != 3}
308 assert_nil issues.detect {|issue| issue.project_id != 3}
309 assert_nil issues.detect {|issue| issue.is_private?}
309 assert_nil issues.detect {|issue| issue.is_private?}
310 assert_visibility_match user, issues
310 assert_visibility_match user, issues
311 end
311 end
312
312
313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
314 Role.non_member.add_permission!(:view_issues)
314 Role.non_member.add_permission!(:view_issues)
315 Role.find(1).remove_permission!(:view_issues)
315 Role.find(1).remove_permission!(:view_issues)
316 user = User.find(2)
316 user = User.find(2)
317
317
318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
320 end
320 end
321
321
322 def test_visible_scope_with_custom_non_member_role_having_restricted_permission
322 def test_visible_scope_with_custom_non_member_role_having_restricted_permission
323 role = Role.generate!(:permissions => [:view_project])
323 role = Role.generate!(:permissions => [:view_project])
324 assert Role.non_member.has_permission?(:view_issues)
324 assert Role.non_member.has_permission?(:view_issues)
325 user = User.generate!
325 user = User.generate!
326 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
326 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
327
327
328 issues = Issue.visible(user).to_a
328 issues = Issue.visible(user).to_a
329 assert issues.any?
329 assert issues.any?
330 assert_nil issues.detect {|issue| issue.project_id == 1}
330 assert_nil issues.detect {|issue| issue.project_id == 1}
331 end
331 end
332
332
333 def test_visible_scope_with_custom_non_member_role_having_extended_permission
333 def test_visible_scope_with_custom_non_member_role_having_extended_permission
334 role = Role.generate!(:permissions => [:view_project, :view_issues])
334 role = Role.generate!(:permissions => [:view_project, :view_issues])
335 Role.non_member.remove_permission!(:view_issues)
335 Role.non_member.remove_permission!(:view_issues)
336 user = User.generate!
336 user = User.generate!
337 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
337 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
338
338
339 issues = Issue.visible(user).to_a
339 issues = Issue.visible(user).to_a
340 assert issues.any?
340 assert issues.any?
341 assert_not_nil issues.detect {|issue| issue.project_id == 1}
341 assert_not_nil issues.detect {|issue| issue.project_id == 1}
342 end
342 end
343
343
344 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
344 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
345 user = User.find(8)
345 user = User.find(8)
346 assert user.groups.any?
346 assert user.groups.any?
347 group = user.groups.first
347 group = user.groups.first
348 Member.create!(:principal => group, :project_id => 1, :role_ids => [2])
348 Member.create!(:principal => group, :project_id => 1, :role_ids => [2])
349 Role.non_member.remove_permission!(:view_issues)
349 Role.non_member.remove_permission!(:view_issues)
350
350
351 with_settings :issue_group_assignment => '1' do
351 with_settings :issue_group_assignment => '1' do
352 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
352 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
353 :status_id => 1, :priority => IssuePriority.all.first,
353 :status_id => 1, :priority => IssuePriority.all.first,
354 :subject => 'Assignment test',
354 :subject => 'Assignment test',
355 :assigned_to => group,
355 :assigned_to => group,
356 :is_private => true)
356 :is_private => true)
357
357
358 Role.find(2).update! :issues_visibility => 'default'
358 Role.find(2).update! :issues_visibility => 'default'
359 issues = Issue.visible(User.find(8)).to_a
359 issues = Issue.visible(User.find(8)).to_a
360 assert issues.any?
360 assert issues.any?
361 assert issues.include?(issue)
361 assert issues.include?(issue)
362
362
363 Role.find(2).update! :issues_visibility => 'own'
363 Role.find(2).update! :issues_visibility => 'own'
364 issues = Issue.visible(User.find(8)).to_a
364 issues = Issue.visible(User.find(8)).to_a
365 assert issues.any?
365 assert issues.any?
366 assert_include issue, issues
366 assert_include issue, issues
367 end
367 end
368 end
368 end
369
369
370 def test_visible_scope_for_member_with_limited_tracker_ids
370 def test_visible_scope_for_member_with_limited_tracker_ids
371 role = Role.find(1)
371 role = Role.find(1)
372 role.set_permission_trackers :view_issues, [2]
372 role.set_permission_trackers :view_issues, [2]
373 role.save!
373 role.save!
374 user = User.find(2)
374 user = User.find(2)
375
375
376 issues = Issue.where(:project_id => 1).visible(user).to_a
376 issues = Issue.where(:project_id => 1).visible(user).to_a
377 assert issues.any?
377 assert issues.any?
378 assert_equal [2], issues.map(&:tracker_id).uniq
378 assert_equal [2], issues.map(&:tracker_id).uniq
379
379
380 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
380 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
381 end
381 end
382
382
383 def test_visible_scope_should_consider_tracker_ids_on_each_project
383 def test_visible_scope_should_consider_tracker_ids_on_each_project
384 user = User.generate!
384 user = User.generate!
385
385
386 project1 = Project.generate!
386 project1 = Project.generate!
387 role1 = Role.generate!
387 role1 = Role.generate!
388 role1.add_permission! :view_issues
388 role1.add_permission! :view_issues
389 role1.set_permission_trackers :view_issues, :all
389 role1.set_permission_trackers :view_issues, :all
390 role1.save!
390 role1.save!
391 User.add_to_project(user, project1, role1)
391 User.add_to_project(user, project1, role1)
392
392
393 project2 = Project.generate!
393 project2 = Project.generate!
394 role2 = Role.generate!
394 role2 = Role.generate!
395 role2.add_permission! :view_issues
395 role2.add_permission! :view_issues
396 role2.set_permission_trackers :view_issues, [2]
396 role2.set_permission_trackers :view_issues, [2]
397 role2.save!
397 role2.save!
398 User.add_to_project(user, project2, role2)
398 User.add_to_project(user, project2, role2)
399
399
400 visible_issues = [
400 visible_issues = [
401 Issue.generate!(:project => project1, :tracker_id => 1),
401 Issue.generate!(:project => project1, :tracker_id => 1),
402 Issue.generate!(:project => project1, :tracker_id => 2),
402 Issue.generate!(:project => project1, :tracker_id => 2),
403 Issue.generate!(:project => project2, :tracker_id => 2)
403 Issue.generate!(:project => project2, :tracker_id => 2)
404 ]
404 ]
405 hidden_issue = Issue.generate!(:project => project2, :tracker_id => 1)
405 hidden_issue = Issue.generate!(:project => project2, :tracker_id => 1)
406
406
407 issues = Issue.where(:project_id => [project1.id, project2.id]).visible(user)
407 issues = Issue.where(:project_id => [project1.id, project2.id]).visible(user)
408 assert_equal visible_issues.map(&:id), issues.ids.sort
408 assert_equal visible_issues.map(&:id), issues.ids.sort
409
409
410 assert visible_issues.all? {|issue| issue.visible?(user)}
410 assert visible_issues.all? {|issue| issue.visible?(user)}
411 assert !hidden_issue.visible?(user)
411 assert !hidden_issue.visible?(user)
412 end
412 end
413
413
414 def test_visible_scope_should_not_consider_roles_without_view_issues_permission
414 def test_visible_scope_should_not_consider_roles_without_view_issues_permission
415 user = User.generate!
415 user = User.generate!
416 role1 = Role.generate!
416 role1 = Role.generate!
417 role1.remove_permission! :view_issues
417 role1.remove_permission! :view_issues
418 role1.set_permission_trackers :view_issues, :all
418 role1.set_permission_trackers :view_issues, :all
419 role1.save!
419 role1.save!
420 role2 = Role.generate!
420 role2 = Role.generate!
421 role2.add_permission! :view_issues
421 role2.add_permission! :view_issues
422 role2.set_permission_trackers :view_issues, [2]
422 role2.set_permission_trackers :view_issues, [2]
423 role2.save!
423 role2.save!
424 User.add_to_project(user, Project.find(1), [role1, role2])
424 User.add_to_project(user, Project.find(1), [role1, role2])
425
425
426 issues = Issue.where(:project_id => 1).visible(user).to_a
426 issues = Issue.where(:project_id => 1).visible(user).to_a
427 assert issues.any?
427 assert issues.any?
428 assert_equal [2], issues.map(&:tracker_id).uniq
428 assert_equal [2], issues.map(&:tracker_id).uniq
429
429
430 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
430 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
431 end
431 end
432
432
433 def test_visible_scope_for_admin
433 def test_visible_scope_for_admin
434 user = User.find(1)
434 user = User.find(1)
435 user.members.each(&:destroy)
435 user.members.each(&:destroy)
436 assert user.projects.empty?
436 assert user.projects.empty?
437 issues = Issue.visible(user).to_a
437 issues = Issue.visible(user).to_a
438 assert issues.any?
438 assert issues.any?
439 # Admin should see issues on private projects that admin does not belong to
439 # Admin should see issues on private projects that admin does not belong to
440 assert issues.detect {|issue| !issue.project.is_public?}
440 assert issues.detect {|issue| !issue.project.is_public?}
441 # Admin should see private issues of other users
441 # Admin should see private issues of other users
442 assert issues.detect {|issue| issue.is_private? && issue.author != user}
442 assert issues.detect {|issue| issue.is_private? && issue.author != user}
443 assert_visibility_match user, issues
443 assert_visibility_match user, issues
444 end
444 end
445
445
446 def test_visible_scope_with_project
446 def test_visible_scope_with_project
447 project = Project.find(1)
447 project = Project.find(1)
448 issues = Issue.visible(User.find(2), :project => project).to_a
448 issues = Issue.visible(User.find(2), :project => project).to_a
449 projects = issues.collect(&:project).uniq
449 projects = issues.collect(&:project).uniq
450 assert_equal 1, projects.size
450 assert_equal 1, projects.size
451 assert_equal project, projects.first
451 assert_equal project, projects.first
452 end
452 end
453
453
454 def test_visible_scope_with_project_and_subprojects
454 def test_visible_scope_with_project_and_subprojects
455 project = Project.find(1)
455 project = Project.find(1)
456 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
456 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
457 projects = issues.collect(&:project).uniq
457 projects = issues.collect(&:project).uniq
458 assert projects.size > 1
458 assert projects.size > 1
459 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
459 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
460 end
460 end
461
461
462 def test_visible_and_nested_set_scopes
462 def test_visible_and_nested_set_scopes
463 user = User.generate!
463 user = User.generate!
464 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
464 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
465 parent = Issue.generate!(:assigned_to => user)
465 parent = Issue.generate!(:assigned_to => user)
466 assert parent.visible?(user)
466 assert parent.visible?(user)
467 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
467 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
468 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
468 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
469 parent.reload
469 parent.reload
470 child1.reload
470 child1.reload
471 child2.reload
471 child2.reload
472 assert child1.visible?(user)
472 assert child1.visible?(user)
473 assert child2.visible?(user)
473 assert child2.visible?(user)
474 assert_equal 2, parent.descendants.count
474 assert_equal 2, parent.descendants.count
475 assert_equal 2, parent.descendants.visible(user).count
475 assert_equal 2, parent.descendants.visible(user).count
476 # awesome_nested_set 2-1-stable branch has regression.
476 # awesome_nested_set 2-1-stable branch has regression.
477 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
477 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
478 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
478 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
479 assert_equal 2, parent.descendants.collect{|i| i}.size
479 assert_equal 2, parent.descendants.collect{|i| i}.size
480 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
480 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
481 end
481 end
482
482
483 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
483 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
484 user = User.new
484 user = User.new
485 assert_nothing_raised do
485 assert_nothing_raised do
486 Issue.visible(user).to_a
486 Issue.visible(user).to_a
487 end
487 end
488 end
488 end
489
489
490 def test_open_scope
490 def test_open_scope
491 issues = Issue.open.to_a
491 issues = Issue.open.to_a
492 assert_nil issues.detect(&:closed?)
492 assert_nil issues.detect(&:closed?)
493 end
493 end
494
494
495 def test_open_scope_with_arg
495 def test_open_scope_with_arg
496 issues = Issue.open(false).to_a
496 issues = Issue.open(false).to_a
497 assert_equal issues, issues.select(&:closed?)
497 assert_equal issues, issues.select(&:closed?)
498 end
498 end
499
499
500 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
500 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
501 version = Version.find(2)
501 version = Version.find(2)
502 assert version.fixed_issues.any?
502 assert version.fixed_issues.any?
503 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
503 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
504 end
504 end
505
505
506 def test_fixed_version_scope_with_empty_array_should_return_no_result
506 def test_fixed_version_scope_with_empty_array_should_return_no_result
507 assert_equal 0, Issue.fixed_version([]).count
507 assert_equal 0, Issue.fixed_version([]).count
508 end
508 end
509
509
510 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
510 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
511 user = User.generate!
511 user = User.generate!
512 issue = Issue.generate!
512 issue = Issue.generate!
513 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
513 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
514 assert_equal [issue], Issue.assigned_to(user).to_a
514 assert_equal [issue], Issue.assigned_to(user).to_a
515 end
515 end
516
516
517 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
517 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
518 group = Group.generate!
518 group = Group.generate!
519 user = User.generate!
519 user = User.generate!
520 group.users << user
520 group.users << user
521 issue = Issue.generate!
521 issue = Issue.generate!
522 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
522 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
523 assert_equal [issue], Issue.assigned_to(user).to_a
523 assert_equal [issue], Issue.assigned_to(user).to_a
524 end
524 end
525
525
526 def test_issue_should_be_readonly_on_closed_project
526 def test_issue_should_be_readonly_on_closed_project
527 issue = Issue.find(1)
527 issue = Issue.find(1)
528 user = User.find(1)
528 user = User.find(1)
529
529
530 assert_equal true, issue.visible?(user)
530 assert_equal true, issue.visible?(user)
531 assert_equal true, issue.editable?(user)
531 assert_equal true, issue.editable?(user)
532 assert_equal true, issue.deletable?(user)
532 assert_equal true, issue.deletable?(user)
533
533
534 issue.project.close
534 issue.project.close
535 issue.reload
535 issue.reload
536
536
537 assert_equal true, issue.visible?(user)
537 assert_equal true, issue.visible?(user)
538 assert_equal false, issue.editable?(user)
538 assert_equal false, issue.editable?(user)
539 assert_equal false, issue.deletable?(user)
539 assert_equal false, issue.deletable?(user)
540 end
540 end
541
541
542 def test_errors_full_messages_should_include_custom_fields_errors
542 def test_errors_full_messages_should_include_custom_fields_errors
543 field = IssueCustomField.find_by_name('Database')
543 field = IssueCustomField.find_by_name('Database')
544
544
545 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
545 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
546 :status_id => 1, :subject => 'test_create',
546 :status_id => 1, :subject => 'test_create',
547 :description => 'IssueTest#test_create_with_required_custom_field')
547 :description => 'IssueTest#test_create_with_required_custom_field')
548 assert issue.available_custom_fields.include?(field)
548 assert issue.available_custom_fields.include?(field)
549 # Invalid value
549 # Invalid value
550 issue.custom_field_values = { field.id => 'SQLServer' }
550 issue.custom_field_values = { field.id => 'SQLServer' }
551
551
552 assert !issue.valid?
552 assert !issue.valid?
553 assert_equal 1, issue.errors.full_messages.size
553 assert_equal 1, issue.errors.full_messages.size
554 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
554 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
555 issue.errors.full_messages.first
555 issue.errors.full_messages.first
556 end
556 end
557
557
558 def test_update_issue_with_required_custom_field
558 def test_update_issue_with_required_custom_field
559 field = IssueCustomField.find_by_name('Database')
559 field = IssueCustomField.find_by_name('Database')
560 field.update!(:is_required => true)
560 field.update!(:is_required => true)
561
561
562 issue = Issue.find(1)
562 issue = Issue.find(1)
563 assert_nil issue.custom_value_for(field)
563 assert_nil issue.custom_value_for(field)
564 assert issue.available_custom_fields.include?(field)
564 assert issue.available_custom_fields.include?(field)
565 # No change to custom values, issue can be saved
565 # No change to custom values, issue can be saved
566 assert issue.save
566 assert issue.save
567 # Blank value
567 # Blank value
568 issue.custom_field_values = { field.id => '' }
568 issue.custom_field_values = { field.id => '' }
569 assert !issue.save
569 assert !issue.save
570 # Valid value
570 # Valid value
571 issue.custom_field_values = { field.id => 'PostgreSQL' }
571 issue.custom_field_values = { field.id => 'PostgreSQL' }
572 assert issue.save
572 assert issue.save
573 issue.reload
573 issue.reload
574 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
574 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
575 end
575 end
576
576
577 def test_should_not_update_attributes_if_custom_fields_validation_fails
577 def test_should_not_update_attributes_if_custom_fields_validation_fails
578 issue = Issue.find(1)
578 issue = Issue.find(1)
579 field = IssueCustomField.find_by_name('Database')
579 field = IssueCustomField.find_by_name('Database')
580 assert issue.available_custom_fields.include?(field)
580 assert issue.available_custom_fields.include?(field)
581
581
582 issue.custom_field_values = { field.id => 'Invalid' }
582 issue.custom_field_values = { field.id => 'Invalid' }
583 issue.subject = 'Should be not be saved'
583 issue.subject = 'Should be not be saved'
584 assert !issue.save
584 assert !issue.save
585
585
586 issue.reload
586 issue.reload
587 assert_equal "Cannot print recipes", issue.subject
587 assert_equal "Cannot print recipes", issue.subject
588 end
588 end
589
589
590 def test_should_not_recreate_custom_values_objects_on_update
590 def test_should_not_recreate_custom_values_objects_on_update
591 field = IssueCustomField.find_by_name('Database')
591 field = IssueCustomField.find_by_name('Database')
592
592
593 issue = Issue.find(1)
593 issue = Issue.find(1)
594 issue.custom_field_values = { field.id => 'PostgreSQL' }
594 issue.custom_field_values = { field.id => 'PostgreSQL' }
595 assert issue.save
595 assert issue.save
596 custom_value = issue.custom_value_for(field)
596 custom_value = issue.custom_value_for(field)
597 issue.reload
597 issue.reload
598 issue.custom_field_values = { field.id => 'MySQL' }
598 issue.custom_field_values = { field.id => 'MySQL' }
599 assert issue.save
599 assert issue.save
600 issue.reload
600 issue.reload
601 assert_equal custom_value.id, issue.custom_value_for(field).id
601 assert_equal custom_value.id, issue.custom_value_for(field).id
602 end
602 end
603
603
604 def test_setting_project_should_set_version_to_default_version
604 def test_setting_project_should_set_version_to_default_version
605 version = Version.generate!(:project_id => 1)
605 version = Version.generate!(:project_id => 1)
606 Project.find(1).update!(:default_version_id => version.id)
606 Project.find(1).update!(:default_version_id => version.id)
607
607
608 issue = Issue.new(:project_id => 1)
608 issue = Issue.new(:project_id => 1)
609 assert_equal version, issue.fixed_version
609 assert_equal version, issue.fixed_version
610 end
610 end
611
611
612 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
612 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
613 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
613 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
614 :status_id => 1, :subject => 'Test',
614 :status_id => 1, :subject => 'Test',
615 :custom_field_values => {'2' => 'Test'})
615 :custom_field_values => {'2' => 'Test'})
616 assert !Tracker.find(2).custom_field_ids.include?(2)
616 assert !Tracker.find(2).custom_field_ids.include?(2)
617
617
618 issue = Issue.find(issue.id)
618 issue = Issue.find(issue.id)
619 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
619 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
620
620
621 issue = Issue.find(issue.id)
621 issue = Issue.find(issue.id)
622 custom_value = issue.custom_value_for(2)
622 custom_value = issue.custom_value_for(2)
623 assert_not_nil custom_value
623 assert_not_nil custom_value
624 assert_equal 'Test', custom_value.value
624 assert_equal 'Test', custom_value.value
625 end
625 end
626
626
627 def test_assigning_tracker_id_should_reload_custom_fields_values
627 def test_assigning_tracker_id_should_reload_custom_fields_values
628 issue = Issue.new(:project => Project.find(1))
628 issue = Issue.new(:project => Project.find(1))
629 assert issue.custom_field_values.empty?
629 assert issue.custom_field_values.empty?
630 issue.tracker_id = 1
630 issue.tracker_id = 1
631 assert issue.custom_field_values.any?
631 assert issue.custom_field_values.any?
632 end
632 end
633
633
634 def test_assigning_attributes_should_assign_project_and_tracker_first
634 def test_assigning_attributes_should_assign_project_and_tracker_first
635 seq = sequence('seq')
635 seq = sequence('seq')
636 issue = Issue.new
636 issue = Issue.new
637 issue.expects(:project_id=).in_sequence(seq)
637 issue.expects(:project_id=).in_sequence(seq)
638 issue.expects(:tracker_id=).in_sequence(seq)
638 issue.expects(:tracker_id=).in_sequence(seq)
639 issue.expects(:subject=).in_sequence(seq)
639 issue.expects(:subject=).in_sequence(seq)
640 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
640 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
641 end
641 end
642
642
643 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
643 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
644 attributes = ActiveSupport::OrderedHash.new
644 attributes = ActiveSupport::OrderedHash.new
645 attributes['custom_field_values'] = { '1' => 'MySQL' }
645 attributes['custom_field_values'] = { '1' => 'MySQL' }
646 attributes['tracker_id'] = '1'
646 attributes['tracker_id'] = '1'
647 issue = Issue.new(:project => Project.find(1))
647 issue = Issue.new(:project => Project.find(1))
648 issue.attributes = attributes
648 issue.attributes = attributes
649 assert_equal 'MySQL', issue.custom_field_value(1)
649 assert_equal 'MySQL', issue.custom_field_value(1)
650 end
650 end
651
651
652 def test_changing_tracker_should_clear_disabled_core_fields
652 def test_changing_tracker_should_clear_disabled_core_fields
653 tracker = Tracker.find(2)
653 tracker = Tracker.find(2)
654 tracker.core_fields = tracker.core_fields - %w(due_date)
654 tracker.core_fields = tracker.core_fields - %w(due_date)
655 tracker.save!
655 tracker.save!
656
656
657 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
657 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
658 issue.save!
658 issue.save!
659
659
660 issue.tracker_id = 2
660 issue.tracker_id = 2
661 issue.save!
661 issue.save!
662 assert_not_nil issue.start_date
662 assert_not_nil issue.start_date
663 assert_nil issue.due_date
663 assert_nil issue.due_date
664 end
664 end
665
665
666 def test_attribute_cleared_on_tracker_change_should_be_journalized
666 def test_attribute_cleared_on_tracker_change_should_be_journalized
667 CustomField.delete_all
667 CustomField.delete_all
668 tracker = Tracker.find(2)
668 tracker = Tracker.find(2)
669 tracker.core_fields = tracker.core_fields - %w(due_date)
669 tracker.core_fields = tracker.core_fields - %w(due_date)
670 tracker.save!
670 tracker.save!
671
671
672 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
672 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
673 issue.save!
673 issue.save!
674
674
675 assert_difference 'Journal.count' do
675 assert_difference 'Journal.count' do
676 issue.init_journal User.find(1)
676 issue.init_journal User.find(1)
677 issue.tracker_id = 2
677 issue.tracker_id = 2
678 issue.save!
678 issue.save!
679 assert_nil issue.due_date
679 assert_nil issue.due_date
680 end
680 end
681 journal = Journal.order('id DESC').first
681 journal = Journal.order('id DESC').first
682 details = journal.details.select {|d| d.prop_key == 'due_date'}
682 details = journal.details.select {|d| d.prop_key == 'due_date'}
683 assert_equal 1, details.count
683 assert_equal 1, details.count
684 end
684 end
685
685
686 def test_reload_should_reload_custom_field_values
686 def test_reload_should_reload_custom_field_values
687 issue = Issue.generate!
687 issue = Issue.generate!
688 issue.custom_field_values = {'2' => 'Foo'}
688 issue.custom_field_values = {'2' => 'Foo'}
689 issue.save!
689 issue.save!
690
690
691 issue = Issue.order('id desc').first
691 issue = Issue.order('id desc').first
692 assert_equal 'Foo', issue.custom_field_value(2)
692 assert_equal 'Foo', issue.custom_field_value(2)
693
693
694 issue.custom_field_values = {'2' => 'Bar'}
694 issue.custom_field_values = {'2' => 'Bar'}
695 assert_equal 'Bar', issue.custom_field_value(2)
695 assert_equal 'Bar', issue.custom_field_value(2)
696
696
697 issue.reload
697 issue.reload
698 assert_equal 'Foo', issue.custom_field_value(2)
698 assert_equal 'Foo', issue.custom_field_value(2)
699 end
699 end
700
700
701 def test_should_update_issue_with_disabled_tracker
701 def test_should_update_issue_with_disabled_tracker
702 p = Project.find(1)
702 p = Project.find(1)
703 issue = Issue.find(1)
703 issue = Issue.find(1)
704
704
705 p.trackers.delete(issue.tracker)
705 p.trackers.delete(issue.tracker)
706 assert !p.trackers.include?(issue.tracker)
706 assert !p.trackers.include?(issue.tracker)
707
707
708 issue.reload
708 issue.reload
709 issue.subject = 'New subject'
709 issue.subject = 'New subject'
710 assert issue.save
710 assert issue.save
711 end
711 end
712
712
713 def test_should_not_set_a_disabled_tracker
713 def test_should_not_set_a_disabled_tracker
714 p = Project.find(1)
714 p = Project.find(1)
715 p.trackers.delete(Tracker.find(2))
715 p.trackers.delete(Tracker.find(2))
716
716
717 issue = Issue.find(1)
717 issue = Issue.find(1)
718 issue.tracker_id = 2
718 issue.tracker_id = 2
719 issue.subject = 'New subject'
719 issue.subject = 'New subject'
720 assert !issue.save
720 assert !issue.save
721 assert_not_equal [], issue.errors[:tracker_id]
721 assert_not_equal [], issue.errors[:tracker_id]
722 end
722 end
723
723
724 def test_category_based_assignment
724 def test_category_based_assignment
725 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
725 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
726 :status_id => 1, :priority => IssuePriority.all.first,
726 :status_id => 1, :priority => IssuePriority.all.first,
727 :subject => 'Assignment test',
727 :subject => 'Assignment test',
728 :description => 'Assignment test', :category_id => 1)
728 :description => 'Assignment test', :category_id => 1)
729 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
729 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
730 end
730 end
731
731
732 def test_new_statuses_allowed_to
732 def test_new_statuses_allowed_to
733 WorkflowTransition.delete_all
733 WorkflowTransition.delete_all
734 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
734 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
735 :old_status_id => 1, :new_status_id => 2,
735 :old_status_id => 1, :new_status_id => 2,
736 :author => false, :assignee => false)
736 :author => false, :assignee => false)
737 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
737 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
738 :old_status_id => 1, :new_status_id => 3,
738 :old_status_id => 1, :new_status_id => 3,
739 :author => true, :assignee => false)
739 :author => true, :assignee => false)
740 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
740 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
741 :old_status_id => 1, :new_status_id => 4,
741 :old_status_id => 1, :new_status_id => 4,
742 :author => false, :assignee => true)
742 :author => false, :assignee => true)
743 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
743 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
744 :old_status_id => 1, :new_status_id => 5,
744 :old_status_id => 1, :new_status_id => 5,
745 :author => true, :assignee => true)
745 :author => true, :assignee => true)
746 status = IssueStatus.find(1)
746 status = IssueStatus.find(1)
747 role = Role.find(1)
747 role = Role.find(1)
748 tracker = Tracker.find(1)
748 tracker = Tracker.find(1)
749 user = User.find(2)
749 user = User.find(2)
750
750
751 issue = Issue.generate!(:tracker => tracker, :status => status,
751 issue = Issue.generate!(:tracker => tracker, :status => status,
752 :project_id => 1, :author_id => 1)
752 :project_id => 1, :author_id => 1)
753 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
753 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
754
754
755 issue = Issue.generate!(:tracker => tracker, :status => status,
755 issue = Issue.generate!(:tracker => tracker, :status => status,
756 :project_id => 1, :author => user)
756 :project_id => 1, :author => user)
757 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
757 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
758
758
759 issue = Issue.generate!(:tracker => tracker, :status => status,
759 issue = Issue.generate!(:tracker => tracker, :status => status,
760 :project_id => 1, :author_id => 1,
760 :project_id => 1, :author_id => 1,
761 :assigned_to => user)
761 :assigned_to => user)
762 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
762 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
763
763
764 issue = Issue.generate!(:tracker => tracker, :status => status,
764 issue = Issue.generate!(:tracker => tracker, :status => status,
765 :project_id => 1, :author => user,
765 :project_id => 1, :author => user,
766 :assigned_to => user)
766 :assigned_to => user)
767 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
767 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
768 end
768 end
769
769
770 def test_new_statuses_allowed_to_should_consider_group_assignment
770 def test_new_statuses_allowed_to_should_consider_group_assignment
771 WorkflowTransition.delete_all
771 WorkflowTransition.delete_all
772 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
772 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
773 :old_status_id => 1, :new_status_id => 4,
773 :old_status_id => 1, :new_status_id => 4,
774 :author => false, :assignee => true)
774 :author => false, :assignee => true)
775 group = Group.generate!
775 group = Group.generate!
776 Member.create!(:project_id => 1, :principal => group, :role_ids => [1])
776 Member.create!(:project_id => 1, :principal => group, :role_ids => [1])
777
777
778 user = User.find(2)
778 user = User.find(2)
779 group.users << user
779 group.users << user
780
780
781 with_settings :issue_group_assignment => '1' do
781 with_settings :issue_group_assignment => '1' do
782 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
782 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
783 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
783 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
784 end
784 end
785 end
785 end
786
786
787 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
787 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
788 admin = User.find(1)
788 admin = User.find(1)
789 issue = Issue.find(1)
789 issue = Issue.find(1)
790 assert !admin.member_of?(issue.project)
790 assert !admin.member_of?(issue.project)
791 expected_statuses = [issue.status] +
791 expected_statuses = [issue.status] +
792 WorkflowTransition.where(:old_status_id => issue.status_id).
792 WorkflowTransition.where(:old_status_id => issue.status_id).
793 map(&:new_status).uniq.sort
793 map(&:new_status).uniq.sort
794 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
794 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
795 end
795 end
796
796
797 def test_new_statuses_allowed_to_should_return_allowed_statuses_and_current_status_when_copying
797 def test_new_statuses_allowed_to_should_return_allowed_statuses_and_current_status_when_copying
798 Tracker.find(1).generate_transitions! :role_id => 1, :clear => true, 0 => [1, 3]
798 Tracker.find(1).generate_transitions! :role_id => 1, :clear => true, 0 => [1, 3]
799
799
800 orig = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
800 orig = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
801 issue = orig.copy
801 issue = orig.copy
802 assert_equal [1, 3, 4], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
802 assert_equal [1, 3, 4], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
803 assert_equal 4, issue.status_id
803 assert_equal 4, issue.status_id
804 end
804 end
805
805
806 def test_safe_attributes_names_should_not_include_disabled_field
806 def test_safe_attributes_names_should_not_include_disabled_field
807 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
807 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
808
808
809 issue = Issue.new(:tracker => tracker)
809 issue = Issue.new(:tracker => tracker)
810 assert_include 'tracker_id', issue.safe_attribute_names
810 assert_include 'tracker_id', issue.safe_attribute_names
811 assert_include 'status_id', issue.safe_attribute_names
811 assert_include 'status_id', issue.safe_attribute_names
812 assert_include 'subject', issue.safe_attribute_names
812 assert_include 'subject', issue.safe_attribute_names
813 assert_include 'description', issue.safe_attribute_names
813 assert_include 'description', issue.safe_attribute_names
814 assert_include 'custom_field_values', issue.safe_attribute_names
814 assert_include 'custom_field_values', issue.safe_attribute_names
815 assert_include 'custom_fields', issue.safe_attribute_names
815 assert_include 'custom_fields', issue.safe_attribute_names
816 assert_include 'lock_version', issue.safe_attribute_names
816 assert_include 'lock_version', issue.safe_attribute_names
817
817
818 tracker.core_fields.each do |field|
818 tracker.core_fields.each do |field|
819 assert_include field, issue.safe_attribute_names
819 assert_include field, issue.safe_attribute_names
820 end
820 end
821
821
822 tracker.disabled_core_fields.each do |field|
822 tracker.disabled_core_fields.each do |field|
823 assert_not_include field, issue.safe_attribute_names
823 assert_not_include field, issue.safe_attribute_names
824 end
824 end
825 end
825 end
826
826
827 def test_safe_attributes_should_ignore_disabled_fields
827 def test_safe_attributes_should_ignore_disabled_fields
828 tracker = Tracker.find(1)
828 tracker = Tracker.find(1)
829 tracker.core_fields = %w(assigned_to_id due_date)
829 tracker.core_fields = %w(assigned_to_id due_date)
830 tracker.save!
830 tracker.save!
831
831
832 issue = Issue.new(:tracker => tracker)
832 issue = Issue.new(:tracker => tracker)
833 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
833 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
834 assert_nil issue.start_date
834 assert_nil issue.start_date
835 assert_equal Date.parse('2012-07-14'), issue.due_date
835 assert_equal Date.parse('2012-07-14'), issue.due_date
836 end
836 end
837
837
838 def test_safe_attributes_should_accept_target_tracker_enabled_fields
838 def test_safe_attributes_should_accept_target_tracker_enabled_fields
839 source = Tracker.find(1)
839 source = Tracker.find(1)
840 source.core_fields = []
840 source.core_fields = []
841 source.save!
841 source.save!
842 target = Tracker.find(2)
842 target = Tracker.find(2)
843 target.core_fields = %w(assigned_to_id due_date)
843 target.core_fields = %w(assigned_to_id due_date)
844 target.save!
844 target.save!
845 user = User.find(2)
845 user = User.find(2)
846
846
847 issue = Issue.new(:project => Project.find(1), :tracker => source)
847 issue = Issue.new(:project => Project.find(1), :tracker => source)
848 issue.send :safe_attributes=, {'tracker_id' => 2, 'due_date' => '2012-07-14'}, user
848 issue.send :safe_attributes=, {'tracker_id' => 2, 'due_date' => '2012-07-14'}, user
849 assert_equal target, issue.tracker
849 assert_equal target, issue.tracker
850 assert_equal Date.parse('2012-07-14'), issue.due_date
850 assert_equal Date.parse('2012-07-14'), issue.due_date
851 end
851 end
852
852
853 def test_safe_attributes_should_not_include_readonly_fields
853 def test_safe_attributes_should_not_include_readonly_fields
854 WorkflowPermission.delete_all
854 WorkflowPermission.delete_all
855 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
855 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
856 :role_id => 1, :field_name => 'due_date',
856 :role_id => 1, :field_name => 'due_date',
857 :rule => 'readonly')
857 :rule => 'readonly')
858 user = User.find(2)
858 user = User.find(2)
859
859
860 issue = Issue.new(:project_id => 1, :tracker_id => 1)
860 issue = Issue.new(:project_id => 1, :tracker_id => 1)
861 assert_equal %w(due_date), issue.read_only_attribute_names(user)
861 assert_equal %w(due_date), issue.read_only_attribute_names(user)
862 assert_not_include 'due_date', issue.safe_attribute_names(user)
862 assert_not_include 'due_date', issue.safe_attribute_names(user)
863
863
864 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
864 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
865 assert_equal Date.parse('2012-07-14'), issue.start_date
865 assert_equal Date.parse('2012-07-14'), issue.start_date
866 assert_nil issue.due_date
866 assert_nil issue.due_date
867 end
867 end
868
868
869 def test_safe_attributes_should_not_include_readonly_custom_fields
869 def test_safe_attributes_should_not_include_readonly_custom_fields
870 cf1 = IssueCustomField.create!(:name => 'Writable field',
870 cf1 = IssueCustomField.create!(:name => 'Writable field',
871 :field_format => 'string',
871 :field_format => 'string',
872 :is_for_all => true, :tracker_ids => [1])
872 :is_for_all => true, :tracker_ids => [1])
873 cf2 = IssueCustomField.create!(:name => 'Readonly field',
873 cf2 = IssueCustomField.create!(:name => 'Readonly field',
874 :field_format => 'string',
874 :field_format => 'string',
875 :is_for_all => true, :tracker_ids => [1])
875 :is_for_all => true, :tracker_ids => [1])
876 WorkflowPermission.delete_all
876 WorkflowPermission.delete_all
877 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
877 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
878 :role_id => 1, :field_name => cf2.id.to_s,
878 :role_id => 1, :field_name => cf2.id.to_s,
879 :rule => 'readonly')
879 :rule => 'readonly')
880 user = User.find(2)
880 user = User.find(2)
881 issue = Issue.new(:project_id => 1, :tracker_id => 1)
881 issue = Issue.new(:project_id => 1, :tracker_id => 1)
882 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
882 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
883 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
883 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
884
884
885 issue.send :safe_attributes=, {'custom_field_values' => {
885 issue.send :safe_attributes=, {'custom_field_values' => {
886 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
886 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
887 }}, user
887 }}, user
888 assert_equal 'value1', issue.custom_field_value(cf1)
888 assert_equal 'value1', issue.custom_field_value(cf1)
889 assert_nil issue.custom_field_value(cf2)
889 assert_nil issue.custom_field_value(cf2)
890
890
891 issue.send :safe_attributes=, {'custom_fields' => [
891 issue.send :safe_attributes=, {'custom_fields' => [
892 {'id' => cf1.id.to_s, 'value' => 'valuea'},
892 {'id' => cf1.id.to_s, 'value' => 'valuea'},
893 {'id' => cf2.id.to_s, 'value' => 'valueb'}
893 {'id' => cf2.id.to_s, 'value' => 'valueb'}
894 ]}, user
894 ]}, user
895 assert_equal 'valuea', issue.custom_field_value(cf1)
895 assert_equal 'valuea', issue.custom_field_value(cf1)
896 assert_nil issue.custom_field_value(cf2)
896 assert_nil issue.custom_field_value(cf2)
897 end
897 end
898
898
899 def test_editable_custom_field_values_should_return_non_readonly_custom_values
899 def test_editable_custom_field_values_should_return_non_readonly_custom_values
900 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
900 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
901 :is_for_all => true, :tracker_ids => [1, 2])
901 :is_for_all => true, :tracker_ids => [1, 2])
902 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
902 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
903 :is_for_all => true, :tracker_ids => [1, 2])
903 :is_for_all => true, :tracker_ids => [1, 2])
904 WorkflowPermission.delete_all
904 WorkflowPermission.delete_all
905 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
905 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
906 :field_name => cf2.id.to_s, :rule => 'readonly')
906 :field_name => cf2.id.to_s, :rule => 'readonly')
907 user = User.find(2)
907 user = User.find(2)
908
908
909 issue = Issue.new(:project_id => 1, :tracker_id => 1)
909 issue = Issue.new(:project_id => 1, :tracker_id => 1)
910 values = issue.editable_custom_field_values(user)
910 values = issue.editable_custom_field_values(user)
911 assert values.detect {|value| value.custom_field == cf1}
911 assert values.detect {|value| value.custom_field == cf1}
912 assert_nil values.detect {|value| value.custom_field == cf2}
912 assert_nil values.detect {|value| value.custom_field == cf2}
913
913
914 issue.tracker_id = 2
914 issue.tracker_id = 2
915 values = issue.editable_custom_field_values(user)
915 values = issue.editable_custom_field_values(user)
916 assert values.detect {|value| value.custom_field == cf1}
916 assert values.detect {|value| value.custom_field == cf1}
917 assert values.detect {|value| value.custom_field == cf2}
917 assert values.detect {|value| value.custom_field == cf2}
918 end
918 end
919
919
920 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
920 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
921 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
921 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
922 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
922 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
923 user = User.find(2)
923 user = User.find(2)
924 issue = Issue.new(:project_id => 1, :tracker_id => 1)
924 issue = Issue.new(:project_id => 1, :tracker_id => 1)
925
925
926 assert_include enabled_cf, issue.editable_custom_fields(user)
926 assert_include enabled_cf, issue.editable_custom_fields(user)
927 assert_not_include disabled_cf, issue.editable_custom_fields(user)
927 assert_not_include disabled_cf, issue.editable_custom_fields(user)
928 end
928 end
929
929
930 def test_safe_attributes_should_accept_target_tracker_writable_fields
930 def test_safe_attributes_should_accept_target_tracker_writable_fields
931 WorkflowPermission.delete_all
931 WorkflowPermission.delete_all
932 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
932 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
933 :role_id => 1, :field_name => 'due_date',
933 :role_id => 1, :field_name => 'due_date',
934 :rule => 'readonly')
934 :rule => 'readonly')
935 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
935 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
936 :role_id => 1, :field_name => 'start_date',
936 :role_id => 1, :field_name => 'start_date',
937 :rule => 'readonly')
937 :rule => 'readonly')
938 user = User.find(2)
938 user = User.find(2)
939
939
940 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
940 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
941
941
942 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
942 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
943 'due_date' => '2012-07-14'}, user
943 'due_date' => '2012-07-14'}, user
944 assert_equal Date.parse('2012-07-12'), issue.start_date
944 assert_equal Date.parse('2012-07-12'), issue.start_date
945 assert_nil issue.due_date
945 assert_nil issue.due_date
946
946
947 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
947 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
948 'due_date' => '2012-07-16',
948 'due_date' => '2012-07-16',
949 'tracker_id' => 2}, user
949 'tracker_id' => 2}, user
950 assert_equal Date.parse('2012-07-12'), issue.start_date
950 assert_equal Date.parse('2012-07-12'), issue.start_date
951 assert_equal Date.parse('2012-07-16'), issue.due_date
951 assert_equal Date.parse('2012-07-16'), issue.due_date
952 end
952 end
953
953
954 def test_safe_attributes_should_accept_target_status_writable_fields
954 def test_safe_attributes_should_accept_target_status_writable_fields
955 WorkflowPermission.delete_all
955 WorkflowPermission.delete_all
956 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
956 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
957 :role_id => 1, :field_name => 'due_date',
957 :role_id => 1, :field_name => 'due_date',
958 :rule => 'readonly')
958 :rule => 'readonly')
959 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
959 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
960 :role_id => 1, :field_name => 'start_date',
960 :role_id => 1, :field_name => 'start_date',
961 :rule => 'readonly')
961 :rule => 'readonly')
962 user = User.find(2)
962 user = User.find(2)
963
963
964 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
964 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
965
965
966 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
966 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
967 'due_date' => '2012-07-14'},
967 'due_date' => '2012-07-14'},
968 user
968 user
969 assert_equal Date.parse('2012-07-12'), issue.start_date
969 assert_equal Date.parse('2012-07-12'), issue.start_date
970 assert_nil issue.due_date
970 assert_nil issue.due_date
971
971
972 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
972 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
973 'due_date' => '2012-07-16',
973 'due_date' => '2012-07-16',
974 'status_id' => 2},
974 'status_id' => 2},
975 user
975 user
976 assert_equal Date.parse('2012-07-12'), issue.start_date
976 assert_equal Date.parse('2012-07-12'), issue.start_date
977 assert_equal Date.parse('2012-07-16'), issue.due_date
977 assert_equal Date.parse('2012-07-16'), issue.due_date
978 end
978 end
979
979
980 def test_required_attributes_should_be_validated
980 def test_required_attributes_should_be_validated
981 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
981 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
982 :is_for_all => true, :tracker_ids => [1, 2])
982 :is_for_all => true, :tracker_ids => [1, 2])
983
983
984 WorkflowPermission.delete_all
984 WorkflowPermission.delete_all
985 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
985 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
986 :role_id => 1, :field_name => 'due_date',
986 :role_id => 1, :field_name => 'due_date',
987 :rule => 'required')
987 :rule => 'required')
988 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
988 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
989 :role_id => 1, :field_name => 'category_id',
989 :role_id => 1, :field_name => 'category_id',
990 :rule => 'required')
990 :rule => 'required')
991 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
991 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
992 :role_id => 1, :field_name => cf.id.to_s,
992 :role_id => 1, :field_name => cf.id.to_s,
993 :rule => 'required')
993 :rule => 'required')
994
994
995 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
995 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
996 :role_id => 1, :field_name => 'start_date',
996 :role_id => 1, :field_name => 'start_date',
997 :rule => 'required')
997 :rule => 'required')
998 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
998 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
999 :role_id => 1, :field_name => cf.id.to_s,
999 :role_id => 1, :field_name => cf.id.to_s,
1000 :rule => 'required')
1000 :rule => 'required')
1001 user = User.find(2)
1001 user = User.find(2)
1002
1002
1003 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1003 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1004 :status_id => 1, :subject => 'Required fields',
1004 :status_id => 1, :subject => 'Required fields',
1005 :author => user)
1005 :author => user)
1006 assert_equal [cf.id.to_s, "category_id", "due_date"],
1006 assert_equal [cf.id.to_s, "category_id", "due_date"],
1007 issue.required_attribute_names(user).sort
1007 issue.required_attribute_names(user).sort
1008 assert !issue.save, "Issue was saved"
1008 assert !issue.save, "Issue was saved"
1009 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
1009 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
1010 issue.errors.full_messages.sort
1010 issue.errors.full_messages.sort
1011
1011
1012 issue.tracker_id = 2
1012 issue.tracker_id = 2
1013 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
1013 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
1014 assert !issue.save, "Issue was saved"
1014 assert !issue.save, "Issue was saved"
1015 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
1015 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
1016 issue.errors.full_messages.sort
1016 issue.errors.full_messages.sort
1017
1017
1018 issue.start_date = Date.today
1018 issue.start_date = Date.today
1019 issue.custom_field_values = {cf.id.to_s => 'bar'}
1019 issue.custom_field_values = {cf.id.to_s => 'bar'}
1020 assert issue.save
1020 assert issue.save
1021 end
1021 end
1022
1022
1023 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
1023 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
1024 WorkflowPermission.delete_all
1024 WorkflowPermission.delete_all
1025 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1025 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1026 :role_id => 1, :field_name => 'start_date',
1026 :role_id => 1, :field_name => 'start_date',
1027 :rule => 'required')
1027 :rule => 'required')
1028 user = User.find(2)
1028 user = User.find(2)
1029
1029
1030 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1030 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1031 :subject => 'Required fields', :author => user)
1031 :subject => 'Required fields', :author => user)
1032 assert !issue.save
1032 assert !issue.save
1033 assert_include "Start date cannot be blank", issue.errors.full_messages
1033 assert_include "Start date cannot be blank", issue.errors.full_messages
1034
1034
1035 tracker = Tracker.find(1)
1035 tracker = Tracker.find(1)
1036 tracker.core_fields -= %w(start_date)
1036 tracker.core_fields -= %w(start_date)
1037 tracker.save!
1037 tracker.save!
1038 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1038 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1039 :subject => 'Required fields', :author => user)
1039 :subject => 'Required fields', :author => user)
1040 assert issue.save
1040 assert issue.save
1041 end
1041 end
1042
1042
1043 def test_category_should_not_be_required_if_project_has_no_categories
1043 def test_category_should_not_be_required_if_project_has_no_categories
1044 Project.find(1).issue_categories.delete_all
1044 Project.find(1).issue_categories.delete_all
1045 WorkflowPermission.delete_all
1045 WorkflowPermission.delete_all
1046 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1046 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1047 :role_id => 1, :field_name => 'category_id',:rule => 'required')
1047 :role_id => 1, :field_name => 'category_id',:rule => 'required')
1048 user = User.find(2)
1048 user = User.find(2)
1049
1049
1050 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1050 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1051 :subject => 'Required fields', :author => user)
1051 :subject => 'Required fields', :author => user)
1052 assert_save issue
1052 assert_save issue
1053 end
1053 end
1054
1054
1055 def test_fixed_version_should_not_be_required_no_assignable_versions
1055 def test_fixed_version_should_not_be_required_no_assignable_versions
1056 Version.delete_all
1056 Version.delete_all
1057 WorkflowPermission.delete_all
1057 WorkflowPermission.delete_all
1058 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1058 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1059 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
1059 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
1060 user = User.find(2)
1060 user = User.find(2)
1061
1061
1062 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1062 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1063 :subject => 'Required fields', :author => user)
1063 :subject => 'Required fields', :author => user)
1064 assert_save issue
1064 assert_save issue
1065 end
1065 end
1066
1066
1067 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
1067 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
1068 CustomField.delete_all
1068 CustomField.delete_all
1069 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1069 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1070 user = User.generate!
1070 user = User.generate!
1071 User.add_to_project(user, Project.find(1), Role.find(2))
1071 User.add_to_project(user, Project.find(1), Role.find(2))
1072
1072
1073 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1073 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1074 :subject => 'Required fields', :author => user)
1074 :subject => 'Required fields', :author => user)
1075 assert_save issue
1075 assert_save issue
1076 end
1076 end
1077
1077
1078 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1078 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1079 CustomField.delete_all
1079 CustomField.delete_all
1080 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1080 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1081 user = User.generate!
1081 user = User.generate!
1082 User.add_to_project(user, Project.find(1), Role.find(1))
1082 User.add_to_project(user, Project.find(1), Role.find(1))
1083
1083
1084 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1084 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1085 :subject => 'Required fields', :author => user)
1085 :subject => 'Required fields', :author => user)
1086 assert !issue.save
1086 assert !issue.save
1087 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1087 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1088 end
1088 end
1089
1089
1090 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1090 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1091 WorkflowPermission.delete_all
1091 WorkflowPermission.delete_all
1092 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1092 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1093 :role_id => 1, :field_name => 'due_date',
1093 :role_id => 1, :field_name => 'due_date',
1094 :rule => 'required')
1094 :rule => 'required')
1095 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1095 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1096 :role_id => 1, :field_name => 'start_date',
1096 :role_id => 1, :field_name => 'start_date',
1097 :rule => 'required')
1097 :rule => 'required')
1098 user = User.find(2)
1098 user = User.find(2)
1099 member = Member.find(1)
1099 member = Member.find(1)
1100 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1100 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1101
1101
1102 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1102 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1103
1103
1104 member.role_ids = [1, 2]
1104 member.role_ids = [1, 2]
1105 member.save!
1105 member.save!
1106 assert_equal [], issue.required_attribute_names(user.reload)
1106 assert_equal [], issue.required_attribute_names(user.reload)
1107
1107
1108 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1108 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1109 :role_id => 2, :field_name => 'due_date',
1109 :role_id => 2, :field_name => 'due_date',
1110 :rule => 'required')
1110 :rule => 'required')
1111 assert_equal %w(due_date), issue.required_attribute_names(user)
1111 assert_equal %w(due_date), issue.required_attribute_names(user)
1112
1112
1113 member.role_ids = [1, 2, 3]
1113 member.role_ids = [1, 2, 3]
1114 member.save!
1114 member.save!
1115 assert_equal [], issue.required_attribute_names(user.reload)
1115 assert_equal [], issue.required_attribute_names(user.reload)
1116
1116
1117 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1117 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1118 :role_id => 3, :field_name => 'due_date',
1118 :role_id => 3, :field_name => 'due_date',
1119 :rule => 'readonly')
1119 :rule => 'readonly')
1120 # required + readonly => required
1120 # required + readonly => required
1121 assert_equal %w(due_date), issue.required_attribute_names(user)
1121 assert_equal %w(due_date), issue.required_attribute_names(user)
1122 end
1122 end
1123
1123
1124 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1124 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1125 WorkflowPermission.delete_all
1125 WorkflowPermission.delete_all
1126 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1126 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1127 :role_id => 1, :field_name => 'due_date',
1127 :role_id => 1, :field_name => 'due_date',
1128 :rule => 'readonly')
1128 :rule => 'readonly')
1129 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1129 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1130 :role_id => 1, :field_name => 'start_date',
1130 :role_id => 1, :field_name => 'start_date',
1131 :rule => 'readonly')
1131 :rule => 'readonly')
1132 user = User.find(2)
1132 user = User.find(2)
1133 member = Member.find(1)
1133 member = Member.find(1)
1134 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1134 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1135
1135
1136 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1136 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1137
1137
1138 member.role_ids = [1, 2]
1138 member.role_ids = [1, 2]
1139 member.save!
1139 member.save!
1140 assert_equal [], issue.read_only_attribute_names(user.reload)
1140 assert_equal [], issue.read_only_attribute_names(user.reload)
1141
1141
1142 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1142 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1143 :role_id => 2, :field_name => 'due_date',
1143 :role_id => 2, :field_name => 'due_date',
1144 :rule => 'readonly')
1144 :rule => 'readonly')
1145 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1145 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1146 end
1146 end
1147
1147
1148 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1148 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1149 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1149 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1150 field = IssueCustomField.generate!(
1150 field = IssueCustomField.generate!(
1151 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1151 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1152 )
1152 )
1153 WorkflowPermission.delete_all
1153 WorkflowPermission.delete_all
1154 WorkflowPermission.create!(
1154 WorkflowPermission.create!(
1155 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1155 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1156 )
1156 )
1157 user = User.generate!
1157 user = User.generate!
1158 project = Project.find(1)
1158 project = Project.find(1)
1159 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1159 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1160
1160
1161 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1161 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1162 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1162 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1163 end
1163 end
1164
1164
1165 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1165 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1166 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1166 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1167 ignored_role = Role.generate! :permissions => [:view_issues]
1167 ignored_role = Role.generate! :permissions => [:view_issues]
1168
1168
1169 WorkflowPermission.delete_all
1169 WorkflowPermission.delete_all
1170 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1170 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1171 :role => role, :field_name => 'due_date',
1171 :role => role, :field_name => 'due_date',
1172 :rule => 'required')
1172 :rule => 'required')
1173 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1173 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1174 :role => role, :field_name => 'start_date',
1174 :role => role, :field_name => 'start_date',
1175 :rule => 'readonly')
1175 :rule => 'readonly')
1176 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1176 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1177 :role => role, :field_name => 'done_ratio',
1177 :role => role, :field_name => 'done_ratio',
1178 :rule => 'readonly')
1178 :rule => 'readonly')
1179 user = User.generate!
1179 user = User.generate!
1180 User.add_to_project user, Project.find(1), [role, ignored_role]
1180 User.add_to_project user, Project.find(1), [role, ignored_role]
1181
1181
1182 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1182 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1183
1183
1184 assert_equal %w(due_date), issue.required_attribute_names(user)
1184 assert_equal %w(due_date), issue.required_attribute_names(user)
1185 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1185 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1186 end
1186 end
1187
1187
1188 def test_workflow_rules_should_work_for_member_with_duplicate_role
1188 def test_workflow_rules_should_work_for_member_with_duplicate_role
1189 WorkflowPermission.delete_all
1189 WorkflowPermission.delete_all
1190 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1190 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1191 :role_id => 1, :field_name => 'due_date',
1191 :role_id => 1, :field_name => 'due_date',
1192 :rule => 'required')
1192 :rule => 'required')
1193 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1193 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1194 :role_id => 1, :field_name => 'start_date',
1194 :role_id => 1, :field_name => 'start_date',
1195 :rule => 'readonly')
1195 :rule => 'readonly')
1196
1196
1197 user = User.generate!
1197 user = User.generate!
1198 m = Member.new(:user_id => user.id, :project_id => 1)
1198 m = Member.new(:user_id => user.id, :project_id => 1)
1199 m.member_roles.build(:role_id => 1)
1199 m.member_roles.build(:role_id => 1)
1200 m.member_roles.build(:role_id => 1)
1200 m.member_roles.build(:role_id => 1)
1201 m.save!
1201 m.save!
1202
1202
1203 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1203 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1204
1204
1205 assert_equal %w(due_date), issue.required_attribute_names(user)
1205 assert_equal %w(due_date), issue.required_attribute_names(user)
1206 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1206 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1207 end
1207 end
1208
1208
1209 def test_copy
1209 def test_copy
1210 issue = Issue.new.copy_from(1)
1210 issue = Issue.new.copy_from(1)
1211 assert issue.copy?
1211 assert issue.copy?
1212 assert issue.save
1212 assert issue.save
1213 issue.reload
1213 issue.reload
1214 orig = Issue.find(1)
1214 orig = Issue.find(1)
1215 assert_equal orig.subject, issue.subject
1215 assert_equal orig.subject, issue.subject
1216 assert_equal orig.tracker, issue.tracker
1216 assert_equal orig.tracker, issue.tracker
1217 assert_equal "125", issue.custom_value_for(2).value
1217 assert_equal "125", issue.custom_value_for(2).value
1218 end
1218 end
1219
1219
1220 def test_copy_to_another_project_should_clear_assignee_if_not_valid
1220 def test_copy_to_another_project_should_clear_assignee_if_not_valid
1221 issue = Issue.generate!(:project_id => 1, :assigned_to_id => 2)
1221 issue = Issue.generate!(:project_id => 1, :assigned_to_id => 2)
1222 project = Project.generate!
1222 project = Project.generate!
1223
1223
1224 issue = Issue.new.copy_from(1)
1224 issue = Issue.new.copy_from(1)
1225 issue.project = project
1225 issue.project = project
1226 assert_nil issue.assigned_to
1226 assert_nil issue.assigned_to
1227 end
1227 end
1228
1228
1229 def test_copy_should_copy_status
1229 def test_copy_should_copy_status
1230 orig = Issue.find(8)
1230 orig = Issue.find(8)
1231 assert orig.status != orig.default_status
1231 assert orig.status != orig.default_status
1232
1232
1233 issue = Issue.new.copy_from(orig)
1233 issue = Issue.new.copy_from(orig)
1234 assert issue.save
1234 assert issue.save
1235 issue.reload
1235 issue.reload
1236 assert_equal orig.status, issue.status
1236 assert_equal orig.status, issue.status
1237 end
1237 end
1238
1238
1239 def test_copy_should_add_relation_with_copied_issue
1239 def test_copy_should_add_relation_with_copied_issue
1240 copied = Issue.find(1)
1240 copied = Issue.find(1)
1241 issue = Issue.new.copy_from(copied)
1241 issue = Issue.new.copy_from(copied)
1242 assert issue.save
1242 assert issue.save
1243 issue.reload
1243 issue.reload
1244
1244
1245 assert_equal 1, issue.relations.size
1245 assert_equal 1, issue.relations.size
1246 relation = issue.relations.first
1246 relation = issue.relations.first
1247 assert_equal 'copied_to', relation.relation_type
1247 assert_equal 'copied_to', relation.relation_type
1248 assert_equal copied, relation.issue_from
1248 assert_equal copied, relation.issue_from
1249 assert_equal issue, relation.issue_to
1249 assert_equal issue, relation.issue_to
1250 end
1250 end
1251
1251
1252 def test_copy_should_copy_subtasks
1252 def test_copy_should_copy_subtasks
1253 issue = Issue.generate_with_descendants!
1253 issue = Issue.generate_with_descendants!
1254
1254
1255 copy = issue.reload.copy
1255 copy = issue.reload.copy
1256 copy.author = User.find(7)
1256 copy.author = User.find(7)
1257 assert_difference 'Issue.count', 1+issue.descendants.count do
1257 assert_difference 'Issue.count', 1+issue.descendants.count do
1258 assert copy.save
1258 assert copy.save
1259 end
1259 end
1260 copy.reload
1260 copy.reload
1261 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1261 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1262 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1262 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1263 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1263 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1264 assert_equal copy.author, child_copy.author
1264 assert_equal copy.author, child_copy.author
1265 end
1265 end
1266
1266
1267 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1267 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1268 parent = Issue.generate!
1268 parent = Issue.generate!
1269 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1269 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1270 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1270 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1271
1271
1272 copy = parent.reload.copy
1272 copy = parent.reload.copy
1273 copy.parent_issue_id = parent.id
1273 copy.parent_issue_id = parent.id
1274 copy.author = User.find(7)
1274 copy.author = User.find(7)
1275 assert_difference 'Issue.count', 3 do
1275 assert_difference 'Issue.count', 3 do
1276 assert copy.save
1276 assert copy.save
1277 end
1277 end
1278 parent.reload
1278 parent.reload
1279 copy.reload
1279 copy.reload
1280 assert_equal parent, copy.parent
1280 assert_equal parent, copy.parent
1281 assert_equal 3, parent.children.count
1281 assert_equal 3, parent.children.count
1282 assert_equal 5, parent.descendants.count
1282 assert_equal 5, parent.descendants.count
1283 assert_equal 2, copy.children.count
1283 assert_equal 2, copy.children.count
1284 assert_equal 2, copy.descendants.count
1284 assert_equal 2, copy.descendants.count
1285 end
1285 end
1286
1286
1287 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1287 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1288 parent = Issue.generate!
1288 parent = Issue.generate!
1289 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1289 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1290 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1290 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1291
1291
1292 copy = parent.reload.copy
1292 copy = parent.reload.copy
1293 copy.parent_issue_id = child1.id
1293 copy.parent_issue_id = child1.id
1294 copy.author = User.find(7)
1294 copy.author = User.find(7)
1295 assert_difference 'Issue.count', 3 do
1295 assert_difference 'Issue.count', 3 do
1296 assert copy.save
1296 assert copy.save
1297 end
1297 end
1298 parent.reload
1298 parent.reload
1299 child1.reload
1299 child1.reload
1300 copy.reload
1300 copy.reload
1301 assert_equal child1, copy.parent
1301 assert_equal child1, copy.parent
1302 assert_equal 2, parent.children.count
1302 assert_equal 2, parent.children.count
1303 assert_equal 5, parent.descendants.count
1303 assert_equal 5, parent.descendants.count
1304 assert_equal 1, child1.children.count
1304 assert_equal 1, child1.children.count
1305 assert_equal 3, child1.descendants.count
1305 assert_equal 3, child1.descendants.count
1306 assert_equal 2, copy.children.count
1306 assert_equal 2, copy.children.count
1307 assert_equal 2, copy.descendants.count
1307 assert_equal 2, copy.descendants.count
1308 end
1308 end
1309
1309
1310 def test_copy_should_copy_subtasks_to_target_project
1310 def test_copy_should_copy_subtasks_to_target_project
1311 issue = Issue.generate_with_descendants!
1311 issue = Issue.generate_with_descendants!
1312
1312
1313 copy = issue.copy(:project_id => 3)
1313 copy = issue.copy(:project_id => 3)
1314 assert_difference 'Issue.count', 1+issue.descendants.count do
1314 assert_difference 'Issue.count', 1+issue.descendants.count do
1315 assert copy.save
1315 assert copy.save
1316 end
1316 end
1317 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1317 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1318 end
1318 end
1319
1319
1320 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1320 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1321 issue = Issue.generate_with_descendants!
1321 issue = Issue.generate_with_descendants!
1322
1322
1323 copy = issue.reload.copy
1323 copy = issue.reload.copy
1324 assert_difference 'Issue.count', 1+issue.descendants.count do
1324 assert_difference 'Issue.count', 1+issue.descendants.count do
1325 assert copy.save
1325 assert copy.save
1326 assert copy.save
1326 assert copy.save
1327 end
1327 end
1328 end
1328 end
1329
1329
1330 def test_copy_should_clear_closed_on
1330 def test_copy_should_clear_closed_on
1331 copied_open = Issue.find(8).copy(:status_id => 1)
1331 copied_open = Issue.find(8).copy(:status_id => 1)
1332 assert copied_open.save
1332 assert copied_open.save
1333 assert_nil copied_open.closed_on
1333 assert_nil copied_open.closed_on
1334
1334
1335 copied_closed = Issue.find(8).copy
1335 copied_closed = Issue.find(8).copy
1336 assert copied_closed.save
1336 assert copied_closed.save
1337 assert_not_nil copied_closed.closed_on
1337 assert_not_nil copied_closed.closed_on
1338 end
1338 end
1339
1339
1340 def test_should_not_call_after_project_change_on_creation
1340 def test_should_not_call_after_project_change_on_creation
1341 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1341 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1342 :subject => 'Test', :author_id => 1)
1342 :subject => 'Test', :author_id => 1)
1343 issue.expects(:after_project_change).never
1343 issue.expects(:after_project_change).never
1344 issue.save!
1344 issue.save!
1345 end
1345 end
1346
1346
1347 def test_should_not_call_after_project_change_on_update
1347 def test_should_not_call_after_project_change_on_update
1348 issue = Issue.find(1)
1348 issue = Issue.find(1)
1349 issue.project = Project.find(1)
1349 issue.project = Project.find(1)
1350 issue.subject = 'No project change'
1350 issue.subject = 'No project change'
1351 issue.expects(:after_project_change).never
1351 issue.expects(:after_project_change).never
1352 issue.save!
1352 issue.save!
1353 end
1353 end
1354
1354
1355 def test_should_call_after_project_change_on_project_change
1355 def test_should_call_after_project_change_on_project_change
1356 issue = Issue.find(1)
1356 issue = Issue.find(1)
1357 issue.project = Project.find(2)
1357 issue.project = Project.find(2)
1358 issue.expects(:after_project_change).once
1358 issue.expects(:after_project_change).once
1359 issue.save!
1359 issue.save!
1360 end
1360 end
1361
1361
1362 def test_adding_journal_should_update_timestamp
1362 def test_adding_journal_should_update_timestamp
1363 issue = Issue.find(1)
1363 issue = Issue.find(1)
1364 updated_on_was = issue.updated_on
1364 updated_on_was = issue.updated_on
1365
1365
1366 issue.init_journal(User.first, "Adding notes")
1366 issue.init_journal(User.first, "Adding notes")
1367 assert_difference 'Journal.count' do
1367 assert_difference 'Journal.count' do
1368 assert issue.save
1368 assert issue.save
1369 end
1369 end
1370 issue.reload
1370 issue.reload
1371
1371
1372 assert_not_equal updated_on_was, issue.updated_on
1372 assert_not_equal updated_on_was, issue.updated_on
1373 end
1373 end
1374
1374
1375 def test_should_close_duplicates
1375 def test_should_close_duplicates
1376 # Create 3 issues
1376 # Create 3 issues
1377 issue1 = Issue.generate!
1377 issue1 = Issue.generate!
1378 issue2 = Issue.generate!
1378 issue2 = Issue.generate!
1379 issue3 = Issue.generate!
1379 issue3 = Issue.generate!
1380
1380
1381 # 2 is a dupe of 1
1381 # 2 is a dupe of 1
1382 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1382 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1383 :relation_type => IssueRelation::TYPE_DUPLICATES)
1383 :relation_type => IssueRelation::TYPE_DUPLICATES)
1384 # And 3 is a dupe of 2
1384 # And 3 is a dupe of 2
1385 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1385 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1386 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1386 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1387 # And 3 is a dupe of 1 (circular duplicates)
1387 # And 3 is a dupe of 1 (circular duplicates)
1388 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1388 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1389 :relation_type => IssueRelation::TYPE_DUPLICATES)
1389 :relation_type => IssueRelation::TYPE_DUPLICATES)
1390
1390
1391 assert issue1.reload.duplicates.include?(issue2)
1391 assert issue1.reload.duplicates.include?(issue2)
1392
1392
1393 # Closing issue 1
1393 # Closing issue 1
1394 issue1.init_journal(User.first, "Closing issue1")
1394 issue1.init_journal(User.first, "Closing issue1")
1395 issue1.status = IssueStatus.where(:is_closed => true).first
1395 issue1.status = IssueStatus.where(:is_closed => true).first
1396 assert issue1.save
1396 assert issue1.save
1397 # 2 and 3 should be also closed
1397 # 2 and 3 should be also closed
1398 assert issue2.reload.closed?
1398 assert issue2.reload.closed?
1399 assert issue3.reload.closed?
1399 assert issue3.reload.closed?
1400 end
1400 end
1401
1401
1402 def test_should_close_duplicates_with_private_notes
1402 def test_should_close_duplicates_with_private_notes
1403 issue = Issue.generate!
1403 issue = Issue.generate!
1404 duplicate = Issue.generate!
1404 duplicate = Issue.generate!
1405 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1405 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1406 :relation_type => IssueRelation::TYPE_DUPLICATES)
1406 :relation_type => IssueRelation::TYPE_DUPLICATES)
1407 assert issue.reload.duplicates.include?(duplicate)
1407 assert issue.reload.duplicates.include?(duplicate)
1408
1408
1409 # Closing issue with private notes
1409 # Closing issue with private notes
1410 issue.init_journal(User.first, "Private notes")
1410 issue.init_journal(User.first, "Private notes")
1411 issue.private_notes = true
1411 issue.private_notes = true
1412 issue.status = IssueStatus.where(:is_closed => true).first
1412 issue.status = IssueStatus.where(:is_closed => true).first
1413 assert_save issue
1413 assert_save issue
1414
1414
1415 duplicate.reload
1415 duplicate.reload
1416 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1416 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1417 assert_equal true, journal.private_notes
1417 assert_equal true, journal.private_notes
1418 end
1418 end
1419
1419
1420 def test_should_not_close_duplicated_issue
1420 def test_should_not_close_duplicated_issue
1421 issue1 = Issue.generate!
1421 issue1 = Issue.generate!
1422 issue2 = Issue.generate!
1422 issue2 = Issue.generate!
1423
1423
1424 # 2 is a dupe of 1
1424 # 2 is a dupe of 1
1425 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1425 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1426 :relation_type => IssueRelation::TYPE_DUPLICATES)
1426 :relation_type => IssueRelation::TYPE_DUPLICATES)
1427 # 2 is a dup of 1 but 1 is not a duplicate of 2
1427 # 2 is a dup of 1 but 1 is not a duplicate of 2
1428 assert !issue2.reload.duplicates.include?(issue1)
1428 assert !issue2.reload.duplicates.include?(issue1)
1429
1429
1430 # Closing issue 2
1430 # Closing issue 2
1431 issue2.init_journal(User.first, "Closing issue2")
1431 issue2.init_journal(User.first, "Closing issue2")
1432 issue2.status = IssueStatus.where(:is_closed => true).first
1432 issue2.status = IssueStatus.where(:is_closed => true).first
1433 assert issue2.save
1433 assert issue2.save
1434 # 1 should not be also closed
1434 # 1 should not be also closed
1435 assert !issue1.reload.closed?
1435 assert !issue1.reload.closed?
1436 end
1436 end
1437
1437
1438 def test_assignable_versions
1438 def test_assignable_versions
1439 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1439 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1440 :status_id => 1, :fixed_version_id => 1,
1440 :status_id => 1, :fixed_version_id => 1,
1441 :subject => 'New issue')
1441 :subject => 'New issue')
1442 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1442 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1443 end
1443 end
1444
1444
1445 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1445 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1446 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1446 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1447 :status_id => 1, :fixed_version_id => 1,
1447 :status_id => 1, :fixed_version_id => 1,
1448 :subject => 'New issue')
1448 :subject => 'New issue')
1449 assert !issue.save
1449 assert !issue.save
1450 assert_not_equal [], issue.errors[:fixed_version_id]
1450 assert_not_equal [], issue.errors[:fixed_version_id]
1451 end
1451 end
1452
1452
1453 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1453 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1454 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1454 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1455 :status_id => 1, :fixed_version_id => 2,
1455 :status_id => 1, :fixed_version_id => 2,
1456 :subject => 'New issue')
1456 :subject => 'New issue')
1457 assert !issue.save
1457 assert !issue.save
1458 assert_not_equal [], issue.errors[:fixed_version_id]
1458 assert_not_equal [], issue.errors[:fixed_version_id]
1459 end
1459 end
1460
1460
1461 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1461 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1462 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1462 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1463 :status_id => 1, :fixed_version_id => 3,
1463 :status_id => 1, :fixed_version_id => 3,
1464 :subject => 'New issue')
1464 :subject => 'New issue')
1465 assert issue.save
1465 assert issue.save
1466 end
1466 end
1467
1467
1468 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1468 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1469 issue = Issue.find(11)
1469 issue = Issue.find(11)
1470 assert_equal 'closed', issue.fixed_version.status
1470 assert_equal 'closed', issue.fixed_version.status
1471 issue.subject = 'Subject changed'
1471 issue.subject = 'Subject changed'
1472 assert issue.save
1472 assert issue.save
1473 end
1473 end
1474
1474
1475 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1475 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1476 issue = Issue.find(11)
1476 issue = Issue.find(11)
1477 issue.status_id = 1
1477 issue.status_id = 1
1478 assert !issue.save
1478 assert !issue.save
1479 assert_not_equal [], issue.errors[:base]
1479 assert_not_equal [], issue.errors[:base]
1480 end
1480 end
1481
1481
1482 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1482 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1483 issue = Issue.find(11)
1483 issue = Issue.find(11)
1484 issue.status_id = 1
1484 issue.status_id = 1
1485 issue.fixed_version_id = 3
1485 issue.fixed_version_id = 3
1486 assert issue.save
1486 assert issue.save
1487 end
1487 end
1488
1488
1489 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1489 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1490 issue = Issue.find(12)
1490 issue = Issue.find(12)
1491 assert_equal 'locked', issue.fixed_version.status
1491 assert_equal 'locked', issue.fixed_version.status
1492 issue.status_id = 1
1492 issue.status_id = 1
1493 assert issue.save
1493 assert issue.save
1494 end
1494 end
1495
1495
1496 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1496 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1497 issue = Issue.find(2)
1497 issue = Issue.find(2)
1498 assert_equal 2, issue.fixed_version_id
1498 assert_equal 2, issue.fixed_version_id
1499 issue.project_id = 3
1499 issue.project_id = 3
1500 assert_nil issue.fixed_version_id
1500 assert_nil issue.fixed_version_id
1501 issue.fixed_version_id = 2
1501 issue.fixed_version_id = 2
1502 assert !issue.save
1502 assert !issue.save
1503 assert_include 'Target version is not included in the list', issue.errors.full_messages
1503 assert_include 'Target version is not included in the list', issue.errors.full_messages
1504 end
1504 end
1505
1505
1506 def test_should_keep_shared_version_when_changing_project
1506 def test_should_keep_shared_version_when_changing_project
1507 Version.find(2).update! :sharing => 'tree'
1507 Version.find(2).update! :sharing => 'tree'
1508
1508
1509 issue = Issue.find(2)
1509 issue = Issue.find(2)
1510 assert_equal 2, issue.fixed_version_id
1510 assert_equal 2, issue.fixed_version_id
1511 issue.project_id = 3
1511 issue.project_id = 3
1512 assert_equal 2, issue.fixed_version_id
1512 assert_equal 2, issue.fixed_version_id
1513 assert issue.save
1513 assert issue.save
1514 end
1514 end
1515
1515
1516 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1516 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1517 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1517 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1518 end
1518 end
1519
1519
1520 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1520 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1521 Project.find(2).disable_module! :issue_tracking
1521 Project.find(2).disable_module! :issue_tracking
1522 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1522 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1523 end
1523 end
1524
1524
1525 def test_allowed_target_projects_should_not_include_projects_without_trackers
1525 def test_allowed_target_projects_should_not_include_projects_without_trackers
1526 project = Project.generate!(:tracker_ids => [])
1526 project = Project.generate!(:tracker_ids => [])
1527 assert project.trackers.empty?
1527 assert project.trackers.empty?
1528 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1528 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1529 end
1529 end
1530
1530
1531 def test_allowed_target_trackers_with_one_role_allowed_on_all_trackers
1531 def test_allowed_target_trackers_with_one_role_allowed_on_all_trackers
1532 user = User.generate!
1532 user = User.generate!
1533 role = Role.generate!
1533 role = Role.generate!
1534 role.add_permission! :add_issues
1534 role.add_permission! :add_issues
1535 role.set_permission_trackers :add_issues, :all
1535 role.set_permission_trackers :add_issues, :all
1536 role.save!
1536 role.save!
1537 User.add_to_project(user, Project.find(1), role)
1537 User.add_to_project(user, Project.find(1), role)
1538
1538
1539 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1539 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1540 end
1540 end
1541
1541
1542 def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
1542 def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
1543 user = User.generate!
1543 user = User.generate!
1544 role = Role.generate!
1544 role = Role.generate!
1545 role.add_permission! :add_issues
1545 role.add_permission! :add_issues
1546 role.set_permission_trackers :add_issues, [1, 3]
1546 role.set_permission_trackers :add_issues, [1, 3]
1547 role.save!
1547 role.save!
1548 User.add_to_project(user, Project.find(1), role)
1548 User.add_to_project(user, Project.find(1), role)
1549
1549
1550 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1550 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1551 end
1551 end
1552
1552
1553 def test_allowed_target_trackers_with_two_roles_allowed_on_some_trackers
1553 def test_allowed_target_trackers_with_two_roles_allowed_on_some_trackers
1554 user = User.generate!
1554 user = User.generate!
1555 role1 = Role.generate!
1555 role1 = Role.generate!
1556 role1.add_permission! :add_issues
1556 role1.add_permission! :add_issues
1557 role1.set_permission_trackers :add_issues, [1]
1557 role1.set_permission_trackers :add_issues, [1]
1558 role1.save!
1558 role1.save!
1559 role2 = Role.generate!
1559 role2 = Role.generate!
1560 role2.add_permission! :add_issues
1560 role2.add_permission! :add_issues
1561 role2.set_permission_trackers :add_issues, [3]
1561 role2.set_permission_trackers :add_issues, [3]
1562 role2.save!
1562 role2.save!
1563 User.add_to_project(user, Project.find(1), [role1, role2])
1563 User.add_to_project(user, Project.find(1), [role1, role2])
1564
1564
1565 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1565 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1566 end
1566 end
1567
1567
1568 def test_allowed_target_trackers_with_two_roles_allowed_on_all_trackers_and_some_trackers
1568 def test_allowed_target_trackers_with_two_roles_allowed_on_all_trackers_and_some_trackers
1569 user = User.generate!
1569 user = User.generate!
1570 role1 = Role.generate!
1570 role1 = Role.generate!
1571 role1.add_permission! :add_issues
1571 role1.add_permission! :add_issues
1572 role1.set_permission_trackers :add_issues, :all
1572 role1.set_permission_trackers :add_issues, :all
1573 role1.save!
1573 role1.save!
1574 role2 = Role.generate!
1574 role2 = Role.generate!
1575 role2.add_permission! :add_issues
1575 role2.add_permission! :add_issues
1576 role2.set_permission_trackers :add_issues, [1, 3]
1576 role2.set_permission_trackers :add_issues, [1, 3]
1577 role2.save!
1577 role2.save!
1578 User.add_to_project(user, Project.find(1), [role1, role2])
1578 User.add_to_project(user, Project.find(1), [role1, role2])
1579
1579
1580 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1580 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1581 end
1581 end
1582
1582
1583 def test_allowed_target_trackers_should_not_consider_roles_without_add_issues_permission
1583 def test_allowed_target_trackers_should_not_consider_roles_without_add_issues_permission
1584 user = User.generate!
1584 user = User.generate!
1585 role1 = Role.generate!
1585 role1 = Role.generate!
1586 role1.remove_permission! :add_issues
1586 role1.remove_permission! :add_issues
1587 role1.set_permission_trackers :add_issues, :all
1587 role1.set_permission_trackers :add_issues, :all
1588 role1.save!
1588 role1.save!
1589 role2 = Role.generate!
1589 role2 = Role.generate!
1590 role2.add_permission! :add_issues
1590 role2.add_permission! :add_issues
1591 role2.set_permission_trackers :add_issues, [1, 3]
1591 role2.set_permission_trackers :add_issues, [1, 3]
1592 role2.save!
1592 role2.save!
1593 User.add_to_project(user, Project.find(1), [role1, role2])
1593 User.add_to_project(user, Project.find(1), [role1, role2])
1594
1594
1595 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1595 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1596 end
1596 end
1597
1597
1598 def test_allowed_target_trackers_without_project_should_be_empty
1598 def test_allowed_target_trackers_without_project_should_be_empty
1599 issue = Issue.new
1599 issue = Issue.new
1600 assert_nil issue.project
1600 assert_nil issue.project
1601 assert_equal [], issue.allowed_target_trackers(User.find(2)).ids
1601 assert_equal [], issue.allowed_target_trackers(User.find(2)).ids
1602 end
1602 end
1603
1603
1604 def test_allowed_target_trackers_should_include_current_tracker
1604 def test_allowed_target_trackers_should_include_current_tracker
1605 user = User.generate!
1605 user = User.generate!
1606 role = Role.generate!
1606 role = Role.generate!
1607 role.add_permission! :add_issues
1607 role.add_permission! :add_issues
1608 role.set_permission_trackers :add_issues, [3]
1608 role.set_permission_trackers :add_issues, [3]
1609 role.save!
1609 role.save!
1610 User.add_to_project(user, Project.find(1), role)
1610 User.add_to_project(user, Project.find(1), role)
1611
1611
1612 issue = Issue.generate!(:project => Project.find(1), :tracker => Tracker.find(1))
1612 issue = Issue.generate!(:project => Project.find(1), :tracker => Tracker.find(1))
1613 assert_equal [1, 3], issue.allowed_target_trackers(user).ids.sort
1613 assert_equal [1, 3], issue.allowed_target_trackers(user).ids.sort
1614 end
1614 end
1615
1615
1616 def test_move_to_another_project_with_same_category
1616 def test_move_to_another_project_with_same_category
1617 issue = Issue.find(1)
1617 issue = Issue.find(1)
1618 issue.project = Project.find(2)
1618 issue.project = Project.find(2)
1619 assert issue.save
1619 assert issue.save
1620 issue.reload
1620 issue.reload
1621 assert_equal 2, issue.project_id
1621 assert_equal 2, issue.project_id
1622 # Category changes
1622 # Category changes
1623 assert_equal 4, issue.category_id
1623 assert_equal 4, issue.category_id
1624 # Make sure time entries were move to the target project
1624 # Make sure time entries were move to the target project
1625 assert_equal 2, issue.time_entries.first.project_id
1625 assert_equal 2, issue.time_entries.first.project_id
1626 end
1626 end
1627
1627
1628 def test_move_to_another_project_without_same_category
1628 def test_move_to_another_project_without_same_category
1629 issue = Issue.find(2)
1629 issue = Issue.find(2)
1630 issue.project = Project.find(2)
1630 issue.project = Project.find(2)
1631 assert issue.save
1631 assert issue.save
1632 issue.reload
1632 issue.reload
1633 assert_equal 2, issue.project_id
1633 assert_equal 2, issue.project_id
1634 # Category cleared
1634 # Category cleared
1635 assert_nil issue.category_id
1635 assert_nil issue.category_id
1636 end
1636 end
1637
1637
1638 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1638 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1639 issue = Issue.find(1)
1639 issue = Issue.find(1)
1640 issue.update!(:fixed_version_id => 3)
1640 issue.update!(:fixed_version_id => 3)
1641 issue.project = Project.find(2)
1641 issue.project = Project.find(2)
1642 assert issue.save
1642 assert issue.save
1643 issue.reload
1643 issue.reload
1644 assert_equal 2, issue.project_id
1644 assert_equal 2, issue.project_id
1645 # Cleared fixed_version
1645 # Cleared fixed_version
1646 assert_equal nil, issue.fixed_version
1646 assert_nil issue.fixed_version
1647 end
1647 end
1648
1648
1649 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1649 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1650 issue = Issue.find(1)
1650 issue = Issue.find(1)
1651 issue.update!(:fixed_version_id => 4)
1651 issue.update!(:fixed_version_id => 4)
1652 issue.project = Project.find(5)
1652 issue.project = Project.find(5)
1653 assert issue.save
1653 assert issue.save
1654 issue.reload
1654 issue.reload
1655 assert_equal 5, issue.project_id
1655 assert_equal 5, issue.project_id
1656 # Keep fixed_version
1656 # Keep fixed_version
1657 assert_equal 4, issue.fixed_version_id
1657 assert_equal 4, issue.fixed_version_id
1658 end
1658 end
1659
1659
1660 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1660 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1661 issue = Issue.find(1)
1661 issue = Issue.find(1)
1662 issue.update!(:fixed_version_id => 3)
1662 issue.update!(:fixed_version_id => 3)
1663 issue.project = Project.find(5)
1663 issue.project = Project.find(5)
1664 assert issue.save
1664 assert issue.save
1665 issue.reload
1665 issue.reload
1666 assert_equal 5, issue.project_id
1666 assert_equal 5, issue.project_id
1667 # Cleared fixed_version
1667 # Cleared fixed_version
1668 assert_equal nil, issue.fixed_version
1668 assert_nil issue.fixed_version
1669 end
1669 end
1670
1670
1671 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1671 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1672 issue = Issue.find(1)
1672 issue = Issue.find(1)
1673 issue.update!(:fixed_version_id => 7)
1673 issue.update!(:fixed_version_id => 7)
1674 issue.project = Project.find(2)
1674 issue.project = Project.find(2)
1675 assert issue.save
1675 assert issue.save
1676 issue.reload
1676 issue.reload
1677 assert_equal 2, issue.project_id
1677 assert_equal 2, issue.project_id
1678 # Keep fixed_version
1678 # Keep fixed_version
1679 assert_equal 7, issue.fixed_version_id
1679 assert_equal 7, issue.fixed_version_id
1680 end
1680 end
1681
1681
1682 def test_move_to_another_project_should_keep_parent_if_valid
1682 def test_move_to_another_project_should_keep_parent_if_valid
1683 issue = Issue.find(1)
1683 issue = Issue.find(1)
1684 issue.update! :parent_issue_id => 2
1684 issue.update! :parent_issue_id => 2
1685 issue.project = Project.find(3)
1685 issue.project = Project.find(3)
1686 assert issue.save
1686 assert issue.save
1687 issue.reload
1687 issue.reload
1688 assert_equal 2, issue.parent_id
1688 assert_equal 2, issue.parent_id
1689 end
1689 end
1690
1690
1691 def test_move_to_another_project_should_clear_parent_if_not_valid
1691 def test_move_to_another_project_should_clear_parent_if_not_valid
1692 issue = Issue.find(1)
1692 issue = Issue.find(1)
1693 issue.update! :parent_issue_id => 2
1693 issue.update! :parent_issue_id => 2
1694 issue.project = Project.find(2)
1694 issue.project = Project.find(2)
1695 assert issue.save
1695 assert issue.save
1696 issue.reload
1696 issue.reload
1697 assert_nil issue.parent_id
1697 assert_nil issue.parent_id
1698 end
1698 end
1699
1699
1700 def test_move_to_another_project_with_disabled_tracker
1700 def test_move_to_another_project_with_disabled_tracker
1701 issue = Issue.find(1)
1701 issue = Issue.find(1)
1702 target = Project.find(2)
1702 target = Project.find(2)
1703 target.tracker_ids = [3]
1703 target.tracker_ids = [3]
1704 target.save
1704 target.save
1705 issue.project = target
1705 issue.project = target
1706 assert issue.save
1706 assert issue.save
1707 issue.reload
1707 issue.reload
1708 assert_equal 2, issue.project_id
1708 assert_equal 2, issue.project_id
1709 assert_equal 3, issue.tracker_id
1709 assert_equal 3, issue.tracker_id
1710 end
1710 end
1711
1711
1712 def test_copy_to_the_same_project
1712 def test_copy_to_the_same_project
1713 issue = Issue.find(1)
1713 issue = Issue.find(1)
1714 copy = issue.copy
1714 copy = issue.copy
1715 assert_difference 'Issue.count' do
1715 assert_difference 'Issue.count' do
1716 copy.save!
1716 copy.save!
1717 end
1717 end
1718 assert_kind_of Issue, copy
1718 assert_kind_of Issue, copy
1719 assert_equal issue.project, copy.project
1719 assert_equal issue.project, copy.project
1720 assert_equal "125", copy.custom_value_for(2).value
1720 assert_equal "125", copy.custom_value_for(2).value
1721 end
1721 end
1722
1722
1723 def test_copy_to_another_project_and_tracker
1723 def test_copy_to_another_project_and_tracker
1724 issue = Issue.find(1)
1724 issue = Issue.find(1)
1725 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1725 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1726 assert_difference 'Issue.count' do
1726 assert_difference 'Issue.count' do
1727 copy.save!
1727 copy.save!
1728 end
1728 end
1729 copy.reload
1729 copy.reload
1730 assert_kind_of Issue, copy
1730 assert_kind_of Issue, copy
1731 assert_equal Project.find(3), copy.project
1731 assert_equal Project.find(3), copy.project
1732 assert_equal Tracker.find(2), copy.tracker
1732 assert_equal Tracker.find(2), copy.tracker
1733 # Custom field #2 is not associated with target tracker
1733 # Custom field #2 is not associated with target tracker
1734 assert_nil copy.custom_value_for(2)
1734 assert_nil copy.custom_value_for(2)
1735 end
1735 end
1736
1736
1737 test "#copy should not create a journal" do
1737 test "#copy should not create a journal" do
1738 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2}, :link => false)
1738 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2}, :link => false)
1739 copy.save!
1739 copy.save!
1740 assert_equal 0, copy.reload.journals.size
1740 assert_equal 0, copy.reload.journals.size
1741 end
1741 end
1742
1742
1743 test "#copy should allow assigned_to changes" do
1743 test "#copy should allow assigned_to changes" do
1744 user = User.generate!
1744 user = User.generate!
1745 Member.create!(:project_id => 3, :principal => user, :role_ids => [1])
1745 Member.create!(:project_id => 3, :principal => user, :role_ids => [1])
1746 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => user.id)
1746 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => user.id)
1747 assert_equal user.id, copy.assigned_to_id
1747 assert_equal user.id, copy.assigned_to_id
1748 end
1748 end
1749
1749
1750 test "#copy should allow status changes" do
1750 test "#copy should allow status changes" do
1751 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1751 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1752 assert_equal 2, copy.status_id
1752 assert_equal 2, copy.status_id
1753 end
1753 end
1754
1754
1755 test "#copy should allow start date changes" do
1755 test "#copy should allow start date changes" do
1756 date = Date.today
1756 date = Date.today
1757 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1757 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1758 assert_equal date, copy.start_date
1758 assert_equal date, copy.start_date
1759 end
1759 end
1760
1760
1761 test "#copy should allow due date changes" do
1761 test "#copy should allow due date changes" do
1762 date = Date.today
1762 date = Date.today
1763 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1763 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1764 assert_equal date, copy.due_date
1764 assert_equal date, copy.due_date
1765 end
1765 end
1766
1766
1767 test "#copy should set current user as author" do
1767 test "#copy should set current user as author" do
1768 User.current = User.find(9)
1768 User.current = User.find(9)
1769 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1769 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1770 assert_equal User.current, copy.author
1770 assert_equal User.current, copy.author
1771 end
1771 end
1772
1772
1773 test "#copy should create a journal with notes" do
1773 test "#copy should create a journal with notes" do
1774 date = Date.today
1774 date = Date.today
1775 notes = "Notes added when copying"
1775 notes = "Notes added when copying"
1776 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1776 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1777 copy.init_journal(User.current, notes)
1777 copy.init_journal(User.current, notes)
1778 copy.save!
1778 copy.save!
1779
1779
1780 assert_equal 1, copy.journals.size
1780 assert_equal 1, copy.journals.size
1781 journal = copy.journals.first
1781 journal = copy.journals.first
1782 assert_equal 0, journal.details.size
1782 assert_equal 0, journal.details.size
1783 assert_equal notes, journal.notes
1783 assert_equal notes, journal.notes
1784 end
1784 end
1785
1785
1786 def test_valid_parent_project
1786 def test_valid_parent_project
1787 issue = Issue.find(1)
1787 issue = Issue.find(1)
1788 issue_in_same_project = Issue.find(2)
1788 issue_in_same_project = Issue.find(2)
1789 issue_in_child_project = Issue.find(5)
1789 issue_in_child_project = Issue.find(5)
1790 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1790 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1791 issue_in_other_child_project = Issue.find(6)
1791 issue_in_other_child_project = Issue.find(6)
1792 issue_in_different_tree = Issue.find(4)
1792 issue_in_different_tree = Issue.find(4)
1793
1793
1794 with_settings :cross_project_subtasks => '' do
1794 with_settings :cross_project_subtasks => '' do
1795 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1795 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1796 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1796 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1797 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1797 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1798 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1798 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1799 end
1799 end
1800
1800
1801 with_settings :cross_project_subtasks => 'system' do
1801 with_settings :cross_project_subtasks => 'system' do
1802 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1802 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1803 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1803 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1804 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1804 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1805 end
1805 end
1806
1806
1807 with_settings :cross_project_subtasks => 'tree' do
1807 with_settings :cross_project_subtasks => 'tree' do
1808 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1808 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1809 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1809 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1810 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1810 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1811 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1811 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1812
1812
1813 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1813 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1814 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1814 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1815 end
1815 end
1816
1816
1817 with_settings :cross_project_subtasks => 'descendants' do
1817 with_settings :cross_project_subtasks => 'descendants' do
1818 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1818 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1819 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1819 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1820 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1820 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1821 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1821 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1822
1822
1823 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1823 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1824 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1824 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1825 end
1825 end
1826 end
1826 end
1827
1827
1828 def test_recipients_should_include_previous_assignee
1828 def test_recipients_should_include_previous_assignee
1829 user = User.find(3)
1829 user = User.find(3)
1830 user.members.update_all ["mail_notification = ?", false]
1830 user.members.update_all ["mail_notification = ?", false]
1831 user.update! :mail_notification => 'only_assigned'
1831 user.update! :mail_notification => 'only_assigned'
1832
1832
1833 issue = Issue.find(2)
1833 issue = Issue.find(2)
1834 issue.assigned_to = nil
1834 issue.assigned_to = nil
1835 assert_include user.mail, issue.recipients
1835 assert_include user.mail, issue.recipients
1836 issue.save!
1836 issue.save!
1837 assert !issue.recipients.include?(user.mail)
1837 assert !issue.recipients.include?(user.mail)
1838 end
1838 end
1839
1839
1840 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1840 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1841 issue = Issue.find(12)
1841 issue = Issue.find(12)
1842 assert issue.recipients.include?(issue.author.mail)
1842 assert issue.recipients.include?(issue.author.mail)
1843 # copy the issue to a private project
1843 # copy the issue to a private project
1844 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1844 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1845 # author is not a member of project anymore
1845 # author is not a member of project anymore
1846 assert !copy.recipients.include?(copy.author.mail)
1846 assert !copy.recipients.include?(copy.author.mail)
1847 end
1847 end
1848
1848
1849 def test_recipients_should_include_the_assigned_group_members
1849 def test_recipients_should_include_the_assigned_group_members
1850 group_member = User.generate!
1850 group_member = User.generate!
1851 group = Group.generate!
1851 group = Group.generate!
1852 group.users << group_member
1852 group.users << group_member
1853
1853
1854 issue = Issue.find(12)
1854 issue = Issue.find(12)
1855 issue.assigned_to = group
1855 issue.assigned_to = group
1856 assert issue.recipients.include?(group_member.mail)
1856 assert issue.recipients.include?(group_member.mail)
1857 end
1857 end
1858
1858
1859 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1859 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1860 user = User.find(3)
1860 user = User.find(3)
1861 issue = Issue.find(9)
1861 issue = Issue.find(9)
1862 Watcher.create!(:user => user, :watchable => issue)
1862 Watcher.create!(:user => user, :watchable => issue)
1863 assert issue.watched_by?(user)
1863 assert issue.watched_by?(user)
1864 assert !issue.watcher_recipients.include?(user.mail)
1864 assert !issue.watcher_recipients.include?(user.mail)
1865 end
1865 end
1866
1866
1867 def test_issue_destroy
1867 def test_issue_destroy
1868 Issue.find(1).destroy
1868 Issue.find(1).destroy
1869 assert_nil Issue.find_by_id(1)
1869 assert_nil Issue.find_by_id(1)
1870 assert_nil TimeEntry.find_by_issue_id(1)
1870 assert_nil TimeEntry.find_by_issue_id(1)
1871 end
1871 end
1872
1872
1873 def test_destroy_should_delete_time_entries_custom_values
1873 def test_destroy_should_delete_time_entries_custom_values
1874 issue = Issue.generate!
1874 issue = Issue.generate!
1875 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1875 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1876
1876
1877 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1877 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1878 assert issue.destroy
1878 assert issue.destroy
1879 end
1879 end
1880 end
1880 end
1881
1881
1882 def test_destroying_a_deleted_issue_should_not_raise_an_error
1882 def test_destroying_a_deleted_issue_should_not_raise_an_error
1883 issue = Issue.find(1)
1883 issue = Issue.find(1)
1884 Issue.find(1).destroy
1884 Issue.find(1).destroy
1885
1885
1886 assert_nothing_raised do
1886 assert_nothing_raised do
1887 assert_no_difference 'Issue.count' do
1887 assert_no_difference 'Issue.count' do
1888 issue.destroy
1888 issue.destroy
1889 end
1889 end
1890 assert issue.destroyed?
1890 assert issue.destroyed?
1891 end
1891 end
1892 end
1892 end
1893
1893
1894 def test_destroying_a_stale_issue_should_not_raise_an_error
1894 def test_destroying_a_stale_issue_should_not_raise_an_error
1895 issue = Issue.find(1)
1895 issue = Issue.find(1)
1896 Issue.find(1).update! :subject => "Updated"
1896 Issue.find(1).update! :subject => "Updated"
1897
1897
1898 assert_nothing_raised do
1898 assert_nothing_raised do
1899 assert_difference 'Issue.count', -1 do
1899 assert_difference 'Issue.count', -1 do
1900 issue.destroy
1900 issue.destroy
1901 end
1901 end
1902 assert issue.destroyed?
1902 assert issue.destroyed?
1903 end
1903 end
1904 end
1904 end
1905
1905
1906 def test_blocked
1906 def test_blocked
1907 blocked_issue = Issue.find(9)
1907 blocked_issue = Issue.find(9)
1908 blocking_issue = Issue.find(10)
1908 blocking_issue = Issue.find(10)
1909
1909
1910 assert blocked_issue.blocked?
1910 assert blocked_issue.blocked?
1911 assert !blocking_issue.blocked?
1911 assert !blocking_issue.blocked?
1912 end
1912 end
1913
1913
1914 def test_blocked_issues_dont_allow_closed_statuses
1914 def test_blocked_issues_dont_allow_closed_statuses
1915 blocked_issue = Issue.find(9)
1915 blocked_issue = Issue.find(9)
1916
1916
1917 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1917 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1918 assert !allowed_statuses.empty?
1918 assert !allowed_statuses.empty?
1919 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1919 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1920 assert closed_statuses.empty?
1920 assert closed_statuses.empty?
1921 end
1921 end
1922
1922
1923 def test_unblocked_issues_allow_closed_statuses
1923 def test_unblocked_issues_allow_closed_statuses
1924 blocking_issue = Issue.find(10)
1924 blocking_issue = Issue.find(10)
1925
1925
1926 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1926 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1927 assert !allowed_statuses.empty?
1927 assert !allowed_statuses.empty?
1928 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1928 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1929 assert !closed_statuses.empty?
1929 assert !closed_statuses.empty?
1930 end
1930 end
1931
1931
1932 def test_reschedule_an_issue_without_dates
1932 def test_reschedule_an_issue_without_dates
1933 with_settings :non_working_week_days => [] do
1933 with_settings :non_working_week_days => [] do
1934 issue = Issue.new(:start_date => nil, :due_date => nil)
1934 issue = Issue.new(:start_date => nil, :due_date => nil)
1935 issue.reschedule_on '2012-10-09'.to_date
1935 issue.reschedule_on '2012-10-09'.to_date
1936 assert_equal '2012-10-09'.to_date, issue.start_date
1936 assert_equal '2012-10-09'.to_date, issue.start_date
1937 assert_equal '2012-10-09'.to_date, issue.due_date
1937 assert_equal '2012-10-09'.to_date, issue.due_date
1938 end
1938 end
1939
1939
1940 with_settings :non_working_week_days => %w(6 7) do
1940 with_settings :non_working_week_days => %w(6 7) do
1941 issue = Issue.new(:start_date => nil, :due_date => nil)
1941 issue = Issue.new(:start_date => nil, :due_date => nil)
1942 issue.reschedule_on '2012-10-09'.to_date
1942 issue.reschedule_on '2012-10-09'.to_date
1943 assert_equal '2012-10-09'.to_date, issue.start_date
1943 assert_equal '2012-10-09'.to_date, issue.start_date
1944 assert_equal '2012-10-09'.to_date, issue.due_date
1944 assert_equal '2012-10-09'.to_date, issue.due_date
1945
1945
1946 issue = Issue.new(:start_date => nil, :due_date => nil)
1946 issue = Issue.new(:start_date => nil, :due_date => nil)
1947 issue.reschedule_on '2012-10-13'.to_date
1947 issue.reschedule_on '2012-10-13'.to_date
1948 assert_equal '2012-10-15'.to_date, issue.start_date
1948 assert_equal '2012-10-15'.to_date, issue.start_date
1949 assert_equal '2012-10-15'.to_date, issue.due_date
1949 assert_equal '2012-10-15'.to_date, issue.due_date
1950 end
1950 end
1951 end
1951 end
1952
1952
1953 def test_reschedule_an_issue_with_start_date
1953 def test_reschedule_an_issue_with_start_date
1954 with_settings :non_working_week_days => [] do
1954 with_settings :non_working_week_days => [] do
1955 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1955 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1956 issue.reschedule_on '2012-10-13'.to_date
1956 issue.reschedule_on '2012-10-13'.to_date
1957 assert_equal '2012-10-13'.to_date, issue.start_date
1957 assert_equal '2012-10-13'.to_date, issue.start_date
1958 assert_equal '2012-10-13'.to_date, issue.due_date
1958 assert_equal '2012-10-13'.to_date, issue.due_date
1959 end
1959 end
1960
1960
1961 with_settings :non_working_week_days => %w(6 7) do
1961 with_settings :non_working_week_days => %w(6 7) do
1962 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1962 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1963 issue.reschedule_on '2012-10-11'.to_date
1963 issue.reschedule_on '2012-10-11'.to_date
1964 assert_equal '2012-10-11'.to_date, issue.start_date
1964 assert_equal '2012-10-11'.to_date, issue.start_date
1965 assert_equal '2012-10-11'.to_date, issue.due_date
1965 assert_equal '2012-10-11'.to_date, issue.due_date
1966
1966
1967 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1967 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1968 issue.reschedule_on '2012-10-13'.to_date
1968 issue.reschedule_on '2012-10-13'.to_date
1969 assert_equal '2012-10-15'.to_date, issue.start_date
1969 assert_equal '2012-10-15'.to_date, issue.start_date
1970 assert_equal '2012-10-15'.to_date, issue.due_date
1970 assert_equal '2012-10-15'.to_date, issue.due_date
1971 end
1971 end
1972 end
1972 end
1973
1973
1974 def test_reschedule_an_issue_with_start_and_due_dates
1974 def test_reschedule_an_issue_with_start_and_due_dates
1975 with_settings :non_working_week_days => [] do
1975 with_settings :non_working_week_days => [] do
1976 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1976 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1977 issue.reschedule_on '2012-10-13'.to_date
1977 issue.reschedule_on '2012-10-13'.to_date
1978 assert_equal '2012-10-13'.to_date, issue.start_date
1978 assert_equal '2012-10-13'.to_date, issue.start_date
1979 assert_equal '2012-10-19'.to_date, issue.due_date
1979 assert_equal '2012-10-19'.to_date, issue.due_date
1980 end
1980 end
1981
1981
1982 with_settings :non_working_week_days => %w(6 7) do
1982 with_settings :non_working_week_days => %w(6 7) do
1983 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1983 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1984 issue.reschedule_on '2012-10-11'.to_date
1984 issue.reschedule_on '2012-10-11'.to_date
1985 assert_equal '2012-10-11'.to_date, issue.start_date
1985 assert_equal '2012-10-11'.to_date, issue.start_date
1986 assert_equal '2012-10-23'.to_date, issue.due_date
1986 assert_equal '2012-10-23'.to_date, issue.due_date
1987
1987
1988 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1988 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1989 issue.reschedule_on '2012-10-13'.to_date
1989 issue.reschedule_on '2012-10-13'.to_date
1990 assert_equal '2012-10-15'.to_date, issue.start_date
1990 assert_equal '2012-10-15'.to_date, issue.start_date
1991 assert_equal '2012-10-25'.to_date, issue.due_date
1991 assert_equal '2012-10-25'.to_date, issue.due_date
1992 end
1992 end
1993 end
1993 end
1994
1994
1995 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1995 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1996 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1996 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1997 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1997 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1998 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1998 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1999 :relation_type => IssueRelation::TYPE_PRECEDES)
1999 :relation_type => IssueRelation::TYPE_PRECEDES)
2000 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2000 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2001
2001
2002 issue1.reload
2002 issue1.reload
2003 issue1.due_date = '2012-10-23'
2003 issue1.due_date = '2012-10-23'
2004 issue1.save!
2004 issue1.save!
2005 issue2.reload
2005 issue2.reload
2006 assert_equal Date.parse('2012-10-24'), issue2.start_date
2006 assert_equal Date.parse('2012-10-24'), issue2.start_date
2007 assert_equal Date.parse('2012-10-26'), issue2.due_date
2007 assert_equal Date.parse('2012-10-26'), issue2.due_date
2008 end
2008 end
2009
2009
2010 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
2010 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
2011 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2011 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2012 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2012 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2013 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2013 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2014 :relation_type => IssueRelation::TYPE_PRECEDES)
2014 :relation_type => IssueRelation::TYPE_PRECEDES)
2015 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2015 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2016
2016
2017 issue1.reload
2017 issue1.reload
2018 issue1.start_date = '2012-09-17'
2018 issue1.start_date = '2012-09-17'
2019 issue1.due_date = '2012-09-18'
2019 issue1.due_date = '2012-09-18'
2020 issue1.save!
2020 issue1.save!
2021 issue2.reload
2021 issue2.reload
2022 assert_equal Date.parse('2012-09-19'), issue2.start_date
2022 assert_equal Date.parse('2012-09-19'), issue2.start_date
2023 assert_equal Date.parse('2012-09-21'), issue2.due_date
2023 assert_equal Date.parse('2012-09-21'), issue2.due_date
2024 end
2024 end
2025
2025
2026 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
2026 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
2027 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2027 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2028 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2028 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2029 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
2029 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
2030 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2030 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2031 :relation_type => IssueRelation::TYPE_PRECEDES)
2031 :relation_type => IssueRelation::TYPE_PRECEDES)
2032 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
2032 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
2033 :relation_type => IssueRelation::TYPE_PRECEDES)
2033 :relation_type => IssueRelation::TYPE_PRECEDES)
2034 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2034 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2035
2035
2036 issue1.reload
2036 issue1.reload
2037 issue1.start_date = '2012-09-17'
2037 issue1.start_date = '2012-09-17'
2038 issue1.due_date = '2012-09-18'
2038 issue1.due_date = '2012-09-18'
2039 issue1.save!
2039 issue1.save!
2040 issue2.reload
2040 issue2.reload
2041 # Issue 2 must start after Issue 3
2041 # Issue 2 must start after Issue 3
2042 assert_equal Date.parse('2012-10-03'), issue2.start_date
2042 assert_equal Date.parse('2012-10-03'), issue2.start_date
2043 assert_equal Date.parse('2012-10-05'), issue2.due_date
2043 assert_equal Date.parse('2012-10-05'), issue2.due_date
2044 end
2044 end
2045
2045
2046 def test_rescheduling_a_stale_issue_should_not_raise_an_error
2046 def test_rescheduling_a_stale_issue_should_not_raise_an_error
2047 with_settings :non_working_week_days => [] do
2047 with_settings :non_working_week_days => [] do
2048 stale = Issue.find(1)
2048 stale = Issue.find(1)
2049 issue = Issue.find(1)
2049 issue = Issue.find(1)
2050 issue.subject = "Updated"
2050 issue.subject = "Updated"
2051 issue.save!
2051 issue.save!
2052 date = 10.days.from_now.to_date
2052 date = 10.days.from_now.to_date
2053 assert_nothing_raised do
2053 assert_nothing_raised do
2054 stale.reschedule_on!(date)
2054 stale.reschedule_on!(date)
2055 end
2055 end
2056 assert_equal date, stale.reload.start_date
2056 assert_equal date, stale.reload.start_date
2057 end
2057 end
2058 end
2058 end
2059
2059
2060 def test_child_issue_should_consider_parent_soonest_start_on_create
2060 def test_child_issue_should_consider_parent_soonest_start_on_create
2061 set_language_if_valid 'en'
2061 set_language_if_valid 'en'
2062 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2062 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2063 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
2063 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
2064 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2064 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2065 :relation_type => IssueRelation::TYPE_PRECEDES)
2065 :relation_type => IssueRelation::TYPE_PRECEDES)
2066 issue1.reload
2066 issue1.reload
2067 issue2.reload
2067 issue2.reload
2068 assert_equal Date.parse('2012-10-18'), issue2.start_date
2068 assert_equal Date.parse('2012-10-18'), issue2.start_date
2069
2069
2070 with_settings :date_format => '%m/%d/%Y' do
2070 with_settings :date_format => '%m/%d/%Y' do
2071 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
2071 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
2072 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
2072 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
2073 assert !child.valid?
2073 assert !child.valid?
2074 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
2074 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
2075 assert_equal Date.parse('2012-10-18'), child.soonest_start
2075 assert_equal Date.parse('2012-10-18'), child.soonest_start
2076 child.start_date = '2012-10-18'
2076 child.start_date = '2012-10-18'
2077 assert child.save
2077 assert child.save
2078 end
2078 end
2079 end
2079 end
2080
2080
2081 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
2081 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
2082 # tests that 3 cannot have 1 as parent:
2082 # tests that 3 cannot have 1 as parent:
2083 #
2083 #
2084 # 1 -> 2 -> 3
2084 # 1 -> 2 -> 3
2085 #
2085 #
2086 set_language_if_valid 'en'
2086 set_language_if_valid 'en'
2087 issue1 = Issue.generate!
2087 issue1 = Issue.generate!
2088 issue2 = Issue.generate!
2088 issue2 = Issue.generate!
2089 issue3 = Issue.generate!
2089 issue3 = Issue.generate!
2090 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2090 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2091 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2091 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2092 issue3.reload
2092 issue3.reload
2093 issue3.parent_issue_id = issue1.id
2093 issue3.parent_issue_id = issue1.id
2094 assert !issue3.valid?
2094 assert !issue3.valid?
2095 assert_include 'Parent task is invalid', issue3.errors.full_messages
2095 assert_include 'Parent task is invalid', issue3.errors.full_messages
2096 end
2096 end
2097
2097
2098 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
2098 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
2099 # tests that 1 cannot have 3 as parent:
2099 # tests that 1 cannot have 3 as parent:
2100 #
2100 #
2101 # 1 -> 2 -> 3
2101 # 1 -> 2 -> 3
2102 #
2102 #
2103 set_language_if_valid 'en'
2103 set_language_if_valid 'en'
2104 issue1 = Issue.generate!
2104 issue1 = Issue.generate!
2105 issue2 = Issue.generate!
2105 issue2 = Issue.generate!
2106 issue3 = Issue.generate!
2106 issue3 = Issue.generate!
2107 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2107 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2108 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2108 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2109 issue1.reload
2109 issue1.reload
2110 issue1.parent_issue_id = issue3.id
2110 issue1.parent_issue_id = issue3.id
2111 assert !issue1.valid?
2111 assert !issue1.valid?
2112 assert_include 'Parent task is invalid', issue1.errors.full_messages
2112 assert_include 'Parent task is invalid', issue1.errors.full_messages
2113 end
2113 end
2114
2114
2115 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
2115 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
2116 # tests that 4 cannot have 1 as parent:
2116 # tests that 4 cannot have 1 as parent:
2117 # changing the due date of 4 would update the end date of 1 which would reschedule 2
2117 # changing the due date of 4 would update the end date of 1 which would reschedule 2
2118 # which would change the end date of 3 which would reschedule 4 and so on...
2118 # which would change the end date of 3 which would reschedule 4 and so on...
2119 #
2119 #
2120 # 3 -> 4
2120 # 3 -> 4
2121 # ^
2121 # ^
2122 # 1 -> 2
2122 # 1 -> 2
2123 #
2123 #
2124 set_language_if_valid 'en'
2124 set_language_if_valid 'en'
2125 issue1 = Issue.generate!
2125 issue1 = Issue.generate!
2126 issue2 = Issue.generate!
2126 issue2 = Issue.generate!
2127 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2127 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2128 issue3 = Issue.generate!
2128 issue3 = Issue.generate!
2129 issue2.reload
2129 issue2.reload
2130 issue2.parent_issue_id = issue3.id
2130 issue2.parent_issue_id = issue3.id
2131 issue2.save!
2131 issue2.save!
2132 issue4 = Issue.generate!
2132 issue4 = Issue.generate!
2133 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
2133 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
2134 issue4.reload
2134 issue4.reload
2135 issue4.parent_issue_id = issue1.id
2135 issue4.parent_issue_id = issue1.id
2136 assert !issue4.valid?
2136 assert !issue4.valid?
2137 assert_include 'Parent task is invalid', issue4.errors.full_messages
2137 assert_include 'Parent task is invalid', issue4.errors.full_messages
2138 end
2138 end
2139
2139
2140 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
2140 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
2141 set_language_if_valid 'en'
2141 set_language_if_valid 'en'
2142 issue1 = Issue.generate!
2142 issue1 = Issue.generate!
2143 issue2 = Issue.generate!
2143 issue2 = Issue.generate!
2144 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
2144 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
2145 parent = Issue.generate!
2145 parent = Issue.generate!
2146 issue1.reload.parent_issue_id = parent.id
2146 issue1.reload.parent_issue_id = parent.id
2147 assert_save issue1
2147 assert_save issue1
2148 parent.reload
2148 parent.reload
2149 issue2.reload.parent_issue_id = parent.id
2149 issue2.reload.parent_issue_id = parent.id
2150 assert_save issue2
2150 assert_save issue2
2151 assert IssueRelation.exists?(relation.id)
2151 assert IssueRelation.exists?(relation.id)
2152 end
2152 end
2153
2153
2154 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
2154 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
2155 set_language_if_valid 'en'
2155 set_language_if_valid 'en'
2156 issue1 = Issue.generate!
2156 issue1 = Issue.generate!
2157 issue2 = Issue.generate!
2157 issue2 = Issue.generate!
2158 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
2158 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
2159 parent = Issue.generate!
2159 parent = Issue.generate!
2160 issue1.reload.parent_issue_id = parent.id
2160 issue1.reload.parent_issue_id = parent.id
2161 assert_save issue1
2161 assert_save issue1
2162 parent.reload
2162 parent.reload
2163 issue2.reload.parent_issue_id = parent.id
2163 issue2.reload.parent_issue_id = parent.id
2164 assert_save issue2
2164 assert_save issue2
2165 assert IssueRelation.exists?(relation.id)
2165 assert IssueRelation.exists?(relation.id)
2166 end
2166 end
2167
2167
2168 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
2168 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
2169 set_language_if_valid 'en'
2169 set_language_if_valid 'en'
2170 issue1 = Issue.generate!
2170 issue1 = Issue.generate!
2171 issue2 = Issue.generate!
2171 issue2 = Issue.generate!
2172 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
2172 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
2173 parent = Issue.generate!
2173 parent = Issue.generate!
2174 issue1.reload.parent_issue_id = parent.id
2174 issue1.reload.parent_issue_id = parent.id
2175 assert_save issue1
2175 assert_save issue1
2176 parent.reload
2176 parent.reload
2177 issue2.reload.parent_issue_id = parent.id
2177 issue2.reload.parent_issue_id = parent.id
2178 assert_save issue2
2178 assert_save issue2
2179 assert IssueRelation.exists?(relation.id)
2179 assert IssueRelation.exists?(relation.id)
2180 end
2180 end
2181
2181
2182 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2182 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2183 set_language_if_valid 'en'
2183 set_language_if_valid 'en'
2184 issue1 = Issue.generate!
2184 issue1 = Issue.generate!
2185 issue2 = Issue.generate!
2185 issue2 = Issue.generate!
2186 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2186 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2187 parent = Issue.generate!
2187 parent = Issue.generate!
2188 issue1.reload.parent_issue_id = parent.id
2188 issue1.reload.parent_issue_id = parent.id
2189 assert_save issue1
2189 assert_save issue1
2190 parent.reload
2190 parent.reload
2191 issue2.reload.parent_issue_id = parent.id
2191 issue2.reload.parent_issue_id = parent.id
2192 assert_save issue2
2192 assert_save issue2
2193 assert IssueRelation.exists?(relation.id)
2193 assert IssueRelation.exists?(relation.id)
2194 end
2194 end
2195
2195
2196 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2196 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2197 issue = Issue.generate!
2197 issue = Issue.generate!
2198 parent = Issue.generate!
2198 parent = Issue.generate!
2199 issue.parent_issue_id = parent.id
2199 issue.parent_issue_id = parent.id
2200 issue.save!
2200 issue.save!
2201 issue.reload
2201 issue.reload
2202
2202
2203 copy = Issue.new.copy_from(issue, :link => true)
2203 copy = Issue.new.copy_from(issue, :link => true)
2204 relation = new_record(IssueRelation) do
2204 relation = new_record(IssueRelation) do
2205 copy.save!
2205 copy.save!
2206 end
2206 end
2207
2207
2208 copy.parent_issue_id = parent.id
2208 copy.parent_issue_id = parent.id
2209 assert_save copy
2209 assert_save copy
2210 assert IssueRelation.exists?(relation.id)
2210 assert IssueRelation.exists?(relation.id)
2211 end
2211 end
2212
2212
2213 def test_overdue
2213 def test_overdue
2214 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2214 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2215 assert !Issue.new(:due_date => Date.today).overdue?
2215 assert !Issue.new(:due_date => Date.today).overdue?
2216 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2216 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2217 assert !Issue.new(:due_date => nil).overdue?
2217 assert !Issue.new(:due_date => nil).overdue?
2218 assert !Issue.new(:due_date => 1.day.ago.to_date,
2218 assert !Issue.new(:due_date => 1.day.ago.to_date,
2219 :status => IssueStatus.where(:is_closed => true).first
2219 :status => IssueStatus.where(:is_closed => true).first
2220 ).overdue?
2220 ).overdue?
2221 end
2221 end
2222
2222
2223 test "#behind_schedule? should be false if the issue has no start_date" do
2223 test "#behind_schedule? should be false if the issue has no start_date" do
2224 assert !Issue.new(:start_date => nil,
2224 assert !Issue.new(:start_date => nil,
2225 :due_date => 1.day.from_now.to_date,
2225 :due_date => 1.day.from_now.to_date,
2226 :done_ratio => 0).behind_schedule?
2226 :done_ratio => 0).behind_schedule?
2227 end
2227 end
2228
2228
2229 test "#behind_schedule? should be false if the issue has no end_date" do
2229 test "#behind_schedule? should be false if the issue has no end_date" do
2230 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2230 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2231 :due_date => nil,
2231 :due_date => nil,
2232 :done_ratio => 0).behind_schedule?
2232 :done_ratio => 0).behind_schedule?
2233 end
2233 end
2234
2234
2235 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2235 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2236 assert !Issue.new(:start_date => 50.days.ago.to_date,
2236 assert !Issue.new(:start_date => 50.days.ago.to_date,
2237 :due_date => 50.days.from_now.to_date,
2237 :due_date => 50.days.from_now.to_date,
2238 :done_ratio => 90).behind_schedule?
2238 :done_ratio => 90).behind_schedule?
2239 end
2239 end
2240
2240
2241 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2241 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2242 assert Issue.new(:start_date => 1.day.ago.to_date,
2242 assert Issue.new(:start_date => 1.day.ago.to_date,
2243 :due_date => 1.day.from_now.to_date,
2243 :due_date => 1.day.from_now.to_date,
2244 :done_ratio => 0).behind_schedule?
2244 :done_ratio => 0).behind_schedule?
2245 end
2245 end
2246
2246
2247 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2247 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2248 assert Issue.new(:start_date => 100.days.ago.to_date,
2248 assert Issue.new(:start_date => 100.days.ago.to_date,
2249 :due_date => Date.today,
2249 :due_date => Date.today,
2250 :done_ratio => 90).behind_schedule?
2250 :done_ratio => 90).behind_schedule?
2251 end
2251 end
2252
2252
2253 test "#assignable_users should be Users" do
2253 test "#assignable_users should be Users" do
2254 assert_kind_of User, Issue.find(1).assignable_users.first
2254 assert_kind_of User, Issue.find(1).assignable_users.first
2255 end
2255 end
2256
2256
2257 test "#assignable_users should include the issue author" do
2257 test "#assignable_users should include the issue author" do
2258 non_project_member = User.generate!
2258 non_project_member = User.generate!
2259 issue = Issue.generate!(:author => non_project_member)
2259 issue = Issue.generate!(:author => non_project_member)
2260
2260
2261 assert issue.assignable_users.include?(non_project_member)
2261 assert issue.assignable_users.include?(non_project_member)
2262 end
2262 end
2263
2263
2264 def test_assignable_users_should_not_include_anonymous_user
2264 def test_assignable_users_should_not_include_anonymous_user
2265 issue = Issue.generate!(:author => User.anonymous)
2265 issue = Issue.generate!(:author => User.anonymous)
2266
2266
2267 assert !issue.assignable_users.include?(User.anonymous)
2267 assert !issue.assignable_users.include?(User.anonymous)
2268 end
2268 end
2269
2269
2270 def test_assignable_users_should_not_include_locked_user
2270 def test_assignable_users_should_not_include_locked_user
2271 user = User.generate!
2271 user = User.generate!
2272 issue = Issue.generate!(:author => user)
2272 issue = Issue.generate!(:author => user)
2273 user.lock!
2273 user.lock!
2274
2274
2275 assert !issue.assignable_users.include?(user)
2275 assert !issue.assignable_users.include?(user)
2276 end
2276 end
2277
2277
2278 def test_assignable_users_should_include_the_current_assignee
2278 def test_assignable_users_should_include_the_current_assignee
2279 user = User.generate!
2279 user = User.generate!
2280 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
2280 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
2281 issue = Issue.generate!(:assigned_to => user)
2281 issue = Issue.generate!(:assigned_to => user)
2282 user.lock!
2282 user.lock!
2283
2283
2284 assert Issue.find(issue.id).assignable_users.include?(user)
2284 assert Issue.find(issue.id).assignable_users.include?(user)
2285 end
2285 end
2286
2286
2287 test "#assignable_users should not show the issue author twice" do
2287 test "#assignable_users should not show the issue author twice" do
2288 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2288 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2289 assert_equal 2, assignable_user_ids.length
2289 assert_equal 2, assignable_user_ids.length
2290
2290
2291 assignable_user_ids.each do |user_id|
2291 assignable_user_ids.each do |user_id|
2292 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2292 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2293 "User #{user_id} appears more or less than once"
2293 "User #{user_id} appears more or less than once"
2294 end
2294 end
2295 end
2295 end
2296
2296
2297 test "#assignable_users with issue_group_assignment should include groups" do
2297 test "#assignable_users with issue_group_assignment should include groups" do
2298 issue = Issue.new(:project => Project.find(2))
2298 issue = Issue.new(:project => Project.find(2))
2299
2299
2300 with_settings :issue_group_assignment => '1' do
2300 with_settings :issue_group_assignment => '1' do
2301 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2301 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2302 assert issue.assignable_users.include?(Group.find(11))
2302 assert issue.assignable_users.include?(Group.find(11))
2303 end
2303 end
2304 end
2304 end
2305
2305
2306 test "#assignable_users without issue_group_assignment should not include groups" do
2306 test "#assignable_users without issue_group_assignment should not include groups" do
2307 issue = Issue.new(:project => Project.find(2))
2307 issue = Issue.new(:project => Project.find(2))
2308
2308
2309 with_settings :issue_group_assignment => '0' do
2309 with_settings :issue_group_assignment => '0' do
2310 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2310 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2311 assert !issue.assignable_users.include?(Group.find(11))
2311 assert !issue.assignable_users.include?(Group.find(11))
2312 end
2312 end
2313 end
2313 end
2314
2314
2315 def test_assignable_users_should_not_include_builtin_groups
2315 def test_assignable_users_should_not_include_builtin_groups
2316 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2316 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2317 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2317 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2318 issue = Issue.new(:project => Project.find(1))
2318 issue = Issue.new(:project => Project.find(1))
2319
2319
2320 with_settings :issue_group_assignment => '1' do
2320 with_settings :issue_group_assignment => '1' do
2321 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2321 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2322 end
2322 end
2323 end
2323 end
2324
2324
2325 def test_assignable_users_should_not_include_users_that_cannot_view_the_tracker
2325 def test_assignable_users_should_not_include_users_that_cannot_view_the_tracker
2326 user = User.find(3)
2326 user = User.find(3)
2327 role = Role.find(2)
2327 role = Role.find(2)
2328 role.set_permission_trackers :view_issues, [1, 3]
2328 role.set_permission_trackers :view_issues, [1, 3]
2329 role.save!
2329 role.save!
2330
2330
2331 issue1 = Issue.new(:project_id => 1, :tracker_id => 1)
2331 issue1 = Issue.new(:project_id => 1, :tracker_id => 1)
2332 issue2 = Issue.new(:project_id => 1, :tracker_id => 2)
2332 issue2 = Issue.new(:project_id => 1, :tracker_id => 2)
2333
2333
2334 assert_include user, issue1.assignable_users
2334 assert_include user, issue1.assignable_users
2335 assert_not_include user, issue2.assignable_users
2335 assert_not_include user, issue2.assignable_users
2336 end
2336 end
2337
2337
2338 def test_create_should_send_email_notification
2338 def test_create_should_send_email_notification
2339 ActionMailer::Base.deliveries.clear
2339 ActionMailer::Base.deliveries.clear
2340 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2340 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2341 :author_id => 3, :status_id => 1,
2341 :author_id => 3, :status_id => 1,
2342 :priority => IssuePriority.all.first,
2342 :priority => IssuePriority.all.first,
2343 :subject => 'test_create', :estimated_hours => '1:30')
2343 :subject => 'test_create', :estimated_hours => '1:30')
2344 with_settings :notified_events => %w(issue_added) do
2344 with_settings :notified_events => %w(issue_added) do
2345 assert issue.save
2345 assert issue.save
2346 assert_equal 1, ActionMailer::Base.deliveries.size
2346 assert_equal 1, ActionMailer::Base.deliveries.size
2347 end
2347 end
2348 end
2348 end
2349
2349
2350 def test_create_should_send_one_email_notification_with_both_settings
2350 def test_create_should_send_one_email_notification_with_both_settings
2351 ActionMailer::Base.deliveries.clear
2351 ActionMailer::Base.deliveries.clear
2352 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2352 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2353 :author_id => 3, :status_id => 1,
2353 :author_id => 3, :status_id => 1,
2354 :priority => IssuePriority.all.first,
2354 :priority => IssuePriority.all.first,
2355 :subject => 'test_create', :estimated_hours => '1:30')
2355 :subject => 'test_create', :estimated_hours => '1:30')
2356 with_settings :notified_events => %w(issue_added issue_updated) do
2356 with_settings :notified_events => %w(issue_added issue_updated) do
2357 assert issue.save
2357 assert issue.save
2358 assert_equal 1, ActionMailer::Base.deliveries.size
2358 assert_equal 1, ActionMailer::Base.deliveries.size
2359 end
2359 end
2360 end
2360 end
2361
2361
2362 def test_create_should_not_send_email_notification_with_no_setting
2362 def test_create_should_not_send_email_notification_with_no_setting
2363 ActionMailer::Base.deliveries.clear
2363 ActionMailer::Base.deliveries.clear
2364 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2364 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2365 :author_id => 3, :status_id => 1,
2365 :author_id => 3, :status_id => 1,
2366 :priority => IssuePriority.all.first,
2366 :priority => IssuePriority.all.first,
2367 :subject => 'test_create', :estimated_hours => '1:30')
2367 :subject => 'test_create', :estimated_hours => '1:30')
2368 with_settings :notified_events => [] do
2368 with_settings :notified_events => [] do
2369 assert issue.save
2369 assert issue.save
2370 assert_equal 0, ActionMailer::Base.deliveries.size
2370 assert_equal 0, ActionMailer::Base.deliveries.size
2371 end
2371 end
2372 end
2372 end
2373
2373
2374 def test_update_should_notify_previous_assignee
2374 def test_update_should_notify_previous_assignee
2375 ActionMailer::Base.deliveries.clear
2375 ActionMailer::Base.deliveries.clear
2376 user = User.find(3)
2376 user = User.find(3)
2377 user.members.update_all ["mail_notification = ?", false]
2377 user.members.update_all ["mail_notification = ?", false]
2378 user.update! :mail_notification => 'only_assigned'
2378 user.update! :mail_notification => 'only_assigned'
2379
2379
2380 with_settings :notified_events => %w(issue_updated) do
2380 with_settings :notified_events => %w(issue_updated) do
2381 issue = Issue.find(2)
2381 issue = Issue.find(2)
2382 issue.init_journal User.find(1)
2382 issue.init_journal User.find(1)
2383 issue.assigned_to = nil
2383 issue.assigned_to = nil
2384 issue.save!
2384 issue.save!
2385 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2385 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2386 end
2386 end
2387 end
2387 end
2388
2388
2389 def test_stale_issue_should_not_send_email_notification
2389 def test_stale_issue_should_not_send_email_notification
2390 ActionMailer::Base.deliveries.clear
2390 ActionMailer::Base.deliveries.clear
2391 issue = Issue.find(1)
2391 issue = Issue.find(1)
2392 stale = Issue.find(1)
2392 stale = Issue.find(1)
2393
2393
2394 issue.init_journal(User.find(1))
2394 issue.init_journal(User.find(1))
2395 issue.subject = 'Subjet update'
2395 issue.subject = 'Subjet update'
2396 with_settings :notified_events => %w(issue_updated) do
2396 with_settings :notified_events => %w(issue_updated) do
2397 assert issue.save
2397 assert issue.save
2398 assert_equal 1, ActionMailer::Base.deliveries.size
2398 assert_equal 1, ActionMailer::Base.deliveries.size
2399 ActionMailer::Base.deliveries.clear
2399 ActionMailer::Base.deliveries.clear
2400
2400
2401 stale.init_journal(User.find(1))
2401 stale.init_journal(User.find(1))
2402 stale.subject = 'Another subjet update'
2402 stale.subject = 'Another subjet update'
2403 assert_raise ActiveRecord::StaleObjectError do
2403 assert_raise ActiveRecord::StaleObjectError do
2404 stale.save
2404 stale.save
2405 end
2405 end
2406 assert ActionMailer::Base.deliveries.empty?
2406 assert ActionMailer::Base.deliveries.empty?
2407 end
2407 end
2408 end
2408 end
2409
2409
2410 def test_journalized_description
2410 def test_journalized_description
2411 IssueCustomField.delete_all
2411 IssueCustomField.delete_all
2412
2412
2413 i = Issue.first
2413 i = Issue.first
2414 old_description = i.description
2414 old_description = i.description
2415 new_description = "This is the new description"
2415 new_description = "This is the new description"
2416
2416
2417 i.init_journal(User.find(2))
2417 i.init_journal(User.find(2))
2418 i.description = new_description
2418 i.description = new_description
2419 assert_difference 'Journal.count', 1 do
2419 assert_difference 'Journal.count', 1 do
2420 assert_difference 'JournalDetail.count', 1 do
2420 assert_difference 'JournalDetail.count', 1 do
2421 i.save!
2421 i.save!
2422 end
2422 end
2423 end
2423 end
2424
2424
2425 detail = JournalDetail.order('id DESC').first
2425 detail = JournalDetail.order('id DESC').first
2426 assert_equal i, detail.journal.journalized
2426 assert_equal i, detail.journal.journalized
2427 assert_equal 'attr', detail.property
2427 assert_equal 'attr', detail.property
2428 assert_equal 'description', detail.prop_key
2428 assert_equal 'description', detail.prop_key
2429 assert_equal old_description, detail.old_value
2429 assert_equal old_description, detail.old_value
2430 assert_equal new_description, detail.value
2430 assert_equal new_description, detail.value
2431 end
2431 end
2432
2432
2433 def test_blank_descriptions_should_not_be_journalized
2433 def test_blank_descriptions_should_not_be_journalized
2434 IssueCustomField.delete_all
2434 IssueCustomField.delete_all
2435 Issue.where(:id => 1).update_all("description = NULL")
2435 Issue.where(:id => 1).update_all("description = NULL")
2436
2436
2437 i = Issue.find(1)
2437 i = Issue.find(1)
2438 i.init_journal(User.find(2))
2438 i.init_journal(User.find(2))
2439 i.subject = "blank description"
2439 i.subject = "blank description"
2440 i.description = "\r\n"
2440 i.description = "\r\n"
2441
2441
2442 assert_difference 'Journal.count', 1 do
2442 assert_difference 'Journal.count', 1 do
2443 assert_difference 'JournalDetail.count', 1 do
2443 assert_difference 'JournalDetail.count', 1 do
2444 i.save!
2444 i.save!
2445 end
2445 end
2446 end
2446 end
2447 end
2447 end
2448
2448
2449 def test_journalized_multi_custom_field
2449 def test_journalized_multi_custom_field
2450 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2450 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2451 :is_filter => true, :is_for_all => true,
2451 :is_filter => true, :is_for_all => true,
2452 :tracker_ids => [1],
2452 :tracker_ids => [1],
2453 :possible_values => ['value1', 'value2', 'value3'],
2453 :possible_values => ['value1', 'value2', 'value3'],
2454 :multiple => true)
2454 :multiple => true)
2455
2455
2456 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2456 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2457 :subject => 'Test', :author_id => 1)
2457 :subject => 'Test', :author_id => 1)
2458
2458
2459 assert_difference 'Journal.count' do
2459 assert_difference 'Journal.count' do
2460 assert_difference 'JournalDetail.count' do
2460 assert_difference 'JournalDetail.count' do
2461 issue.init_journal(User.first)
2461 issue.init_journal(User.first)
2462 issue.custom_field_values = {field.id => ['value1']}
2462 issue.custom_field_values = {field.id => ['value1']}
2463 issue.save!
2463 issue.save!
2464 end
2464 end
2465 assert_difference 'JournalDetail.count' do
2465 assert_difference 'JournalDetail.count' do
2466 issue.init_journal(User.first)
2466 issue.init_journal(User.first)
2467 issue.custom_field_values = {field.id => ['value1', 'value2']}
2467 issue.custom_field_values = {field.id => ['value1', 'value2']}
2468 issue.save!
2468 issue.save!
2469 end
2469 end
2470 assert_difference 'JournalDetail.count', 2 do
2470 assert_difference 'JournalDetail.count', 2 do
2471 issue.init_journal(User.first)
2471 issue.init_journal(User.first)
2472 issue.custom_field_values = {field.id => ['value3', 'value2']}
2472 issue.custom_field_values = {field.id => ['value3', 'value2']}
2473 issue.save!
2473 issue.save!
2474 end
2474 end
2475 assert_difference 'JournalDetail.count', 2 do
2475 assert_difference 'JournalDetail.count', 2 do
2476 issue.init_journal(User.first)
2476 issue.init_journal(User.first)
2477 issue.custom_field_values = {field.id => nil}
2477 issue.custom_field_values = {field.id => nil}
2478 issue.save!
2478 issue.save!
2479 end
2479 end
2480 end
2480 end
2481 end
2481 end
2482
2482
2483 def test_custom_value_cleared_on_tracker_change_should_be_journalized
2483 def test_custom_value_cleared_on_tracker_change_should_be_journalized
2484 a = IssueCustomField.generate!(:tracker_ids => [1])
2484 a = IssueCustomField.generate!(:tracker_ids => [1])
2485 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {a.id.to_s => "foo"})
2485 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {a.id.to_s => "foo"})
2486 assert_equal "foo", issue.custom_field_value(a)
2486 assert_equal "foo", issue.custom_field_value(a)
2487
2487
2488 journal = new_record(Journal) do
2488 journal = new_record(Journal) do
2489 issue.init_journal(User.first)
2489 issue.init_journal(User.first)
2490 issue.tracker_id = 2
2490 issue.tracker_id = 2
2491 issue.save!
2491 issue.save!
2492 end
2492 end
2493 details = journal.details.select {|d| d.property == 'cf' && d.prop_key == a.id.to_s}
2493 details = journal.details.select {|d| d.property == 'cf' && d.prop_key == a.id.to_s}
2494 assert_equal 1, details.size
2494 assert_equal 1, details.size
2495 assert_equal 'foo', details.first.old_value
2495 assert_equal 'foo', details.first.old_value
2496 assert_nil details.first.value
2496 assert_nil details.first.value
2497 end
2497 end
2498
2498
2499 def test_description_eol_should_be_normalized
2499 def test_description_eol_should_be_normalized
2500 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2500 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2501 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2501 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2502 end
2502 end
2503
2503
2504 def test_saving_twice_should_not_duplicate_journal_details
2504 def test_saving_twice_should_not_duplicate_journal_details
2505 i = Issue.first
2505 i = Issue.first
2506 i.init_journal(User.find(2), 'Some notes')
2506 i.init_journal(User.find(2), 'Some notes')
2507 # initial changes
2507 # initial changes
2508 i.subject = 'New subject'
2508 i.subject = 'New subject'
2509 i.done_ratio = i.done_ratio + 10
2509 i.done_ratio = i.done_ratio + 10
2510 assert_difference 'Journal.count' do
2510 assert_difference 'Journal.count' do
2511 assert i.save
2511 assert i.save
2512 end
2512 end
2513 # 1 more change
2513 # 1 more change
2514 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2514 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2515 assert_no_difference 'Journal.count' do
2515 assert_no_difference 'Journal.count' do
2516 assert_difference 'JournalDetail.count', 1 do
2516 assert_difference 'JournalDetail.count', 1 do
2517 i.save
2517 i.save
2518 end
2518 end
2519 end
2519 end
2520 # no more change
2520 # no more change
2521 assert_no_difference 'Journal.count' do
2521 assert_no_difference 'Journal.count' do
2522 assert_no_difference 'JournalDetail.count' do
2522 assert_no_difference 'JournalDetail.count' do
2523 i.save
2523 i.save
2524 end
2524 end
2525 end
2525 end
2526 end
2526 end
2527
2527
2528 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2528 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2529 @issue = Issue.find(1)
2529 @issue = Issue.find(1)
2530 @issue_status = IssueStatus.find(1)
2530 @issue_status = IssueStatus.find(1)
2531 @issue_status.update!(:default_done_ratio => 50)
2531 @issue_status.update!(:default_done_ratio => 50)
2532 @issue2 = Issue.find(2)
2532 @issue2 = Issue.find(2)
2533 @issue_status2 = IssueStatus.find(2)
2533 @issue_status2 = IssueStatus.find(2)
2534 @issue_status2.update!(:default_done_ratio => 0)
2534 @issue_status2.update!(:default_done_ratio => 0)
2535
2535
2536 with_settings :issue_done_ratio => 'issue_field' do
2536 with_settings :issue_done_ratio => 'issue_field' do
2537 assert_equal 0, @issue.done_ratio
2537 assert_equal 0, @issue.done_ratio
2538 assert_equal 30, @issue2.done_ratio
2538 assert_equal 30, @issue2.done_ratio
2539 end
2539 end
2540
2540
2541 with_settings :issue_done_ratio => 'issue_status' do
2541 with_settings :issue_done_ratio => 'issue_status' do
2542 assert_equal 50, @issue.done_ratio
2542 assert_equal 50, @issue.done_ratio
2543 assert_equal 0, @issue2.done_ratio
2543 assert_equal 0, @issue2.done_ratio
2544 end
2544 end
2545 end
2545 end
2546
2546
2547 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2547 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2548 @issue = Issue.find(1)
2548 @issue = Issue.find(1)
2549 @issue_status = IssueStatus.find(1)
2549 @issue_status = IssueStatus.find(1)
2550 @issue_status.update!(:default_done_ratio => 50)
2550 @issue_status.update!(:default_done_ratio => 50)
2551 @issue2 = Issue.find(2)
2551 @issue2 = Issue.find(2)
2552 @issue_status2 = IssueStatus.find(2)
2552 @issue_status2 = IssueStatus.find(2)
2553 @issue_status2.update!(:default_done_ratio => 0)
2553 @issue_status2.update!(:default_done_ratio => 0)
2554
2554
2555 with_settings :issue_done_ratio => 'issue_field' do
2555 with_settings :issue_done_ratio => 'issue_field' do
2556 @issue.update_done_ratio_from_issue_status
2556 @issue.update_done_ratio_from_issue_status
2557 @issue2.update_done_ratio_from_issue_status
2557 @issue2.update_done_ratio_from_issue_status
2558
2558
2559 assert_equal 0, @issue.read_attribute(:done_ratio)
2559 assert_equal 0, @issue.read_attribute(:done_ratio)
2560 assert_equal 30, @issue2.read_attribute(:done_ratio)
2560 assert_equal 30, @issue2.read_attribute(:done_ratio)
2561 end
2561 end
2562
2562
2563 with_settings :issue_done_ratio => 'issue_status' do
2563 with_settings :issue_done_ratio => 'issue_status' do
2564 @issue.update_done_ratio_from_issue_status
2564 @issue.update_done_ratio_from_issue_status
2565 @issue2.update_done_ratio_from_issue_status
2565 @issue2.update_done_ratio_from_issue_status
2566
2566
2567 assert_equal 50, @issue.read_attribute(:done_ratio)
2567 assert_equal 50, @issue.read_attribute(:done_ratio)
2568 assert_equal 0, @issue2.read_attribute(:done_ratio)
2568 assert_equal 0, @issue2.read_attribute(:done_ratio)
2569 end
2569 end
2570 end
2570 end
2571
2571
2572 test "#by_tracker" do
2572 test "#by_tracker" do
2573 User.current = User.anonymous
2573 User.current = User.anonymous
2574 groups = Issue.by_tracker(Project.find(1))
2574 groups = Issue.by_tracker(Project.find(1))
2575 assert_equal 3, groups.count
2575 assert_equal 3, groups.count
2576 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2576 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2577 end
2577 end
2578
2578
2579 test "#by_version" do
2579 test "#by_version" do
2580 User.current = User.anonymous
2580 User.current = User.anonymous
2581 groups = Issue.by_version(Project.find(1))
2581 groups = Issue.by_version(Project.find(1))
2582 assert_equal 3, groups.count
2582 assert_equal 3, groups.count
2583 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2583 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2584 end
2584 end
2585
2585
2586 test "#by_priority" do
2586 test "#by_priority" do
2587 User.current = User.anonymous
2587 User.current = User.anonymous
2588 groups = Issue.by_priority(Project.find(1))
2588 groups = Issue.by_priority(Project.find(1))
2589 assert_equal 4, groups.count
2589 assert_equal 4, groups.count
2590 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2590 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2591 end
2591 end
2592
2592
2593 test "#by_category" do
2593 test "#by_category" do
2594 User.current = User.anonymous
2594 User.current = User.anonymous
2595 groups = Issue.by_category(Project.find(1))
2595 groups = Issue.by_category(Project.find(1))
2596 assert_equal 2, groups.count
2596 assert_equal 2, groups.count
2597 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2597 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2598 end
2598 end
2599
2599
2600 test "#by_assigned_to" do
2600 test "#by_assigned_to" do
2601 User.current = User.anonymous
2601 User.current = User.anonymous
2602 groups = Issue.by_assigned_to(Project.find(1))
2602 groups = Issue.by_assigned_to(Project.find(1))
2603 assert_equal 2, groups.count
2603 assert_equal 2, groups.count
2604 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2604 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2605 end
2605 end
2606
2606
2607 test "#by_author" do
2607 test "#by_author" do
2608 User.current = User.anonymous
2608 User.current = User.anonymous
2609 groups = Issue.by_author(Project.find(1))
2609 groups = Issue.by_author(Project.find(1))
2610 assert_equal 4, groups.count
2610 assert_equal 4, groups.count
2611 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2611 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2612 end
2612 end
2613
2613
2614 test "#by_subproject" do
2614 test "#by_subproject" do
2615 User.current = User.anonymous
2615 User.current = User.anonymous
2616 groups = Issue.by_subproject(Project.find(1))
2616 groups = Issue.by_subproject(Project.find(1))
2617 # Private descendant not visible
2617 # Private descendant not visible
2618 assert_equal 1, groups.count
2618 assert_equal 1, groups.count
2619 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2619 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2620 end
2620 end
2621
2621
2622 def test_recently_updated_scope
2622 def test_recently_updated_scope
2623 #should return the last updated issue
2623 #should return the last updated issue
2624 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2624 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2625 end
2625 end
2626
2626
2627 def test_on_active_projects_scope
2627 def test_on_active_projects_scope
2628 assert Project.find(2).archive
2628 assert Project.find(2).archive
2629
2629
2630 before = Issue.on_active_project.length
2630 before = Issue.on_active_project.length
2631 # test inclusion to results
2631 # test inclusion to results
2632 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2632 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2633 assert_equal before + 1, Issue.on_active_project.length
2633 assert_equal before + 1, Issue.on_active_project.length
2634
2634
2635 # Move to an archived project
2635 # Move to an archived project
2636 issue.project = Project.find(2)
2636 issue.project = Project.find(2)
2637 assert issue.save
2637 assert issue.save
2638 assert_equal before, Issue.on_active_project.length
2638 assert_equal before, Issue.on_active_project.length
2639 end
2639 end
2640
2640
2641 test "Issue#recipients should include project recipients" do
2641 test "Issue#recipients should include project recipients" do
2642 issue = Issue.generate!
2642 issue = Issue.generate!
2643 assert issue.project.recipients.present?
2643 assert issue.project.recipients.present?
2644 issue.project.recipients.each do |project_recipient|
2644 issue.project.recipients.each do |project_recipient|
2645 assert issue.recipients.include?(project_recipient)
2645 assert issue.recipients.include?(project_recipient)
2646 end
2646 end
2647 end
2647 end
2648
2648
2649 test "Issue#recipients should include the author if the author is active" do
2649 test "Issue#recipients should include the author if the author is active" do
2650 issue = Issue.generate!(:author => User.generate!)
2650 issue = Issue.generate!(:author => User.generate!)
2651 assert issue.author, "No author set for Issue"
2651 assert issue.author, "No author set for Issue"
2652 assert issue.recipients.include?(issue.author.mail)
2652 assert issue.recipients.include?(issue.author.mail)
2653 end
2653 end
2654
2654
2655 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2655 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2656 user = User.generate!
2656 user = User.generate!
2657 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
2657 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
2658 issue = Issue.generate!(:assigned_to => user)
2658 issue = Issue.generate!(:assigned_to => user)
2659 assert issue.assigned_to, "No assigned_to set for Issue"
2659 assert issue.assigned_to, "No assigned_to set for Issue"
2660 assert issue.recipients.include?(issue.assigned_to.mail)
2660 assert issue.recipients.include?(issue.assigned_to.mail)
2661 end
2661 end
2662
2662
2663 test "Issue#recipients should not include users who opt out of all email" do
2663 test "Issue#recipients should not include users who opt out of all email" do
2664 issue = Issue.generate!(:author => User.generate!)
2664 issue = Issue.generate!(:author => User.generate!)
2665 issue.author.update!(:mail_notification => :none)
2665 issue.author.update!(:mail_notification => :none)
2666 assert !issue.recipients.include?(issue.author.mail)
2666 assert !issue.recipients.include?(issue.author.mail)
2667 end
2667 end
2668
2668
2669 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2669 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2670 issue = Issue.generate!(:author => User.generate!)
2670 issue = Issue.generate!(:author => User.generate!)
2671 issue.author.update!(:mail_notification => :only_assigned)
2671 issue.author.update!(:mail_notification => :only_assigned)
2672 assert !issue.recipients.include?(issue.author.mail)
2672 assert !issue.recipients.include?(issue.author.mail)
2673 end
2673 end
2674
2674
2675 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2675 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2676 user = User.generate!
2676 user = User.generate!
2677 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
2677 Member.create!(:project_id => 1, :principal => user, :role_ids => [1])
2678 issue = Issue.generate!(:assigned_to => user)
2678 issue = Issue.generate!(:assigned_to => user)
2679 issue.assigned_to.update!(:mail_notification => :only_owner)
2679 issue.assigned_to.update!(:mail_notification => :only_owner)
2680 assert !issue.recipients.include?(issue.assigned_to.mail)
2680 assert !issue.recipients.include?(issue.assigned_to.mail)
2681 end
2681 end
2682
2682
2683 def test_last_journal_id_with_journals_should_return_the_journal_id
2683 def test_last_journal_id_with_journals_should_return_the_journal_id
2684 assert_equal 2, Issue.find(1).last_journal_id
2684 assert_equal 2, Issue.find(1).last_journal_id
2685 end
2685 end
2686
2686
2687 def test_last_journal_id_without_journals_should_return_nil
2687 def test_last_journal_id_without_journals_should_return_nil
2688 assert_nil Issue.find(3).last_journal_id
2688 assert_nil Issue.find(3).last_journal_id
2689 end
2689 end
2690
2690
2691 def test_journals_after_should_return_journals_with_greater_id
2691 def test_journals_after_should_return_journals_with_greater_id
2692 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2692 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2693 assert_equal [], Issue.find(1).journals_after('2')
2693 assert_equal [], Issue.find(1).journals_after('2')
2694 end
2694 end
2695
2695
2696 def test_journals_after_with_blank_arg_should_return_all_journals
2696 def test_journals_after_with_blank_arg_should_return_all_journals
2697 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2697 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2698 end
2698 end
2699
2699
2700 def test_css_classes_should_include_tracker
2700 def test_css_classes_should_include_tracker
2701 issue = Issue.new(:tracker => Tracker.find(2))
2701 issue = Issue.new(:tracker => Tracker.find(2))
2702 classes = issue.css_classes.split(' ')
2702 classes = issue.css_classes.split(' ')
2703 assert_include 'tracker-2', classes
2703 assert_include 'tracker-2', classes
2704 end
2704 end
2705
2705
2706 def test_css_classes_should_include_priority
2706 def test_css_classes_should_include_priority
2707 issue = Issue.new(:priority => IssuePriority.find(8))
2707 issue = Issue.new(:priority => IssuePriority.find(8))
2708 classes = issue.css_classes.split(' ')
2708 classes = issue.css_classes.split(' ')
2709 assert_include 'priority-8', classes
2709 assert_include 'priority-8', classes
2710 assert_include 'priority-highest', classes
2710 assert_include 'priority-highest', classes
2711 end
2711 end
2712
2712
2713 def test_css_classes_should_include_user_and_group_assignment
2713 def test_css_classes_should_include_user_and_group_assignment
2714 project = Project.first
2714 project = Project.first
2715 user = User.generate!
2715 user = User.generate!
2716 group = Group.generate!
2716 group = Group.generate!
2717 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2717 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2718 group.users << user
2718 group.users << user
2719 assert user.member_of?(project)
2719 assert user.member_of?(project)
2720 issue1 = Issue.generate(:assigned_to_id => group.id)
2720 issue1 = Issue.generate(:assigned_to_id => group.id)
2721 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2721 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2722 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2722 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2723 issue2 = Issue.generate(:assigned_to_id => user.id)
2723 issue2 = Issue.generate(:assigned_to_id => user.id)
2724 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2724 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2725 assert_include 'assigned-to-me', issue2.css_classes(user)
2725 assert_include 'assigned-to-me', issue2.css_classes(user)
2726 end
2726 end
2727
2727
2728 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2728 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2729 set_tmp_attachments_directory
2729 set_tmp_attachments_directory
2730 issue = Issue.generate!
2730 issue = Issue.generate!
2731 issue.save_attachments({
2731 issue.save_attachments({
2732 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2732 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2733 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2733 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2734 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2734 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2735 })
2735 })
2736 issue.attach_saved_attachments
2736 issue.attach_saved_attachments
2737
2737
2738 assert_equal 3, issue.reload.attachments.count
2738 assert_equal 3, issue.reload.attachments.count
2739 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2739 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2740 end
2740 end
2741
2741
2742 def test_save_attachments_with_array_should_warn_about_missing_tokens
2742 def test_save_attachments_with_array_should_warn_about_missing_tokens
2743 set_tmp_attachments_directory
2743 set_tmp_attachments_directory
2744 issue = Issue.generate!
2744 issue = Issue.generate!
2745 issue.save_attachments([
2745 issue.save_attachments([
2746 {'token' => 'missing'}
2746 {'token' => 'missing'}
2747 ])
2747 ])
2748 assert !issue.save
2748 assert !issue.save
2749 assert issue.errors[:base].present?
2749 assert issue.errors[:base].present?
2750 assert_equal 0, issue.reload.attachments.count
2750 assert_equal 0, issue.reload.attachments.count
2751 end
2751 end
2752
2752
2753 def test_closed_on_should_be_nil_when_creating_an_open_issue
2753 def test_closed_on_should_be_nil_when_creating_an_open_issue
2754 issue = Issue.generate!(:status_id => 1).reload
2754 issue = Issue.generate!(:status_id => 1).reload
2755 assert !issue.closed?
2755 assert !issue.closed?
2756 assert_nil issue.closed_on
2756 assert_nil issue.closed_on
2757 end
2757 end
2758
2758
2759 def test_closed_on_should_be_set_when_creating_a_closed_issue
2759 def test_closed_on_should_be_set_when_creating_a_closed_issue
2760 issue = Issue.generate!(:status_id => 5).reload
2760 issue = Issue.generate!(:status_id => 5).reload
2761 assert issue.closed?
2761 assert issue.closed?
2762 assert_not_nil issue.closed_on
2762 assert_not_nil issue.closed_on
2763 assert_equal issue.updated_on, issue.closed_on
2763 assert_equal issue.updated_on, issue.closed_on
2764 assert_equal issue.created_on, issue.closed_on
2764 assert_equal issue.created_on, issue.closed_on
2765 end
2765 end
2766
2766
2767 def test_closed_on_should_be_nil_when_updating_an_open_issue
2767 def test_closed_on_should_be_nil_when_updating_an_open_issue
2768 issue = Issue.find(1)
2768 issue = Issue.find(1)
2769 issue.subject = 'Not closed yet'
2769 issue.subject = 'Not closed yet'
2770 issue.save!
2770 issue.save!
2771 issue.reload
2771 issue.reload
2772 assert_nil issue.closed_on
2772 assert_nil issue.closed_on
2773 end
2773 end
2774
2774
2775 def test_closed_on_should_be_set_when_closing_an_open_issue
2775 def test_closed_on_should_be_set_when_closing_an_open_issue
2776 issue = Issue.find(1)
2776 issue = Issue.find(1)
2777 issue.subject = 'Now closed'
2777 issue.subject = 'Now closed'
2778 issue.status_id = 5
2778 issue.status_id = 5
2779 issue.save!
2779 issue.save!
2780 issue.reload
2780 issue.reload
2781 assert_not_nil issue.closed_on
2781 assert_not_nil issue.closed_on
2782 assert_equal issue.updated_on, issue.closed_on
2782 assert_equal issue.updated_on, issue.closed_on
2783 end
2783 end
2784
2784
2785 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2785 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2786 issue = Issue.open(false).first
2786 issue = Issue.open(false).first
2787 was_closed_on = issue.closed_on
2787 was_closed_on = issue.closed_on
2788 assert_not_nil was_closed_on
2788 assert_not_nil was_closed_on
2789 issue.subject = 'Updating a closed issue'
2789 issue.subject = 'Updating a closed issue'
2790 issue.save!
2790 issue.save!
2791 issue.reload
2791 issue.reload
2792 assert_equal was_closed_on, issue.closed_on
2792 assert_equal was_closed_on, issue.closed_on
2793 end
2793 end
2794
2794
2795 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2795 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2796 issue = Issue.open(false).first
2796 issue = Issue.open(false).first
2797 was_closed_on = issue.closed_on
2797 was_closed_on = issue.closed_on
2798 assert_not_nil was_closed_on
2798 assert_not_nil was_closed_on
2799 issue.subject = 'Reopening a closed issue'
2799 issue.subject = 'Reopening a closed issue'
2800 issue.status_id = 1
2800 issue.status_id = 1
2801 issue.save!
2801 issue.save!
2802 issue.reload
2802 issue.reload
2803 assert !issue.closed?
2803 assert !issue.closed?
2804 assert_equal was_closed_on, issue.closed_on
2804 assert_equal was_closed_on, issue.closed_on
2805 end
2805 end
2806
2806
2807 def test_status_was_should_return_nil_for_new_issue
2807 def test_status_was_should_return_nil_for_new_issue
2808 issue = Issue.new
2808 issue = Issue.new
2809 assert_nil issue.status_was
2809 assert_nil issue.status_was
2810 end
2810 end
2811
2811
2812 def test_status_was_should_return_status_before_change
2812 def test_status_was_should_return_status_before_change
2813 issue = Issue.find(1)
2813 issue = Issue.find(1)
2814 issue.status = IssueStatus.find(2)
2814 issue.status = IssueStatus.find(2)
2815 assert_equal IssueStatus.find(1), issue.status_was
2815 assert_equal IssueStatus.find(1), issue.status_was
2816 end
2816 end
2817
2817
2818 def test_status_was_should_return_status_before_change_with_status_id
2818 def test_status_was_should_return_status_before_change_with_status_id
2819 issue = Issue.find(1)
2819 issue = Issue.find(1)
2820 assert_equal IssueStatus.find(1), issue.status
2820 assert_equal IssueStatus.find(1), issue.status
2821 issue.status_id = 2
2821 issue.status_id = 2
2822 assert_equal IssueStatus.find(1), issue.status_was
2822 assert_equal IssueStatus.find(1), issue.status_was
2823 end
2823 end
2824
2824
2825 def test_status_was_should_be_reset_on_save
2825 def test_status_was_should_be_reset_on_save
2826 issue = Issue.find(1)
2826 issue = Issue.find(1)
2827 issue.status = IssueStatus.find(2)
2827 issue.status = IssueStatus.find(2)
2828 assert_equal IssueStatus.find(1), issue.status_was
2828 assert_equal IssueStatus.find(1), issue.status_was
2829 assert issue.save!
2829 assert issue.save!
2830 assert_equal IssueStatus.find(2), issue.status_was
2830 assert_equal IssueStatus.find(2), issue.status_was
2831 end
2831 end
2832
2832
2833 def test_closing_should_return_true_when_closing_an_issue
2833 def test_closing_should_return_true_when_closing_an_issue
2834 issue = Issue.find(1)
2834 issue = Issue.find(1)
2835 issue.status = IssueStatus.find(2)
2835 issue.status = IssueStatus.find(2)
2836 assert_equal false, issue.closing?
2836 assert_equal false, issue.closing?
2837 issue.status = IssueStatus.find(5)
2837 issue.status = IssueStatus.find(5)
2838 assert_equal true, issue.closing?
2838 assert_equal true, issue.closing?
2839 end
2839 end
2840
2840
2841 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2841 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2842 issue = Issue.find(1)
2842 issue = Issue.find(1)
2843 issue.status_id = 2
2843 issue.status_id = 2
2844 assert_equal false, issue.closing?
2844 assert_equal false, issue.closing?
2845 issue.status_id = 5
2845 issue.status_id = 5
2846 assert_equal true, issue.closing?
2846 assert_equal true, issue.closing?
2847 end
2847 end
2848
2848
2849 def test_closing_should_return_true_for_new_closed_issue
2849 def test_closing_should_return_true_for_new_closed_issue
2850 issue = Issue.new
2850 issue = Issue.new
2851 assert_equal false, issue.closing?
2851 assert_equal false, issue.closing?
2852 issue.status = IssueStatus.find(5)
2852 issue.status = IssueStatus.find(5)
2853 assert_equal true, issue.closing?
2853 assert_equal true, issue.closing?
2854 end
2854 end
2855
2855
2856 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2856 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2857 issue = Issue.new
2857 issue = Issue.new
2858 assert_equal false, issue.closing?
2858 assert_equal false, issue.closing?
2859 issue.status_id = 5
2859 issue.status_id = 5
2860 assert_equal true, issue.closing?
2860 assert_equal true, issue.closing?
2861 end
2861 end
2862
2862
2863 def test_closing_should_be_reset_after_save
2863 def test_closing_should_be_reset_after_save
2864 issue = Issue.find(1)
2864 issue = Issue.find(1)
2865 issue.status_id = 5
2865 issue.status_id = 5
2866 assert_equal true, issue.closing?
2866 assert_equal true, issue.closing?
2867 issue.save!
2867 issue.save!
2868 assert_equal false, issue.closing?
2868 assert_equal false, issue.closing?
2869 end
2869 end
2870
2870
2871 def test_reopening_should_return_true_when_reopening_an_issue
2871 def test_reopening_should_return_true_when_reopening_an_issue
2872 issue = Issue.find(8)
2872 issue = Issue.find(8)
2873 issue.status = IssueStatus.find(6)
2873 issue.status = IssueStatus.find(6)
2874 assert_equal false, issue.reopening?
2874 assert_equal false, issue.reopening?
2875 issue.status = IssueStatus.find(2)
2875 issue.status = IssueStatus.find(2)
2876 assert_equal true, issue.reopening?
2876 assert_equal true, issue.reopening?
2877 end
2877 end
2878
2878
2879 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2879 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2880 issue = Issue.find(8)
2880 issue = Issue.find(8)
2881 issue.status_id = 6
2881 issue.status_id = 6
2882 assert_equal false, issue.reopening?
2882 assert_equal false, issue.reopening?
2883 issue.status_id = 2
2883 issue.status_id = 2
2884 assert_equal true, issue.reopening?
2884 assert_equal true, issue.reopening?
2885 end
2885 end
2886
2886
2887 def test_reopening_should_return_false_for_new_open_issue
2887 def test_reopening_should_return_false_for_new_open_issue
2888 issue = Issue.new
2888 issue = Issue.new
2889 issue.status = IssueStatus.find(1)
2889 issue.status = IssueStatus.find(1)
2890 assert_equal false, issue.reopening?
2890 assert_equal false, issue.reopening?
2891 end
2891 end
2892
2892
2893 def test_reopening_should_be_reset_after_save
2893 def test_reopening_should_be_reset_after_save
2894 issue = Issue.find(8)
2894 issue = Issue.find(8)
2895 issue.status_id = 2
2895 issue.status_id = 2
2896 assert_equal true, issue.reopening?
2896 assert_equal true, issue.reopening?
2897 issue.save!
2897 issue.save!
2898 assert_equal false, issue.reopening?
2898 assert_equal false, issue.reopening?
2899 end
2899 end
2900
2900
2901 def test_default_status_without_tracker_should_be_nil
2901 def test_default_status_without_tracker_should_be_nil
2902 issue = Issue.new
2902 issue = Issue.new
2903 assert_nil issue.tracker
2903 assert_nil issue.tracker
2904 assert_nil issue.default_status
2904 assert_nil issue.default_status
2905 end
2905 end
2906
2906
2907 def test_default_status_should_be_tracker_default_status
2907 def test_default_status_should_be_tracker_default_status
2908 issue = Issue.new(:tracker_id => 1)
2908 issue = Issue.new(:tracker_id => 1)
2909 assert_not_nil issue.status
2909 assert_not_nil issue.status
2910 assert_equal issue.tracker.default_status, issue.default_status
2910 assert_equal issue.tracker.default_status, issue.default_status
2911 end
2911 end
2912
2912
2913 def test_initializing_with_tracker_should_set_default_status
2913 def test_initializing_with_tracker_should_set_default_status
2914 issue = Issue.new(:tracker => Tracker.find(1))
2914 issue = Issue.new(:tracker => Tracker.find(1))
2915 assert_not_nil issue.status
2915 assert_not_nil issue.status
2916 assert_equal issue.default_status, issue.status
2916 assert_equal issue.default_status, issue.status
2917 end
2917 end
2918
2918
2919 def test_initializing_with_tracker_id_should_set_default_status
2919 def test_initializing_with_tracker_id_should_set_default_status
2920 issue = Issue.new(:tracker_id => 1)
2920 issue = Issue.new(:tracker_id => 1)
2921 assert_not_nil issue.status
2921 assert_not_nil issue.status
2922 assert_equal issue.default_status, issue.status
2922 assert_equal issue.default_status, issue.status
2923 end
2923 end
2924
2924
2925 def test_setting_tracker_should_set_default_status
2925 def test_setting_tracker_should_set_default_status
2926 issue = Issue.new
2926 issue = Issue.new
2927 issue.tracker = Tracker.find(1)
2927 issue.tracker = Tracker.find(1)
2928 assert_not_nil issue.status
2928 assert_not_nil issue.status
2929 assert_equal issue.default_status, issue.status
2929 assert_equal issue.default_status, issue.status
2930 end
2930 end
2931
2931
2932 def test_changing_tracker_should_set_default_status_if_status_was_default
2932 def test_changing_tracker_should_set_default_status_if_status_was_default
2933 WorkflowTransition.delete_all
2933 WorkflowTransition.delete_all
2934 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2934 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2935 Tracker.find(2).update! :default_status_id => 2
2935 Tracker.find(2).update! :default_status_id => 2
2936
2936
2937 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2937 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2938 assert_equal IssueStatus.find(1), issue.status
2938 assert_equal IssueStatus.find(1), issue.status
2939 issue.tracker = Tracker.find(2)
2939 issue.tracker = Tracker.find(2)
2940 assert_equal IssueStatus.find(2), issue.status
2940 assert_equal IssueStatus.find(2), issue.status
2941 end
2941 end
2942
2942
2943 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2943 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2944 WorkflowTransition.delete_all
2944 WorkflowTransition.delete_all
2945 Tracker.find(2).update! :default_status_id => 2
2945 Tracker.find(2).update! :default_status_id => 2
2946
2946
2947 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2947 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2948 assert_equal IssueStatus.find(3), issue.status
2948 assert_equal IssueStatus.find(3), issue.status
2949 issue.tracker = Tracker.find(2)
2949 issue.tracker = Tracker.find(2)
2950 assert_equal IssueStatus.find(2), issue.status
2950 assert_equal IssueStatus.find(2), issue.status
2951 end
2951 end
2952
2952
2953 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2953 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2954 WorkflowTransition.delete_all
2954 WorkflowTransition.delete_all
2955 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2955 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2956 Tracker.find(2).update! :default_status_id => 2
2956 Tracker.find(2).update! :default_status_id => 2
2957
2957
2958 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2958 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2959 assert_equal IssueStatus.find(3), issue.status
2959 assert_equal IssueStatus.find(3), issue.status
2960 issue.tracker = Tracker.find(2)
2960 issue.tracker = Tracker.find(2)
2961 assert_equal IssueStatus.find(3), issue.status
2961 assert_equal IssueStatus.find(3), issue.status
2962 end
2962 end
2963
2963
2964 def test_assigned_to_was_with_a_group
2964 def test_assigned_to_was_with_a_group
2965 group = Group.find(10)
2965 group = Group.find(10)
2966 Member.create!(:project_id => 1, :principal => group, :role_ids => [1])
2966 Member.create!(:project_id => 1, :principal => group, :role_ids => [1])
2967
2967
2968 with_settings :issue_group_assignment => '1' do
2968 with_settings :issue_group_assignment => '1' do
2969 issue = Issue.generate!(:assigned_to => group)
2969 issue = Issue.generate!(:assigned_to => group)
2970 issue.reload.assigned_to = nil
2970 issue.reload.assigned_to = nil
2971 assert_equal group, issue.assigned_to_was
2971 assert_equal group, issue.assigned_to_was
2972 end
2972 end
2973 end
2973 end
2974
2974
2975 def test_issue_overdue_should_respect_user_timezone
2975 def test_issue_overdue_should_respect_user_timezone
2976 user_in_europe = users(:users_001)
2976 user_in_europe = users(:users_001)
2977 user_in_europe.pref.update! :time_zone => 'UTC'
2977 user_in_europe.pref.update! :time_zone => 'UTC'
2978
2978
2979 user_in_asia = users(:users_002)
2979 user_in_asia = users(:users_002)
2980 user_in_asia.pref.update! :time_zone => 'Hongkong'
2980 user_in_asia.pref.update! :time_zone => 'Hongkong'
2981
2981
2982 issue = Issue.generate! :due_date => Date.parse('2016-03-20')
2982 issue = Issue.generate! :due_date => Date.parse('2016-03-20')
2983
2983
2984 # server time is UTC
2984 # server time is UTC
2985 time = Time.parse '2016-03-20 20:00 UTC'
2985 time = Time.parse '2016-03-20 20:00 UTC'
2986 Time.stubs(:now).returns(time)
2986 Time.stubs(:now).returns(time)
2987 Date.stubs(:today).returns(time.to_date)
2987 Date.stubs(:today).returns(time.to_date)
2988
2988
2989 # for a user in the same time zone as the server the issue is not overdue
2989 # for a user in the same time zone as the server the issue is not overdue
2990 # yet
2990 # yet
2991 User.current = user_in_europe
2991 User.current = user_in_europe
2992 assert !issue.overdue?
2992 assert !issue.overdue?
2993
2993
2994 # at the same time, a user in East Asia looks at the issue - it's already
2994 # at the same time, a user in East Asia looks at the issue - it's already
2995 # March 21st and the issue should be marked overdue
2995 # March 21st and the issue should be marked overdue
2996 User.current = user_in_asia
2996 User.current = user_in_asia
2997 assert issue.overdue?
2997 assert issue.overdue?
2998
2998
2999 end
2999 end
3000 end
3000 end
@@ -1,223 +1,223
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class JournalTest < ActiveSupport::TestCase
20 class JournalTest < ActiveSupport::TestCase
21 fixtures :projects, :issues, :issue_statuses, :journals, :journal_details,
21 fixtures :projects, :issues, :issue_statuses, :journals, :journal_details,
22 :issue_relations, :workflows,
22 :issue_relations, :workflows,
23 :users, :members, :member_roles, :roles, :enabled_modules,
23 :users, :members, :member_roles, :roles, :enabled_modules,
24 :groups_users, :email_addresses,
24 :groups_users, :email_addresses,
25 :enumerations,
25 :enumerations,
26 :projects_trackers, :trackers, :custom_fields
26 :projects_trackers, :trackers, :custom_fields
27
27
28 def setup
28 def setup
29 @journal = Journal.find 1
29 @journal = Journal.find 1
30 User.current = nil
30 User.current = nil
31 end
31 end
32
32
33 def test_journalized_is_an_issue
33 def test_journalized_is_an_issue
34 issue = @journal.issue
34 issue = @journal.issue
35 assert_kind_of Issue, issue
35 assert_kind_of Issue, issue
36 assert_equal 1, issue.id
36 assert_equal 1, issue.id
37 end
37 end
38
38
39 def test_new_status
39 def test_new_status
40 status = @journal.new_status
40 status = @journal.new_status
41 assert_not_nil status
41 assert_not_nil status
42 assert_kind_of IssueStatus, status
42 assert_kind_of IssueStatus, status
43 assert_equal 2, status.id
43 assert_equal 2, status.id
44 end
44 end
45
45
46 def test_create_should_send_email_notification
46 def test_create_should_send_email_notification
47 ActionMailer::Base.deliveries.clear
47 ActionMailer::Base.deliveries.clear
48 issue = Issue.first
48 issue = Issue.first
49 user = User.first
49 user = User.first
50 journal = issue.init_journal(user, issue)
50 journal = issue.init_journal(user, issue)
51
51
52 assert journal.save
52 assert journal.save
53 assert_equal 1, ActionMailer::Base.deliveries.size
53 assert_equal 1, ActionMailer::Base.deliveries.size
54 end
54 end
55
55
56 def test_should_not_save_journal_with_blank_notes_and_no_details
56 def test_should_not_save_journal_with_blank_notes_and_no_details
57 journal = Journal.new(:journalized => Issue.first, :user => User.first)
57 journal = Journal.new(:journalized => Issue.first, :user => User.first)
58
58
59 assert_no_difference 'Journal.count' do
59 assert_no_difference 'Journal.count' do
60 assert_equal false, journal.save
60 assert_equal false, journal.save
61 end
61 end
62 end
62 end
63
63
64 def test_create_should_not_split_non_private_notes
64 def test_create_should_not_split_non_private_notes
65 assert_difference 'Journal.count' do
65 assert_difference 'Journal.count' do
66 assert_no_difference 'JournalDetail.count' do
66 assert_no_difference 'JournalDetail.count' do
67 journal = Journal.generate!(:notes => 'Notes')
67 journal = Journal.generate!(:notes => 'Notes')
68 end
68 end
69 end
69 end
70
70
71 assert_difference 'Journal.count' do
71 assert_difference 'Journal.count' do
72 assert_difference 'JournalDetail.count' do
72 assert_difference 'JournalDetail.count' do
73 journal = Journal.generate!(:notes => 'Notes', :details => [JournalDetail.new])
73 journal = Journal.generate!(:notes => 'Notes', :details => [JournalDetail.new])
74 end
74 end
75 end
75 end
76
76
77 assert_difference 'Journal.count' do
77 assert_difference 'Journal.count' do
78 assert_difference 'JournalDetail.count' do
78 assert_difference 'JournalDetail.count' do
79 journal = Journal.generate!(:notes => '', :details => [JournalDetail.new])
79 journal = Journal.generate!(:notes => '', :details => [JournalDetail.new])
80 end
80 end
81 end
81 end
82 end
82 end
83
83
84 def test_create_should_split_private_notes
84 def test_create_should_split_private_notes
85 assert_difference 'Journal.count' do
85 assert_difference 'Journal.count' do
86 assert_no_difference 'JournalDetail.count' do
86 assert_no_difference 'JournalDetail.count' do
87 journal = Journal.generate!(:notes => 'Notes', :private_notes => true)
87 journal = Journal.generate!(:notes => 'Notes', :private_notes => true)
88 journal.reload
88 journal.reload
89 assert_equal true, journal.private_notes
89 assert_equal true, journal.private_notes
90 assert_equal 'Notes', journal.notes
90 assert_equal 'Notes', journal.notes
91 end
91 end
92 end
92 end
93
93
94 assert_difference 'Journal.count', 2 do
94 assert_difference 'Journal.count', 2 do
95 assert_difference 'JournalDetail.count' do
95 assert_difference 'JournalDetail.count' do
96 journal = Journal.generate!(:notes => 'Notes', :private_notes => true, :details => [JournalDetail.new])
96 journal = Journal.generate!(:notes => 'Notes', :private_notes => true, :details => [JournalDetail.new])
97 journal.reload
97 journal.reload
98 assert_equal true, journal.private_notes
98 assert_equal true, journal.private_notes
99 assert_equal 'Notes', journal.notes
99 assert_equal 'Notes', journal.notes
100 assert_equal 0, journal.details.size
100 assert_equal 0, journal.details.size
101
101
102 journal_with_changes = Journal.order('id DESC').offset(1).first
102 journal_with_changes = Journal.order('id DESC').offset(1).first
103 assert_equal false, journal_with_changes.private_notes
103 assert_equal false, journal_with_changes.private_notes
104 assert_nil journal_with_changes.notes
104 assert_nil journal_with_changes.notes
105 assert_equal 1, journal_with_changes.details.size
105 assert_equal 1, journal_with_changes.details.size
106 assert_equal journal.created_on, journal_with_changes.created_on
106 assert_equal journal.created_on, journal_with_changes.created_on
107 end
107 end
108 end
108 end
109
109
110 assert_difference 'Journal.count' do
110 assert_difference 'Journal.count' do
111 assert_difference 'JournalDetail.count' do
111 assert_difference 'JournalDetail.count' do
112 journal = Journal.generate!(:notes => '', :private_notes => true, :details => [JournalDetail.new])
112 journal = Journal.generate!(:notes => '', :private_notes => true, :details => [JournalDetail.new])
113 journal.reload
113 journal.reload
114 assert_equal false, journal.private_notes
114 assert_equal false, journal.private_notes
115 assert_equal '', journal.notes
115 assert_equal '', journal.notes
116 assert_equal 1, journal.details.size
116 assert_equal 1, journal.details.size
117 end
117 end
118 end
118 end
119 end
119 end
120
120
121 def test_visible_scope_for_anonymous
121 def test_visible_scope_for_anonymous
122 # Anonymous user should see issues of public projects only
122 # Anonymous user should see issues of public projects only
123 journals = Journal.visible(User.anonymous).to_a
123 journals = Journal.visible(User.anonymous).to_a
124 assert journals.any?
124 assert journals.any?
125 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
125 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
126 # Anonymous user should not see issues without permission
126 # Anonymous user should not see issues without permission
127 Role.anonymous.remove_permission!(:view_issues)
127 Role.anonymous.remove_permission!(:view_issues)
128 journals = Journal.visible(User.anonymous).to_a
128 journals = Journal.visible(User.anonymous).to_a
129 assert journals.empty?
129 assert journals.empty?
130 end
130 end
131
131
132 def test_visible_scope_for_user
132 def test_visible_scope_for_user
133 user = User.find(9)
133 user = User.find(9)
134 assert user.projects.empty?
134 assert user.projects.empty?
135 # Non member user should see issues of public projects only
135 # Non member user should see issues of public projects only
136 journals = Journal.visible(user).to_a
136 journals = Journal.visible(user).to_a
137 assert journals.any?
137 assert journals.any?
138 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
138 assert_nil journals.detect {|journal| !journal.issue.project.is_public?}
139 # Non member user should not see issues without permission
139 # Non member user should not see issues without permission
140 Role.non_member.remove_permission!(:view_issues)
140 Role.non_member.remove_permission!(:view_issues)
141 user.reload
141 user.reload
142 journals = Journal.visible(user).to_a
142 journals = Journal.visible(user).to_a
143 assert journals.empty?
143 assert journals.empty?
144 # User should see issues of projects for which user has view_issues permissions only
144 # User should see issues of projects for which user has view_issues permissions only
145 Member.create!(:principal => user, :project_id => 1, :role_ids => [1])
145 Member.create!(:principal => user, :project_id => 1, :role_ids => [1])
146 user.reload
146 user.reload
147 journals = Journal.visible(user).to_a
147 journals = Journal.visible(user).to_a
148 assert journals.any?
148 assert journals.any?
149 assert_nil journals.detect {|journal| journal.issue.project_id != 1}
149 assert_nil journals.detect {|journal| journal.issue.project_id != 1}
150 end
150 end
151
151
152 def test_visible_scope_for_admin
152 def test_visible_scope_for_admin
153 user = User.find(1)
153 user = User.find(1)
154 user.members.each(&:destroy)
154 user.members.each(&:destroy)
155 assert user.projects.empty?
155 assert user.projects.empty?
156 journals = Journal.visible(user).to_a
156 journals = Journal.visible(user).to_a
157 assert journals.any?
157 assert journals.any?
158 # Admin should see issues on private projects that admin does not belong to
158 # Admin should see issues on private projects that admin does not belong to
159 assert journals.detect {|journal| !journal.issue.project.is_public?}
159 assert journals.detect {|journal| !journal.issue.project.is_public?}
160 end
160 end
161
161
162 def test_preload_journals_details_custom_fields_should_set_custom_field_instance_variable
162 def test_preload_journals_details_custom_fields_should_set_custom_field_instance_variable
163 d = JournalDetail.new(:property => 'cf', :prop_key => '2')
163 d = JournalDetail.new(:property => 'cf', :prop_key => '2')
164 journals = [Journal.new(:details => [d])]
164 journals = [Journal.new(:details => [d])]
165
165
166 d.expects(:instance_variable_set).with("@custom_field", CustomField.find(2)).once
166 d.expects(:instance_variable_set).with("@custom_field", CustomField.find(2)).once
167 Journal.preload_journals_details_custom_fields(journals)
167 Journal.preload_journals_details_custom_fields(journals)
168 end
168 end
169
169
170 def test_preload_journals_details_custom_fields_with_empty_set
170 def test_preload_journals_details_custom_fields_with_empty_set
171 assert_nothing_raised do
171 assert_nothing_raised do
172 Journal.preload_journals_details_custom_fields([])
172 Journal.preload_journals_details_custom_fields([])
173 end
173 end
174 end
174 end
175
175
176 def test_details_should_normalize_dates
176 def test_details_should_normalize_dates
177 j = JournalDetail.create!(:old_value => Date.parse('2012-11-03'), :value => Date.parse('2013-01-02'))
177 j = JournalDetail.create!(:old_value => Date.parse('2012-11-03'), :value => Date.parse('2013-01-02'))
178 j.reload
178 j.reload
179 assert_equal '2012-11-03', j.old_value
179 assert_equal '2012-11-03', j.old_value
180 assert_equal '2013-01-02', j.value
180 assert_equal '2013-01-02', j.value
181 end
181 end
182
182
183 def test_details_should_normalize_true_values
183 def test_details_should_normalize_true_values
184 j = JournalDetail.create!(:old_value => true, :value => true)
184 j = JournalDetail.create!(:old_value => true, :value => true)
185 j.reload
185 j.reload
186 assert_equal '1', j.old_value
186 assert_equal '1', j.old_value
187 assert_equal '1', j.value
187 assert_equal '1', j.value
188 end
188 end
189
189
190 def test_details_should_normalize_false_values
190 def test_details_should_normalize_false_values
191 j = JournalDetail.create!(:old_value => false, :value => false)
191 j = JournalDetail.create!(:old_value => false, :value => false)
192 j.reload
192 j.reload
193 assert_equal '0', j.old_value
193 assert_equal '0', j.old_value
194 assert_equal '0', j.value
194 assert_equal '0', j.value
195 end
195 end
196
196
197 def test_custom_field_should_return_custom_field_for_cf_detail
197 def test_custom_field_should_return_custom_field_for_cf_detail
198 d = JournalDetail.new(:property => 'cf', :prop_key => '2')
198 d = JournalDetail.new(:property => 'cf', :prop_key => '2')
199 assert_equal CustomField.find(2), d.custom_field
199 assert_equal CustomField.find(2), d.custom_field
200 end
200 end
201
201
202 def test_custom_field_should_return_nil_for_non_cf_detail
202 def test_custom_field_should_return_nil_for_non_cf_detail
203 d = JournalDetail.new(:property => 'subject')
203 d = JournalDetail.new(:property => 'subject')
204 assert_equal nil, d.custom_field
204 assert_nil d.custom_field
205 end
205 end
206
206
207 def test_visible_details_should_include_relations_to_visible_issues_only
207 def test_visible_details_should_include_relations_to_visible_issues_only
208 issue = Issue.generate!
208 issue = Issue.generate!
209 visible_issue = Issue.generate!
209 visible_issue = Issue.generate!
210 hidden_issue = Issue.generate!(:is_private => true)
210 hidden_issue = Issue.generate!(:is_private => true)
211
211
212 journal = Journal.new
212 journal = Journal.new
213 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'relates', :value => visible_issue.id)
213 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'relates', :value => visible_issue.id)
214 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'relates', :value => hidden_issue.id)
214 journal.details << JournalDetail.new(:property => 'relation', :prop_key => 'relates', :value => hidden_issue.id)
215
215
216 visible_details = journal.visible_details(User.anonymous)
216 visible_details = journal.visible_details(User.anonymous)
217 assert_equal 1, visible_details.size
217 assert_equal 1, visible_details.size
218 assert_equal visible_issue.id.to_s, visible_details.first.value.to_s
218 assert_equal visible_issue.id.to_s, visible_details.first.value.to_s
219
219
220 visible_details = journal.visible_details(User.find(2))
220 visible_details = journal.visible_details(User.find(2))
221 assert_equal 2, visible_details.size
221 assert_equal 2, visible_details.size
222 end
222 end
223 end
223 end
@@ -1,88 +1,88
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../test_helper', __FILE__)
19
19
20 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
20 class Redmine::CodesetUtilTest < ActiveSupport::TestCase
21
21
22 def test_to_utf8_by_setting_from_latin1
22 def test_to_utf8_by_setting_from_latin1
23 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
23 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
24 s1 = "Texte encod\xc3\xa9".force_encoding("UTF-8")
24 s1 = "Texte encod\xc3\xa9".force_encoding("UTF-8")
25 s2 = "Texte encod\xe9".force_encoding("ASCII-8BIT")
25 s2 = "Texte encod\xe9".force_encoding("ASCII-8BIT")
26 s3 = s2.dup.force_encoding("UTF-8")
26 s3 = s2.dup.force_encoding("UTF-8")
27 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
27 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
28 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
28 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
29 end
29 end
30 end
30 end
31
31
32 def test_to_utf8_by_setting_from_euc_jp
32 def test_to_utf8_by_setting_from_euc_jp
33 with_settings :repositories_encodings => 'UTF-8,EUC-JP' do
33 with_settings :repositories_encodings => 'UTF-8,EUC-JP' do
34 s1 = "\xe3\x83\xac\xe3\x83\x83\xe3\x83\x89\xe3\x83\x9e\xe3\x82\xa4\xe3\x83\xb3".force_encoding("UTF-8")
34 s1 = "\xe3\x83\xac\xe3\x83\x83\xe3\x83\x89\xe3\x83\x9e\xe3\x82\xa4\xe3\x83\xb3".force_encoding("UTF-8")
35 s2 = "\xa5\xec\xa5\xc3\xa5\xc9\xa5\xde\xa5\xa4\xa5\xf3".force_encoding("ASCII-8BIT")
35 s2 = "\xa5\xec\xa5\xc3\xa5\xc9\xa5\xde\xa5\xa4\xa5\xf3".force_encoding("ASCII-8BIT")
36 s3 = s2.dup.force_encoding("UTF-8")
36 s3 = s2.dup.force_encoding("UTF-8")
37 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
37 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
38 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
38 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
39 end
39 end
40 end
40 end
41
41
42 def test_to_utf8_by_setting_should_be_converted_all_latin1
42 def test_to_utf8_by_setting_should_be_converted_all_latin1
43 with_settings :repositories_encodings => 'ISO-8859-1' do
43 with_settings :repositories_encodings => 'ISO-8859-1' do
44 s1 = "\xc3\x82\xc2\x80".force_encoding("UTF-8")
44 s1 = "\xc3\x82\xc2\x80".force_encoding("UTF-8")
45 s2 = "\xC2\x80".force_encoding("ASCII-8BIT")
45 s2 = "\xC2\x80".force_encoding("ASCII-8BIT")
46 s3 = s2.dup.force_encoding("UTF-8")
46 s3 = s2.dup.force_encoding("UTF-8")
47 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
47 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s2)
48 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
48 assert_equal s1, Redmine::CodesetUtil.to_utf8_by_setting(s3)
49 end
49 end
50 end
50 end
51
51
52 def test_to_utf8_by_setting_blank_string
52 def test_to_utf8_by_setting_blank_string
53 assert_equal "", Redmine::CodesetUtil.to_utf8_by_setting("")
53 assert_equal "", Redmine::CodesetUtil.to_utf8_by_setting("")
54 assert_equal nil, Redmine::CodesetUtil.to_utf8_by_setting(nil)
54 assert_nil Redmine::CodesetUtil.to_utf8_by_setting(nil)
55 end
55 end
56
56
57 def test_to_utf8_by_setting_returns_ascii_as_utf8
57 def test_to_utf8_by_setting_returns_ascii_as_utf8
58 s1 = "ASCII".force_encoding("UTF-8")
58 s1 = "ASCII".force_encoding("UTF-8")
59 s2 = s1.dup.force_encoding("ISO-8859-1")
59 s2 = s1.dup.force_encoding("ISO-8859-1")
60 str1 = Redmine::CodesetUtil.to_utf8_by_setting(s1)
60 str1 = Redmine::CodesetUtil.to_utf8_by_setting(s1)
61 str2 = Redmine::CodesetUtil.to_utf8_by_setting(s2)
61 str2 = Redmine::CodesetUtil.to_utf8_by_setting(s2)
62 assert_equal s1, str1
62 assert_equal s1, str1
63 assert_equal s1, str2
63 assert_equal s1, str2
64 assert_equal "UTF-8", str1.encoding.to_s
64 assert_equal "UTF-8", str1.encoding.to_s
65 assert_equal "UTF-8", str2.encoding.to_s
65 assert_equal "UTF-8", str2.encoding.to_s
66 end
66 end
67
67
68 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped
68 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped
69 with_settings :repositories_encodings => '' do
69 with_settings :repositories_encodings => '' do
70 # s1 = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
70 # s1 = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
71 s1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
71 s1 = "Texte encod\xe9 en ISO-8859-1.".force_encoding("ASCII-8BIT")
72 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
72 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
73 assert str.valid_encoding?
73 assert str.valid_encoding?
74 assert_equal "UTF-8", str.encoding.to_s
74 assert_equal "UTF-8", str.encoding.to_s
75 assert_equal "Texte encod? en ISO-8859-1.", str
75 assert_equal "Texte encod? en ISO-8859-1.", str
76 end
76 end
77 end
77 end
78
78
79 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped_ja_jis
79 def test_to_utf8_by_setting_invalid_utf8_sequences_should_be_stripped_ja_jis
80 with_settings :repositories_encodings => 'ISO-2022-JP' do
80 with_settings :repositories_encodings => 'ISO-2022-JP' do
81 s1 = "test\xb5\xfetest\xb5\xfe".force_encoding("ASCII-8BIT")
81 s1 = "test\xb5\xfetest\xb5\xfe".force_encoding("ASCII-8BIT")
82 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
82 str = Redmine::CodesetUtil.to_utf8_by_setting(s1)
83 assert str.valid_encoding?
83 assert str.valid_encoding?
84 assert_equal "UTF-8", str.encoding.to_s
84 assert_equal "UTF-8", str.encoding.to_s
85 assert_equal "test??test??", str
85 assert_equal "test??test??", str
86 end
86 end
87 end
87 end
88 end
88 end
@@ -1,104 +1,104
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../test_helper', __FILE__)
19
19
20 class PdfTest < ActiveSupport::TestCase
20 class PdfTest < ActiveSupport::TestCase
21 fixtures :users, :projects, :roles, :members, :member_roles,
21 fixtures :users, :projects, :roles, :members, :member_roles,
22 :enabled_modules, :issues, :trackers, :attachments
22 :enabled_modules, :issues, :trackers, :attachments
23
23
24 def test_fix_text_encoding_nil
24 def test_fix_text_encoding_nil
25 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "UTF-8")
25 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "UTF-8")
26 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "ISO-8859-1")
26 assert_equal '', Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(nil, "ISO-8859-1")
27 end
27 end
28
28
29 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
29 def test_rdm_pdf_iconv_cannot_convert_ja_cp932
30 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
30 utf8_txt_1 = "\xe7\x8b\x80\xe6\x85\x8b"
31 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
31 utf8_txt_2 = "\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
32 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
32 utf8_txt_3 = "\xe7\x8b\x80\xe7\x8b\x80\xe6\x85\x8b\xe7\x8b\x80"
33 ["CP932", "SJIS"].each do |encoding|
33 ["CP932", "SJIS"].each do |encoding|
34 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
34 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_1, encoding)
35 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
35 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_2, encoding)
36 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
36 txt_3 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(utf8_txt_3, encoding)
37 assert_equal "?\x91\xd4".force_encoding("ASCII-8BIT"), txt_1
37 assert_equal "?\x91\xd4".force_encoding("ASCII-8BIT"), txt_1
38 assert_equal "?\x91\xd4?".force_encoding("ASCII-8BIT"), txt_2
38 assert_equal "?\x91\xd4?".force_encoding("ASCII-8BIT"), txt_2
39 assert_equal "??\x91\xd4?".force_encoding("ASCII-8BIT"), txt_3
39 assert_equal "??\x91\xd4?".force_encoding("ASCII-8BIT"), txt_3
40 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
40 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
41 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
41 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
42 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
42 assert_equal "ASCII-8BIT", txt_3.encoding.to_s
43 end
43 end
44 end
44 end
45
45
46 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
46 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_en
47 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
47 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
48 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
48 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
49 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
49 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, 'UTF-8')
50 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
50 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, 'UTF-8')
51 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
51 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
52 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
52 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
53 assert_equal "Texte encod? en ISO-8859-1", txt_1
53 assert_equal "Texte encod? en ISO-8859-1", txt_1
54 assert_equal "?a?b?c?d?e test", txt_2
54 assert_equal "?a?b?c?d?e test", txt_2
55 end
55 end
56
56
57 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
57 def test_rdm_pdf_iconv_invalid_utf8_should_be_replaced_ja
58 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
58 str1 = "Texte encod\xe9 en ISO-8859-1".force_encoding("UTF-8")
59 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
59 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test".force_encoding("ASCII-8BIT")
60 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
60 encoding = ( RUBY_PLATFORM == 'java' ? "SJIS" : "CP932" )
61 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
61 txt_1 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str1, encoding)
62 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
62 txt_2 = Redmine::Export::PDF::RDMPdfEncoding::rdm_from_utf8(str2, encoding)
63 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
63 assert_equal "ASCII-8BIT", txt_1.encoding.to_s
64 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
64 assert_equal "ASCII-8BIT", txt_2.encoding.to_s
65 assert_equal "Texte encod? en ISO-8859-1", txt_1
65 assert_equal "Texte encod? en ISO-8859-1", txt_1
66 assert_equal "?a?b?c?d?e test", txt_2
66 assert_equal "?a?b?c?d?e test", txt_2
67 end
67 end
68
68
69 def test_attach
69 def test_attach
70 ["CP932", "SJIS"].each do |encoding|
70 ["CP932", "SJIS"].each do |encoding|
71 set_fixtures_attachments_directory
71 set_fixtures_attachments_directory
72
72
73 str2 = "\x83e\x83X\x83g".force_encoding("ASCII-8BIT")
73 str2 = "\x83e\x83X\x83g".force_encoding("ASCII-8BIT")
74
74
75 a1 = Attachment.find(17)
75 a1 = Attachment.find(17)
76 a2 = Attachment.find(19)
76 a2 = Attachment.find(19)
77 User.current = User.find(1)
77 User.current = User.find(1)
78 assert a1.readable?
78 assert a1.readable?
79 assert a1.visible?
79 assert a1.visible?
80 assert a2.readable?
80 assert a2.readable?
81 assert a2.visible?
81 assert a2.visible?
82
82
83 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
83 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
84 assert_not_nil aa1
84 assert_not_nil aa1
85 assert_equal 17, aa1.id
85 assert_equal 17, aa1.id
86
86
87 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
87 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
88 assert_not_nil aa2
88 assert_not_nil aa2
89 assert_equal 19, aa2.id
89 assert_equal 19, aa2.id
90
90
91 User.current = nil
91 User.current = nil
92 assert a1.readable?
92 assert a1.readable?
93 assert (! a1.visible?)
93 assert (! a1.visible?)
94 assert a2.readable?
94 assert a2.readable?
95 assert (! a2.visible?)
95 assert (! a2.visible?)
96 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
96 aa1 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "Testfile.PNG", "UTF-8")
97 assert_equal nil, aa1
97 assert_nil aa1
98 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
98 aa2 = Redmine::Export::PDF::RDMPdfEncoding::attach(Attachment.all, "test#{str2}.png", encoding)
99 assert_equal nil, aa2
99 assert_nil aa2
100
100
101 set_tmp_attachments_directory
101 set_tmp_attachments_directory
102 end
102 end
103 end
103 end
104 end
104 end
@@ -1,191 +1,191
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../test_helper', __FILE__)
19
19
20 class Redmine::MenuManager::MapperTest < ActiveSupport::TestCase
20 class Redmine::MenuManager::MapperTest < ActiveSupport::TestCase
21 test "Mapper#initialize should define a root MenuNode if menu is not present in items" do
21 test "Mapper#initialize should define a root MenuNode if menu is not present in items" do
22 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
22 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
23 node = menu_mapper.menu_items
23 node = menu_mapper.menu_items
24 assert_not_nil node
24 assert_not_nil node
25 assert_equal :root, node.name
25 assert_equal :root, node.name
26 end
26 end
27
27
28 test "Mapper#initialize should use existing MenuNode if present" do
28 test "Mapper#initialize should use existing MenuNode if present" do
29 node = "foo" # just an arbitrary reference
29 node = "foo" # just an arbitrary reference
30 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {:test_menu => node})
30 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {:test_menu => node})
31 assert_equal node, menu_mapper.menu_items
31 assert_equal node, menu_mapper.menu_items
32 end
32 end
33
33
34 def test_push_onto_root
34 def test_push_onto_root
35 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
35 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
36 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
36 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
37
37
38 menu_mapper.exists?(:test_overview)
38 menu_mapper.exists?(:test_overview)
39 end
39 end
40
40
41 def test_push_onto_parent
41 def test_push_onto_parent
42 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
42 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
43 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
43 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
44 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
44 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
45
45
46 assert menu_mapper.exists?(:test_child)
46 assert menu_mapper.exists?(:test_child)
47 assert_equal :test_child, menu_mapper.find(:test_child).name
47 assert_equal :test_child, menu_mapper.find(:test_child).name
48 end
48 end
49
49
50 def test_push_onto_grandparent
50 def test_push_onto_grandparent
51 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
51 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
52 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
52 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
53 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
53 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
54 menu_mapper.push :test_grandchild, { :controller => 'projects', :action => 'show'}, {:parent => :test_child}
54 menu_mapper.push :test_grandchild, { :controller => 'projects', :action => 'show'}, {:parent => :test_child}
55
55
56 assert menu_mapper.exists?(:test_grandchild)
56 assert menu_mapper.exists?(:test_grandchild)
57 grandchild = menu_mapper.find(:test_grandchild)
57 grandchild = menu_mapper.find(:test_grandchild)
58 assert_equal :test_grandchild, grandchild.name
58 assert_equal :test_grandchild, grandchild.name
59 assert_equal :test_child, grandchild.parent.name
59 assert_equal :test_child, grandchild.parent.name
60 end
60 end
61
61
62 def test_push_first
62 def test_push_first
63 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
63 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
64 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
64 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
65 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
65 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
66 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
66 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
67 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
67 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
68 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {:first => true}
68 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {:first => true}
69
69
70 root = menu_mapper.find(:root)
70 root = menu_mapper.find(:root)
71 assert_equal 5, root.children.size
71 assert_equal 5, root.children.size
72 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
72 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
73 assert_not_nil root.children[position]
73 assert_not_nil root.children[position]
74 assert_equal name, root.children[position].name
74 assert_equal name, root.children[position].name
75 end
75 end
76
76
77 end
77 end
78
78
79 def test_push_before
79 def test_push_before
80 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
80 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
81 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
81 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
82 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
82 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
83 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
83 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
84 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
84 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
85 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {:before => :test_fourth}
85 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {:before => :test_fourth}
86
86
87 root = menu_mapper.find(:root)
87 root = menu_mapper.find(:root)
88 assert_equal 5, root.children.size
88 assert_equal 5, root.children.size
89 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
89 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
90 assert_not_nil root.children[position]
90 assert_not_nil root.children[position]
91 assert_equal name, root.children[position].name
91 assert_equal name, root.children[position].name
92 end
92 end
93
93
94 end
94 end
95
95
96 def test_push_after
96 def test_push_after
97 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
97 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
98 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
98 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
99 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
99 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
100 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
100 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
101 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
101 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
102 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {:after => :test_third}
102 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {:after => :test_third}
103
103
104 root = menu_mapper.find(:root)
104 root = menu_mapper.find(:root)
105 assert_equal 5, root.children.size
105 assert_equal 5, root.children.size
106 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
106 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
107 assert_not_nil root.children[position]
107 assert_not_nil root.children[position]
108 assert_equal name, root.children[position].name
108 assert_equal name, root.children[position].name
109 end
109 end
110
110
111 end
111 end
112
112
113 def test_push_last
113 def test_push_last
114 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
114 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
115 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
115 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
116 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
116 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
117 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
117 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
118 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {:last => true}
118 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {:last => true}
119 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
119 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
120
120
121 root = menu_mapper.find(:root)
121 root = menu_mapper.find(:root)
122 assert_equal 5, root.children.size
122 assert_equal 5, root.children.size
123 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
123 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
124 assert_not_nil root.children[position]
124 assert_not_nil root.children[position]
125 assert_equal name, root.children[position].name
125 assert_equal name, root.children[position].name
126 end
126 end
127
127
128 end
128 end
129
129
130 def test_exists_for_child_node
130 def test_exists_for_child_node
131 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
131 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
132 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
132 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
133 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview }
133 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview }
134
134
135 assert menu_mapper.exists?(:test_child)
135 assert menu_mapper.exists?(:test_child)
136 end
136 end
137
137
138 def test_exists_for_invalid_node
138 def test_exists_for_invalid_node
139 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
139 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
140 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
140 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
141
141
142 assert !menu_mapper.exists?(:nothing)
142 assert !menu_mapper.exists?(:nothing)
143 end
143 end
144
144
145 def test_find
145 def test_find
146 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
146 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
147 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
147 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
148
148
149 item = menu_mapper.find(:test_overview)
149 item = menu_mapper.find(:test_overview)
150 assert_equal :test_overview, item.name
150 assert_equal :test_overview, item.name
151 assert_equal({:controller => 'projects', :action => 'show'}, item.url)
151 assert_equal({:controller => 'projects', :action => 'show'}, item.url)
152 end
152 end
153
153
154 def test_find_missing
154 def test_find_missing
155 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
155 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
156 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
156 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
157
157
158 item = menu_mapper.find(:nothing)
158 item = menu_mapper.find(:nothing)
159 assert_equal nil, item
159 assert_nil item
160 end
160 end
161
161
162 def test_delete
162 def test_delete
163 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
163 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
164 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
164 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
165 assert_not_nil menu_mapper.delete(:test_overview)
165 assert_not_nil menu_mapper.delete(:test_overview)
166
166
167 assert_nil menu_mapper.find(:test_overview)
167 assert_nil menu_mapper.find(:test_overview)
168 end
168 end
169
169
170 def test_delete_missing
170 def test_delete_missing
171 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
171 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
172 assert_nil menu_mapper.delete(:test_missing)
172 assert_nil menu_mapper.delete(:test_missing)
173 end
173 end
174
174
175 test 'deleting all items' do
175 test 'deleting all items' do
176 # Exposed by deleting :last items
176 # Exposed by deleting :last items
177 Redmine::MenuManager.map :test_menu do |menu|
177 Redmine::MenuManager.map :test_menu do |menu|
178 menu.push :not_last, Redmine::Info.help_url
178 menu.push :not_last, Redmine::Info.help_url
179 menu.push :administration, { :controller => 'projects', :action => 'show'}, {:last => true}
179 menu.push :administration, { :controller => 'projects', :action => 'show'}, {:last => true}
180 menu.push :help, Redmine::Info.help_url, :last => true
180 menu.push :help, Redmine::Info.help_url, :last => true
181 end
181 end
182
182
183 assert_nothing_raised do
183 assert_nothing_raised do
184 Redmine::MenuManager.map :test_menu do |menu|
184 Redmine::MenuManager.map :test_menu do |menu|
185 menu.delete(:administration)
185 menu.delete(:administration)
186 menu.delete(:help)
186 menu.delete(:help)
187 menu.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
187 menu.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
188 end
188 end
189 end
189 end
190 end
190 end
191 end
191 end
@@ -1,597 +1,597
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../../test_helper', __FILE__)
19
19
20 class GitAdapterTest < ActiveSupport::TestCase
20 class GitAdapterTest < ActiveSupport::TestCase
21 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
21 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
22
22
23 FELIX_HEX = "Felix Sch\xC3\xA4fer"
23 FELIX_HEX = "Felix Sch\xC3\xA4fer"
24 CHAR_1_HEX = "\xc3\x9c"
24 CHAR_1_HEX = "\xc3\x9c"
25
25
26 ## Git, Mercurial and CVS path encodings are binary.
26 ## Git, Mercurial and CVS path encodings are binary.
27 ## Subversion supports URL encoding for path.
27 ## Subversion supports URL encoding for path.
28 ## Redmine Mercurial adapter and extension use URL encoding.
28 ## Redmine Mercurial adapter and extension use URL encoding.
29 ## Git accepts only binary path in command line parameter.
29 ## Git accepts only binary path in command line parameter.
30 ## So, there is no way to use binary command line parameter in JRuby.
30 ## So, there is no way to use binary command line parameter in JRuby.
31 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
31 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
32 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
32 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
33
33
34 if File.directory?(REPOSITORY_PATH)
34 if File.directory?(REPOSITORY_PATH)
35 ## Ruby uses ANSI api to fork a process on Windows.
35 ## Ruby uses ANSI api to fork a process on Windows.
36 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
36 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
37 ## and these are incompatible with ASCII.
37 ## and these are incompatible with ASCII.
38 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
38 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
39 ## http://code.google.com/p/msysgit/issues/detail?id=80
39 ## http://code.google.com/p/msysgit/issues/detail?id=80
40 ## So, Latin-1 path tests fail on Japanese Windows
40 ## So, Latin-1 path tests fail on Japanese Windows
41 WINDOWS_PASS = (Redmine::Platform.mswin? &&
41 WINDOWS_PASS = (Redmine::Platform.mswin? &&
42 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
42 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
43 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
43 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
44
44
45 def setup
45 def setup
46 adapter_class = Redmine::Scm::Adapters::GitAdapter
46 adapter_class = Redmine::Scm::Adapters::GitAdapter
47 assert adapter_class
47 assert adapter_class
48 assert adapter_class.client_command
48 assert adapter_class.client_command
49 assert_equal true, adapter_class.client_available
49 assert_equal true, adapter_class.client_available
50 assert_equal true, adapter_class.client_version_above?([1])
50 assert_equal true, adapter_class.client_version_above?([1])
51 assert_equal true, adapter_class.client_version_above?([1, 0])
51 assert_equal true, adapter_class.client_version_above?([1, 0])
52
52
53 @adapter = Redmine::Scm::Adapters::GitAdapter.new(
53 @adapter = Redmine::Scm::Adapters::GitAdapter.new(
54 REPOSITORY_PATH,
54 REPOSITORY_PATH,
55 nil,
55 nil,
56 nil,
56 nil,
57 nil,
57 nil,
58 'ISO-8859-1'
58 'ISO-8859-1'
59 )
59 )
60 assert @adapter
60 assert @adapter
61 @char_1 = CHAR_1_HEX.dup.force_encoding('UTF-8')
61 @char_1 = CHAR_1_HEX.dup.force_encoding('UTF-8')
62 @str_felix_hex = FELIX_HEX.dup.force_encoding('ASCII-8BIT')
62 @str_felix_hex = FELIX_HEX.dup.force_encoding('ASCII-8BIT')
63 end
63 end
64
64
65 def test_scm_version
65 def test_scm_version
66 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
66 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
67 "1.6.1\n1.7\n1.8" => [1,6,1],
67 "1.6.1\n1.7\n1.8" => [1,6,1],
68 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
68 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
69 to_test.each do |s, v|
69 to_test.each do |s, v|
70 test_scm_version_for(s, v)
70 test_scm_version_for(s, v)
71 end
71 end
72 end
72 end
73
73
74 def test_branches
74 def test_branches
75 brs = []
75 brs = []
76 @adapter.branches.each do |b|
76 @adapter.branches.each do |b|
77 brs << b
77 brs << b
78 end
78 end
79 assert_equal 6, brs.length
79 assert_equal 6, brs.length
80 br_issue_8857 = brs[0]
80 br_issue_8857 = brs[0]
81 assert_equal 'issue-8857', br_issue_8857.to_s
81 assert_equal 'issue-8857', br_issue_8857.to_s
82 assert_equal '2a682156a3b6e77a8bf9cd4590e8db757f3c6c78', br_issue_8857.revision
82 assert_equal '2a682156a3b6e77a8bf9cd4590e8db757f3c6c78', br_issue_8857.revision
83 assert_equal br_issue_8857.scmid, br_issue_8857.revision
83 assert_equal br_issue_8857.scmid, br_issue_8857.revision
84 assert_equal false, br_issue_8857.is_default
84 assert_equal false, br_issue_8857.is_default
85 br_latin_1_path = brs[1]
85 br_latin_1_path = brs[1]
86 assert_equal 'latin-1-path-encoding', br_latin_1_path.to_s
86 assert_equal 'latin-1-path-encoding', br_latin_1_path.to_s
87 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', br_latin_1_path.revision
87 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', br_latin_1_path.revision
88 assert_equal br_latin_1_path.scmid, br_latin_1_path.revision
88 assert_equal br_latin_1_path.scmid, br_latin_1_path.revision
89 assert_equal false, br_latin_1_path.is_default
89 assert_equal false, br_latin_1_path.is_default
90 br_master = brs[2]
90 br_master = brs[2]
91 assert_equal 'master', br_master.to_s
91 assert_equal 'master', br_master.to_s
92 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master.revision
92 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master.revision
93 assert_equal br_master.scmid, br_master.revision
93 assert_equal br_master.scmid, br_master.revision
94 assert_equal false, br_master.is_default
94 assert_equal false, br_master.is_default
95 br_master_20120212 = brs[3]
95 br_master_20120212 = brs[3]
96 assert_equal 'master-20120212', br_master_20120212.to_s
96 assert_equal 'master-20120212', br_master_20120212.to_s
97 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master_20120212.revision
97 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', br_master_20120212.revision
98 assert_equal br_master_20120212.scmid, br_master_20120212.revision
98 assert_equal br_master_20120212.scmid, br_master_20120212.revision
99 assert_equal true, br_master_20120212.is_default
99 assert_equal true, br_master_20120212.is_default
100 br_latin_1 = brs[-2]
100 br_latin_1 = brs[-2]
101 assert_equal 'test-latin-1', br_latin_1.to_s
101 assert_equal 'test-latin-1', br_latin_1.to_s
102 assert_equal '67e7792ce20ccae2e4bb73eed09bb397819c8834', br_latin_1.revision
102 assert_equal '67e7792ce20ccae2e4bb73eed09bb397819c8834', br_latin_1.revision
103 assert_equal br_latin_1.scmid, br_latin_1.revision
103 assert_equal br_latin_1.scmid, br_latin_1.revision
104 assert_equal false, br_latin_1.is_default
104 assert_equal false, br_latin_1.is_default
105 br_test = brs[-1]
105 br_test = brs[-1]
106 assert_equal 'test_branch', br_test.to_s
106 assert_equal 'test_branch', br_test.to_s
107 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', br_test.revision
107 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', br_test.revision
108 assert_equal br_test.scmid, br_test.revision
108 assert_equal br_test.scmid, br_test.revision
109 assert_equal false, br_test.is_default
109 assert_equal false, br_test.is_default
110 end
110 end
111
111
112 def test_default_branch
112 def test_default_branch
113 assert_equal 'master-20120212', @adapter.default_branch
113 assert_equal 'master-20120212', @adapter.default_branch
114 end
114 end
115
115
116 def test_tags
116 def test_tags
117 assert_equal [
117 assert_equal [
118 "tag00.lightweight",
118 "tag00.lightweight",
119 "tag01.annotated",
119 "tag01.annotated",
120 ], @adapter.tags
120 ], @adapter.tags
121 end
121 end
122
122
123 def test_revisions_master_all
123 def test_revisions_master_all
124 revs1 = []
124 revs1 = []
125 @adapter.revisions('', nil, "master",{}) do |rev|
125 @adapter.revisions('', nil, "master",{}) do |rev|
126 revs1 << rev
126 revs1 << rev
127 end
127 end
128 assert_equal 15, revs1.length
128 assert_equal 15, revs1.length
129 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 0].identifier
129 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 0].identifier
130 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
130 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
131
131
132 revs2 = []
132 revs2 = []
133 @adapter.revisions('', nil, "master",
133 @adapter.revisions('', nil, "master",
134 {:reverse => true}) do |rev|
134 {:reverse => true}) do |rev|
135 revs2 << rev
135 revs2 << rev
136 end
136 end
137 assert_equal 15, revs2.length
137 assert_equal 15, revs2.length
138 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
138 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
139 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
139 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
140 end
140 end
141
141
142 def test_revisions_master_merged_rev
142 def test_revisions_master_merged_rev
143 revs1 = []
143 revs1 = []
144 @adapter.revisions('',
144 @adapter.revisions('',
145 "713f4944648826f558cf548222f813dabe7cbb04",
145 "713f4944648826f558cf548222f813dabe7cbb04",
146 "master",
146 "master",
147 {:reverse => true}) do |rev|
147 {:reverse => true}) do |rev|
148 revs1 << rev
148 revs1 << rev
149 end
149 end
150 assert_equal 8, revs1.length
150 assert_equal 8, revs1.length
151 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', revs1[ 0].identifier
151 assert_equal 'fba357b886984ee71185ad2065e65fc0417d9b92', revs1[ 0].identifier
152 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 1].identifier
152 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 1].identifier
153 # 4a07fe31b is not a child of 713f49446
153 # 4a07fe31b is not a child of 713f49446
154 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 2].identifier
154 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 2].identifier
155 # Merged revision
155 # Merged revision
156 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 3].identifier
156 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 3].identifier
157 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
157 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
158
158
159 revs2 = []
159 revs2 = []
160 @adapter.revisions('',
160 @adapter.revisions('',
161 "fba357b886984ee71185ad2065e65fc0417d9b92",
161 "fba357b886984ee71185ad2065e65fc0417d9b92",
162 "master",
162 "master",
163 {:reverse => true}) do |rev|
163 {:reverse => true}) do |rev|
164 revs2 << rev
164 revs2 << rev
165 end
165 end
166 assert_equal 7, revs2.length
166 assert_equal 7, revs2.length
167 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs2[ 0].identifier
167 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs2[ 0].identifier
168 # 4a07fe31b is not a child of fba357b8869
168 # 4a07fe31b is not a child of fba357b8869
169 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs2[ 1].identifier
169 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs2[ 1].identifier
170 # Merged revision
170 # Merged revision
171 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs2[ 2].identifier
171 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs2[ 2].identifier
172 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
172 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs2[-1].identifier
173 end
173 end
174
174
175 def test_revisions_branch_latin_1_path_encoding_all
175 def test_revisions_branch_latin_1_path_encoding_all
176 revs1 = []
176 revs1 = []
177 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
177 @adapter.revisions('', nil, "latin-1-path-encoding",{}) do |rev|
178 revs1 << rev
178 revs1 << rev
179 end
179 end
180 assert_equal 8, revs1.length
180 assert_equal 8, revs1.length
181 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[ 0].identifier
181 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[ 0].identifier
182 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
182 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[-1].identifier
183
183
184 revs2 = []
184 revs2 = []
185 @adapter.revisions('', nil, "latin-1-path-encoding",
185 @adapter.revisions('', nil, "latin-1-path-encoding",
186 {:reverse => true}) do |rev|
186 {:reverse => true}) do |rev|
187 revs2 << rev
187 revs2 << rev
188 end
188 end
189 assert_equal 8, revs2.length
189 assert_equal 8, revs2.length
190 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
190 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
191 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
191 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs2[ 0].identifier
192 end
192 end
193
193
194 def test_revisions_branch_latin_1_path_encoding_with_rev
194 def test_revisions_branch_latin_1_path_encoding_with_rev
195 revs1 = []
195 revs1 = []
196 @adapter.revisions('',
196 @adapter.revisions('',
197 '7234cb2750b63f47bff735edc50a1c0a433c2518',
197 '7234cb2750b63f47bff735edc50a1c0a433c2518',
198 "latin-1-path-encoding",
198 "latin-1-path-encoding",
199 {:reverse => true}) do |rev|
199 {:reverse => true}) do |rev|
200 revs1 << rev
200 revs1 << rev
201 end
201 end
202 assert_equal 7, revs1.length
202 assert_equal 7, revs1.length
203 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
203 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 0].identifier
204 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
204 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
205
205
206 revs2 = []
206 revs2 = []
207 @adapter.revisions('',
207 @adapter.revisions('',
208 '57ca437c0acbbcb749821fdf3726a1367056d364',
208 '57ca437c0acbbcb749821fdf3726a1367056d364',
209 "latin-1-path-encoding",
209 "latin-1-path-encoding",
210 {:reverse => true}) do |rev|
210 {:reverse => true}) do |rev|
211 revs2 << rev
211 revs2 << rev
212 end
212 end
213 assert_equal 3, revs2.length
213 assert_equal 3, revs2.length
214 assert_equal '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', revs2[ 0].identifier
214 assert_equal '4fc55c43bf3d3dc2efb66145365ddc17639ce81e', revs2[ 0].identifier
215 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
215 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs2[-1].identifier
216 end
216 end
217
217
218 def test_revisions_invalid_rev
218 def test_revisions_invalid_rev
219 assert_equal [], @adapter.revisions('', '1234abcd', "master")
219 assert_equal [], @adapter.revisions('', '1234abcd', "master")
220 assert_raise Redmine::Scm::Adapters::CommandFailed do
220 assert_raise Redmine::Scm::Adapters::CommandFailed do
221 revs1 = []
221 revs1 = []
222 @adapter.revisions('',
222 @adapter.revisions('',
223 '1234abcd',
223 '1234abcd',
224 "master",
224 "master",
225 {:reverse => true}) do |rev|
225 {:reverse => true}) do |rev|
226 revs1 << rev
226 revs1 << rev
227 end
227 end
228 end
228 end
229 end
229 end
230
230
231 def test_revisions_includes_master_two_revs
231 def test_revisions_includes_master_two_revs
232 revs1 = []
232 revs1 = []
233 @adapter.revisions('', nil, nil,
233 @adapter.revisions('', nil, nil,
234 {:reverse => true,
234 {:reverse => true,
235 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
235 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
236 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
236 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43']}) do |rev|
237 revs1 << rev
237 revs1 << rev
238 end
238 end
239 assert_equal 2, revs1.length
239 assert_equal 2, revs1.length
240 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
240 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
241 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
241 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
242 end
242 end
243
243
244 def test_revisions_includes_master_two_revs_from_origin
244 def test_revisions_includes_master_two_revs_from_origin
245 revs1 = []
245 revs1 = []
246 @adapter.revisions('', nil, nil,
246 @adapter.revisions('', nil, nil,
247 {:reverse => true,
247 {:reverse => true,
248 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
248 :includes => ['899a15dba03a3b350b89c3f537e4bbe02a03cdc9'],
249 :excludes => []}) do |rev|
249 :excludes => []}) do |rev|
250 revs1 << rev
250 revs1 << rev
251 end
251 end
252 assert_equal 2, revs1.length
252 assert_equal 2, revs1.length
253 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
253 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518', revs1[ 0].identifier
254 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
254 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', revs1[ 1].identifier
255 end
255 end
256
256
257 def test_revisions_includes_merged_revs
257 def test_revisions_includes_merged_revs
258 revs1 = []
258 revs1 = []
259 @adapter.revisions('', nil, nil,
259 @adapter.revisions('', nil, nil,
260 {:reverse => true,
260 {:reverse => true,
261 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
261 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
262 :excludes => ['fba357b886984ee71185ad2065e65fc0417d9b92']}) do |rev|
262 :excludes => ['fba357b886984ee71185ad2065e65fc0417d9b92']}) do |rev|
263 revs1 << rev
263 revs1 << rev
264 end
264 end
265 assert_equal 7, revs1.length
265 assert_equal 7, revs1.length
266 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 0].identifier
266 assert_equal '7e61ac704deecde634b51e59daa8110435dcb3da', revs1[ 0].identifier
267 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 1].identifier
267 assert_equal '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8', revs1[ 1].identifier
268 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 2].identifier
268 assert_equal '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf', revs1[ 2].identifier
269 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
269 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[-1].identifier
270 end
270 end
271
271
272 def test_revisions_includes_two_heads
272 def test_revisions_includes_two_heads
273 revs1 = []
273 revs1 = []
274 @adapter.revisions('', nil, nil,
274 @adapter.revisions('', nil, nil,
275 {:reverse => true,
275 {:reverse => true,
276 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
276 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
277 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127'],
277 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127'],
278 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43',
278 :excludes => ['4f26664364207fa8b1af9f8722647ab2d4ac5d43',
279 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e']}) do |rev|
279 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e']}) do |rev|
280 revs1 << rev
280 revs1 << rev
281 end
281 end
282 assert_equal 4, revs1.length
282 assert_equal 4, revs1.length
283 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
283 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
284 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
284 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
285 assert_equal '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', revs1[-2].identifier
285 assert_equal '64f1f3e89ad1cb57976ff0ad99a107012ba3481d', revs1[-2].identifier
286 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
286 assert_equal '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127', revs1[-1].identifier
287 end
287 end
288
288
289 def test_revisions_disjointed_histories_revisions
289 def test_revisions_disjointed_histories_revisions
290 revs1 = []
290 revs1 = []
291 @adapter.revisions('', nil, nil,
291 @adapter.revisions('', nil, nil,
292 {:reverse => true,
292 {:reverse => true,
293 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
293 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c',
294 '92397af84d22f27389c822848ecd5b463c181583'],
294 '92397af84d22f27389c822848ecd5b463c181583'],
295 :excludes => ['95488a44bc25f7d1f97d775a31359539ff333a63',
295 :excludes => ['95488a44bc25f7d1f97d775a31359539ff333a63',
296 '4f26664364207fa8b1af9f8722647ab2d4ac5d43'] }) do |rev|
296 '4f26664364207fa8b1af9f8722647ab2d4ac5d43'] }) do |rev|
297 revs1 << rev
297 revs1 << rev
298 end
298 end
299 assert_equal 4, revs1.length
299 assert_equal 4, revs1.length
300 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
300 assert_equal 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b', revs1[ 0].identifier
301 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
301 assert_equal '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', revs1[ 1].identifier
302 assert_equal 'bc201c95999c4f10d018b0aa03b541cd6a2ff0ee', revs1[-2].identifier
302 assert_equal 'bc201c95999c4f10d018b0aa03b541cd6a2ff0ee', revs1[-2].identifier
303 assert_equal '92397af84d22f27389c822848ecd5b463c181583', revs1[-1].identifier
303 assert_equal '92397af84d22f27389c822848ecd5b463c181583', revs1[-1].identifier
304 end
304 end
305
305
306 def test_revisions_invalid_rev_excludes
306 def test_revisions_invalid_rev_excludes
307 assert_equal [],
307 assert_equal [],
308 @adapter.revisions('', nil, nil,
308 @adapter.revisions('', nil, nil,
309 {:reverse => true,
309 {:reverse => true,
310 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
310 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
311 :excludes => ['0123abcd4567']})
311 :excludes => ['0123abcd4567']})
312 assert_raise Redmine::Scm::Adapters::CommandFailed do
312 assert_raise Redmine::Scm::Adapters::CommandFailed do
313 revs1 = []
313 revs1 = []
314 @adapter.revisions('', nil, nil,
314 @adapter.revisions('', nil, nil,
315 {:reverse => true,
315 {:reverse => true,
316 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
316 :includes => ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c'],
317 :excludes => ['0123abcd4567']}) do |rev|
317 :excludes => ['0123abcd4567']}) do |rev|
318 revs1 << rev
318 revs1 << rev
319 end
319 end
320 end
320 end
321 end
321 end
322
322
323 def test_getting_revisions_with_spaces_in_filename
323 def test_getting_revisions_with_spaces_in_filename
324 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
324 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
325 nil, "master").length
325 nil, "master").length
326 end
326 end
327
327
328 def test_parents
328 def test_parents
329 revs1 = []
329 revs1 = []
330 @adapter.revisions('',
330 @adapter.revisions('',
331 nil,
331 nil,
332 "master",
332 "master",
333 {:reverse => true}) do |rev|
333 {:reverse => true}) do |rev|
334 revs1 << rev
334 revs1 << rev
335 end
335 end
336 assert_equal 15, revs1.length
336 assert_equal 15, revs1.length
337 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
337 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
338 revs1[0].identifier
338 revs1[0].identifier
339 assert_equal nil, revs1[0].parents
339 assert_nil revs1[0].parents
340 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
340 assert_equal "899a15dba03a3b350b89c3f537e4bbe02a03cdc9",
341 revs1[1].identifier
341 revs1[1].identifier
342 assert_equal 1, revs1[1].parents.length
342 assert_equal 1, revs1[1].parents.length
343 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
343 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
344 revs1[1].parents[0]
344 revs1[1].parents[0]
345 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
345 assert_equal "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
346 revs1[10].identifier
346 revs1[10].identifier
347 assert_equal 2, revs1[10].parents.length
347 assert_equal 2, revs1[10].parents.length
348 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
348 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
349 revs1[10].parents[0]
349 revs1[10].parents[0]
350 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
350 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da",
351 revs1[10].parents[1]
351 revs1[10].parents[1]
352 end
352 end
353
353
354 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
354 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
355 assert_equal " filename with a leading space.txt ",
355 assert_equal " filename with a leading space.txt ",
356 @adapter.revisions(" filename with a leading space.txt ",
356 @adapter.revisions(" filename with a leading space.txt ",
357 nil, "master")[0].paths[0][:path]
357 nil, "master")[0].paths[0][:path]
358 end
358 end
359
359
360 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
360 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
361 assert_equal " filename with a leading space.txt ",
361 assert_equal " filename with a leading space.txt ",
362 @adapter.entries('',
362 @adapter.entries('',
363 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
363 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
364 end
364 end
365
365
366 def test_annotate
366 def test_annotate
367 annotate = @adapter.annotate('sources/watchers_controller.rb')
367 annotate = @adapter.annotate('sources/watchers_controller.rb')
368 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
368 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
369 assert_equal 41, annotate.lines.size
369 assert_equal 41, annotate.lines.size
370 assert_equal "# This program is free software; you can redistribute it and/or",
370 assert_equal "# This program is free software; you can redistribute it and/or",
371 annotate.lines[4].strip
371 annotate.lines[4].strip
372 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
372 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
373 annotate.revisions[4].identifier
373 annotate.revisions[4].identifier
374 assert_equal "jsmith", annotate.revisions[4].author
374 assert_equal "jsmith", annotate.revisions[4].author
375 end
375 end
376
376
377 def test_annotate_moved_file
377 def test_annotate_moved_file
378 annotate = @adapter.annotate('renamed_test.txt')
378 annotate = @adapter.annotate('renamed_test.txt')
379 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
379 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
380 assert_equal 2, annotate.lines.size
380 assert_equal 2, annotate.lines.size
381 end
381 end
382
382
383 def test_last_rev
383 def test_last_rev
384 last_rev = @adapter.lastrev("README",
384 last_rev = @adapter.lastrev("README",
385 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
385 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
386 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
386 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
387 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
387 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
388 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
388 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
389 assert_equal Time.gm(2009, 6, 24, 5, 27, 38), last_rev.time
389 assert_equal Time.gm(2009, 6, 24, 5, 27, 38), last_rev.time
390 end
390 end
391
391
392 def test_last_rev_with_spaces_in_filename
392 def test_last_rev_with_spaces_in_filename
393 last_rev = @adapter.lastrev("filemane with spaces.txt",
393 last_rev = @adapter.lastrev("filemane with spaces.txt",
394 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
394 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
395 last_rev_author = last_rev.author
395 last_rev_author = last_rev.author
396 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
396 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
397 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
397 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
398 assert_equal "#{@str_felix_hex} <felix@fachschaften.org>",
398 assert_equal "#{@str_felix_hex} <felix@fachschaften.org>",
399 last_rev.author
399 last_rev.author
400 assert_equal Time.gm(2010, 9, 18, 19, 59, 46), last_rev.time
400 assert_equal Time.gm(2010, 9, 18, 19, 59, 46), last_rev.time
401 end
401 end
402
402
403 def test_latin_1_path
403 def test_latin_1_path
404 if WINDOWS_PASS
404 if WINDOWS_PASS
405 puts WINDOWS_SKIP_STR
405 puts WINDOWS_SKIP_STR
406 elsif JRUBY_SKIP
406 elsif JRUBY_SKIP
407 puts JRUBY_SKIP_STR
407 puts JRUBY_SKIP_STR
408 else
408 else
409 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
409 p2 = "latin-1-dir/test-#{@char_1}-2.txt"
410 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
410 ['4fc55c43bf3d3dc2efb66145365ddc17639ce81e', '4fc55c43bf3'].each do |r1|
411 assert @adapter.diff(p2, r1)
411 assert @adapter.diff(p2, r1)
412 assert @adapter.cat(p2, r1)
412 assert @adapter.cat(p2, r1)
413 assert_equal 1, @adapter.annotate(p2, r1).lines.length
413 assert_equal 1, @adapter.annotate(p2, r1).lines.length
414 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
414 ['64f1f3e89ad1cb57976ff0ad99a107012ba3481d', '64f1f3e89ad1cb5797'].each do |r2|
415 assert @adapter.diff(p2, r1, r2)
415 assert @adapter.diff(p2, r1, r2)
416 end
416 end
417 end
417 end
418 end
418 end
419 end
419 end
420
420
421 def test_latin_1_user_annotate
421 def test_latin_1_user_annotate
422 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
422 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
423 annotate = @adapter.annotate(" filename with a leading space.txt ", r1)
423 annotate = @adapter.annotate(" filename with a leading space.txt ", r1)
424 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
424 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
425 assert_equal 1, annotate.lines.size
425 assert_equal 1, annotate.lines.size
426 assert_equal "And this is a file with a leading and trailing space...",
426 assert_equal "And this is a file with a leading and trailing space...",
427 annotate.lines[0].strip
427 annotate.lines[0].strip
428 assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
428 assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
429 annotate.revisions[0].identifier
429 annotate.revisions[0].identifier
430 assert_equal @str_felix_hex, annotate.revisions[0].author
430 assert_equal @str_felix_hex, annotate.revisions[0].author
431 end
431 end
432 end
432 end
433
433
434 def test_entries_tag
434 def test_entries_tag
435 entries1 = @adapter.entries(nil, 'tag01.annotated',
435 entries1 = @adapter.entries(nil, 'tag01.annotated',
436 options = {:report_last_commit => true})
436 options = {:report_last_commit => true})
437 assert entries1
437 assert entries1
438 assert_equal 3, entries1.size
438 assert_equal 3, entries1.size
439 assert_equal 'sources', entries1[1].name
439 assert_equal 'sources', entries1[1].name
440 assert_equal 'sources', entries1[1].path
440 assert_equal 'sources', entries1[1].path
441 assert_equal 'dir', entries1[1].kind
441 assert_equal 'dir', entries1[1].kind
442 readme = entries1[2]
442 readme = entries1[2]
443 assert_equal 'README', readme.name
443 assert_equal 'README', readme.name
444 assert_equal 'README', readme.path
444 assert_equal 'README', readme.path
445 assert_equal 'file', readme.kind
445 assert_equal 'file', readme.kind
446 assert_equal 27, readme.size
446 assert_equal 27, readme.size
447 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
447 assert_equal '899a15dba03a3b350b89c3f537e4bbe02a03cdc9', readme.lastrev.identifier
448 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
448 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
449 end
449 end
450
450
451 def test_entries_branch
451 def test_entries_branch
452 entries1 = @adapter.entries(nil, 'test_branch',
452 entries1 = @adapter.entries(nil, 'test_branch',
453 options = {:report_last_commit => true})
453 options = {:report_last_commit => true})
454 assert entries1
454 assert entries1
455 assert_equal 4, entries1.size
455 assert_equal 4, entries1.size
456 assert_equal 'sources', entries1[1].name
456 assert_equal 'sources', entries1[1].name
457 assert_equal 'sources', entries1[1].path
457 assert_equal 'sources', entries1[1].path
458 assert_equal 'dir', entries1[1].kind
458 assert_equal 'dir', entries1[1].kind
459 readme = entries1[2]
459 readme = entries1[2]
460 assert_equal 'README', readme.name
460 assert_equal 'README', readme.name
461 assert_equal 'README', readme.path
461 assert_equal 'README', readme.path
462 assert_equal 'file', readme.kind
462 assert_equal 'file', readme.kind
463 assert_equal 159, readme.size
463 assert_equal 159, readme.size
464 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
464 assert_equal '713f4944648826f558cf548222f813dabe7cbb04', readme.lastrev.identifier
465 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
465 assert_equal Time.gm(2009, 6, 19, 4, 37, 23), readme.lastrev.time
466 end
466 end
467
467
468 def test_entries_wrong_path_encoding
468 def test_entries_wrong_path_encoding
469 adpt = Redmine::Scm::Adapters::GitAdapter.new(
469 adpt = Redmine::Scm::Adapters::GitAdapter.new(
470 REPOSITORY_PATH,
470 REPOSITORY_PATH,
471 nil,
471 nil,
472 nil,
472 nil,
473 nil,
473 nil,
474 'EUC-JP'
474 'EUC-JP'
475 )
475 )
476 entries1 = adpt.entries('latin-1-dir', '64f1f3e8')
476 entries1 = adpt.entries('latin-1-dir', '64f1f3e8')
477 assert entries1
477 assert entries1
478 assert_equal 3, entries1.size
478 assert_equal 3, entries1.size
479 f1 = entries1[1]
479 f1 = entries1[1]
480 assert_equal nil, f1.name
480 assert_nil f1.name
481 assert_equal nil, f1.path
481 assert_nil f1.path
482 assert_equal 'file', f1.kind
482 assert_equal 'file', f1.kind
483 end
483 end
484
484
485 def test_entries_latin_1_files
485 def test_entries_latin_1_files
486 entries1 = @adapter.entries('latin-1-dir', '64f1f3e8')
486 entries1 = @adapter.entries('latin-1-dir', '64f1f3e8')
487 assert entries1
487 assert entries1
488 assert_equal 3, entries1.size
488 assert_equal 3, entries1.size
489 f1 = entries1[1]
489 f1 = entries1[1]
490 assert_equal "test-#{@char_1}-2.txt", f1.name
490 assert_equal "test-#{@char_1}-2.txt", f1.name
491 assert_equal "latin-1-dir/test-#{@char_1}-2.txt", f1.path
491 assert_equal "latin-1-dir/test-#{@char_1}-2.txt", f1.path
492 assert_equal 'file', f1.kind
492 assert_equal 'file', f1.kind
493 end
493 end
494
494
495 def test_entries_latin_1_dir
495 def test_entries_latin_1_dir
496 if WINDOWS_PASS
496 if WINDOWS_PASS
497 puts WINDOWS_SKIP_STR
497 puts WINDOWS_SKIP_STR
498 elsif JRUBY_SKIP
498 elsif JRUBY_SKIP
499 puts JRUBY_SKIP_STR
499 puts JRUBY_SKIP_STR
500 else
500 else
501 entries1 = @adapter.entries("latin-1-dir/test-#{@char_1}-subdir",
501 entries1 = @adapter.entries("latin-1-dir/test-#{@char_1}-subdir",
502 '1ca7f5ed')
502 '1ca7f5ed')
503 assert entries1
503 assert entries1
504 assert_equal 3, entries1.size
504 assert_equal 3, entries1.size
505 f1 = entries1[1]
505 f1 = entries1[1]
506 assert_equal "test-#{@char_1}-2.txt", f1.name
506 assert_equal "test-#{@char_1}-2.txt", f1.name
507 assert_equal "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-2.txt", f1.path
507 assert_equal "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-2.txt", f1.path
508 assert_equal 'file', f1.kind
508 assert_equal 'file', f1.kind
509 end
509 end
510 end
510 end
511
511
512 def test_entry
512 def test_entry
513 entry = @adapter.entry()
513 entry = @adapter.entry()
514 assert_equal "", entry.path
514 assert_equal "", entry.path
515 assert_equal "dir", entry.kind
515 assert_equal "dir", entry.kind
516 entry = @adapter.entry('')
516 entry = @adapter.entry('')
517 assert_equal "", entry.path
517 assert_equal "", entry.path
518 assert_equal "dir", entry.kind
518 assert_equal "dir", entry.kind
519 assert_nil @adapter.entry('invalid')
519 assert_nil @adapter.entry('invalid')
520 assert_nil @adapter.entry('/invalid')
520 assert_nil @adapter.entry('/invalid')
521 assert_nil @adapter.entry('/invalid/')
521 assert_nil @adapter.entry('/invalid/')
522 assert_nil @adapter.entry('invalid/invalid')
522 assert_nil @adapter.entry('invalid/invalid')
523 assert_nil @adapter.entry('invalid/invalid/')
523 assert_nil @adapter.entry('invalid/invalid/')
524 assert_nil @adapter.entry('/invalid/invalid')
524 assert_nil @adapter.entry('/invalid/invalid')
525 assert_nil @adapter.entry('/invalid/invalid/')
525 assert_nil @adapter.entry('/invalid/invalid/')
526 ["README", "/README"].each do |path|
526 ["README", "/README"].each do |path|
527 entry = @adapter.entry(path, '7234cb2750b63f')
527 entry = @adapter.entry(path, '7234cb2750b63f')
528 assert_equal "README", entry.path
528 assert_equal "README", entry.path
529 assert_equal "file", entry.kind
529 assert_equal "file", entry.kind
530 end
530 end
531 ["sources", "/sources", "/sources/"].each do |path|
531 ["sources", "/sources", "/sources/"].each do |path|
532 entry = @adapter.entry(path, '7234cb2750b63f')
532 entry = @adapter.entry(path, '7234cb2750b63f')
533 assert_equal "sources", entry.path
533 assert_equal "sources", entry.path
534 assert_equal "dir", entry.kind
534 assert_equal "dir", entry.kind
535 end
535 end
536 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
536 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
537 entry = @adapter.entry(path, '7234cb2750b63f')
537 entry = @adapter.entry(path, '7234cb2750b63f')
538 assert_equal "sources/watchers_controller.rb", entry.path
538 assert_equal "sources/watchers_controller.rb", entry.path
539 assert_equal "file", entry.kind
539 assert_equal "file", entry.kind
540 end
540 end
541 end
541 end
542
542
543 def test_path_encoding_default_utf8
543 def test_path_encoding_default_utf8
544 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
544 adpt1 = Redmine::Scm::Adapters::GitAdapter.new(
545 REPOSITORY_PATH
545 REPOSITORY_PATH
546 )
546 )
547 assert_equal "UTF-8", adpt1.path_encoding
547 assert_equal "UTF-8", adpt1.path_encoding
548 adpt2 = Redmine::Scm::Adapters::GitAdapter.new(
548 adpt2 = Redmine::Scm::Adapters::GitAdapter.new(
549 REPOSITORY_PATH,
549 REPOSITORY_PATH,
550 nil,
550 nil,
551 nil,
551 nil,
552 nil,
552 nil,
553 ""
553 ""
554 )
554 )
555 assert_equal "UTF-8", adpt2.path_encoding
555 assert_equal "UTF-8", adpt2.path_encoding
556 end
556 end
557
557
558 def test_cat_path_invalid
558 def test_cat_path_invalid
559 assert_nil @adapter.cat('invalid')
559 assert_nil @adapter.cat('invalid')
560 end
560 end
561
561
562 def test_cat_revision_invalid
562 def test_cat_revision_invalid
563 assert @adapter.cat('README')
563 assert @adapter.cat('README')
564 assert_nil @adapter.cat('README', '1234abcd5678')
564 assert_nil @adapter.cat('README', '1234abcd5678')
565 end
565 end
566
566
567 def test_diff_path_invalid
567 def test_diff_path_invalid
568 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
568 assert_equal [], @adapter.diff('invalid', '713f4944648826f5')
569 end
569 end
570
570
571 def test_diff_revision_invalid
571 def test_diff_revision_invalid
572 assert_nil @adapter.diff(nil, '1234abcd5678')
572 assert_nil @adapter.diff(nil, '1234abcd5678')
573 assert_nil @adapter.diff(nil, '713f4944648826f5', '1234abcd5678')
573 assert_nil @adapter.diff(nil, '713f4944648826f5', '1234abcd5678')
574 assert_nil @adapter.diff(nil, '1234abcd5678', '713f4944648826f5')
574 assert_nil @adapter.diff(nil, '1234abcd5678', '713f4944648826f5')
575 end
575 end
576
576
577 def test_annotate_path_invalid
577 def test_annotate_path_invalid
578 assert_nil @adapter.annotate('invalid')
578 assert_nil @adapter.annotate('invalid')
579 end
579 end
580
580
581 def test_annotate_revision_invalid
581 def test_annotate_revision_invalid
582 assert @adapter.annotate('README')
582 assert @adapter.annotate('README')
583 assert_nil @adapter.annotate('README', '1234abcd5678')
583 assert_nil @adapter.annotate('README', '1234abcd5678')
584 end
584 end
585
585
586 private
586 private
587
587
588 def test_scm_version_for(scm_command_version, version)
588 def test_scm_version_for(scm_command_version, version)
589 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
589 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
590 assert_equal version, @adapter.class.scm_command_version
590 assert_equal version, @adapter.class.scm_command_version
591 end
591 end
592
592
593 else
593 else
594 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
594 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
595 def test_fake; assert true end
595 def test_fake; assert true end
596 end
596 end
597 end
597 end
@@ -1,1003 +1,1003
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ProjectTest < ActiveSupport::TestCase
20 class ProjectTest < ActiveSupport::TestCase
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :journals, :journal_details,
22 :journals, :journal_details,
23 :enumerations, :users, :issue_categories,
23 :enumerations, :users, :issue_categories,
24 :projects_trackers,
24 :projects_trackers,
25 :custom_fields,
25 :custom_fields,
26 :custom_fields_projects,
26 :custom_fields_projects,
27 :custom_fields_trackers,
27 :custom_fields_trackers,
28 :custom_values,
28 :custom_values,
29 :roles,
29 :roles,
30 :member_roles,
30 :member_roles,
31 :members,
31 :members,
32 :enabled_modules,
32 :enabled_modules,
33 :versions,
33 :versions,
34 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
34 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
35 :groups_users,
35 :groups_users,
36 :boards, :messages,
36 :boards, :messages,
37 :repositories,
37 :repositories,
38 :news, :comments,
38 :news, :comments,
39 :documents,
39 :documents,
40 :workflows
40 :workflows
41
41
42 def setup
42 def setup
43 @ecookbook = Project.find(1)
43 @ecookbook = Project.find(1)
44 @ecookbook_sub1 = Project.find(3)
44 @ecookbook_sub1 = Project.find(3)
45 set_tmp_attachments_directory
45 set_tmp_attachments_directory
46 User.current = nil
46 User.current = nil
47 end
47 end
48
48
49 def test_truth
49 def test_truth
50 assert_kind_of Project, @ecookbook
50 assert_kind_of Project, @ecookbook
51 assert_equal "eCookbook", @ecookbook.name
51 assert_equal "eCookbook", @ecookbook.name
52 end
52 end
53
53
54 def test_default_attributes
54 def test_default_attributes
55 with_settings :default_projects_public => '1' do
55 with_settings :default_projects_public => '1' do
56 assert_equal true, Project.new.is_public
56 assert_equal true, Project.new.is_public
57 assert_equal false, Project.new(:is_public => false).is_public
57 assert_equal false, Project.new(:is_public => false).is_public
58 end
58 end
59
59
60 with_settings :default_projects_public => '0' do
60 with_settings :default_projects_public => '0' do
61 assert_equal false, Project.new.is_public
61 assert_equal false, Project.new.is_public
62 assert_equal true, Project.new(:is_public => true).is_public
62 assert_equal true, Project.new(:is_public => true).is_public
63 end
63 end
64
64
65 with_settings :sequential_project_identifiers => '1' do
65 with_settings :sequential_project_identifiers => '1' do
66 assert !Project.new.identifier.blank?
66 assert !Project.new.identifier.blank?
67 assert Project.new(:identifier => '').identifier.blank?
67 assert Project.new(:identifier => '').identifier.blank?
68 end
68 end
69
69
70 with_settings :sequential_project_identifiers => '0' do
70 with_settings :sequential_project_identifiers => '0' do
71 assert Project.new.identifier.blank?
71 assert Project.new.identifier.blank?
72 assert !Project.new(:identifier => 'test').blank?
72 assert !Project.new(:identifier => 'test').blank?
73 end
73 end
74
74
75 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
75 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
76 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
76 assert_equal ['issue_tracking', 'repository'], Project.new.enabled_module_names
77 end
77 end
78 end
78 end
79
79
80 def test_default_trackers_should_match_default_tracker_ids_setting
80 def test_default_trackers_should_match_default_tracker_ids_setting
81 with_settings :default_projects_tracker_ids => ['1', '3'] do
81 with_settings :default_projects_tracker_ids => ['1', '3'] do
82 assert_equal Tracker.find(1, 3).sort, Project.new.trackers.sort
82 assert_equal Tracker.find(1, 3).sort, Project.new.trackers.sort
83 end
83 end
84 end
84 end
85
85
86 def test_default_trackers_should_be_all_trackers_with_blank_setting
86 def test_default_trackers_should_be_all_trackers_with_blank_setting
87 with_settings :default_projects_tracker_ids => nil do
87 with_settings :default_projects_tracker_ids => nil do
88 assert_equal Tracker.all.sort, Project.new.trackers.sort
88 assert_equal Tracker.all.sort, Project.new.trackers.sort
89 end
89 end
90 end
90 end
91
91
92 def test_default_trackers_should_be_empty_with_empty_setting
92 def test_default_trackers_should_be_empty_with_empty_setting
93 with_settings :default_projects_tracker_ids => [] do
93 with_settings :default_projects_tracker_ids => [] do
94 assert_equal [], Project.new.trackers
94 assert_equal [], Project.new.trackers
95 end
95 end
96 end
96 end
97
97
98 def test_default_trackers_should_not_replace_initialized_trackers
98 def test_default_trackers_should_not_replace_initialized_trackers
99 with_settings :default_projects_tracker_ids => ['1', '3'] do
99 with_settings :default_projects_tracker_ids => ['1', '3'] do
100 assert_equal Tracker.find(1, 2).sort, Project.new(:tracker_ids => [1, 2]).trackers.sort
100 assert_equal Tracker.find(1, 2).sort, Project.new(:tracker_ids => [1, 2]).trackers.sort
101 end
101 end
102 end
102 end
103
103
104 def test_update
104 def test_update
105 assert_equal "eCookbook", @ecookbook.name
105 assert_equal "eCookbook", @ecookbook.name
106 @ecookbook.name = "eCook"
106 @ecookbook.name = "eCook"
107 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
107 assert @ecookbook.save, @ecookbook.errors.full_messages.join("; ")
108 @ecookbook.reload
108 @ecookbook.reload
109 assert_equal "eCook", @ecookbook.name
109 assert_equal "eCook", @ecookbook.name
110 end
110 end
111
111
112 def test_validate_identifier
112 def test_validate_identifier
113 to_test = {"abc" => true,
113 to_test = {"abc" => true,
114 "ab12" => true,
114 "ab12" => true,
115 "ab-12" => true,
115 "ab-12" => true,
116 "ab_12" => true,
116 "ab_12" => true,
117 "12" => false,
117 "12" => false,
118 "new" => false}
118 "new" => false}
119
119
120 to_test.each do |identifier, valid|
120 to_test.each do |identifier, valid|
121 p = Project.new
121 p = Project.new
122 p.identifier = identifier
122 p.identifier = identifier
123 p.valid?
123 p.valid?
124 if valid
124 if valid
125 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
125 assert p.errors['identifier'].blank?, "identifier #{identifier} was not valid"
126 else
126 else
127 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
127 assert p.errors['identifier'].present?, "identifier #{identifier} was valid"
128 end
128 end
129 end
129 end
130 end
130 end
131
131
132 def test_identifier_should_not_be_frozen_for_a_new_project
132 def test_identifier_should_not_be_frozen_for_a_new_project
133 assert_equal false, Project.new.identifier_frozen?
133 assert_equal false, Project.new.identifier_frozen?
134 end
134 end
135
135
136 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
136 def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier
137 Project.where(:id => 1).update_all(["identifier = ''"])
137 Project.where(:id => 1).update_all(["identifier = ''"])
138 assert_equal false, Project.find(1).identifier_frozen?
138 assert_equal false, Project.find(1).identifier_frozen?
139 end
139 end
140
140
141 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
141 def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier
142 assert_equal true, Project.find(1).identifier_frozen?
142 assert_equal true, Project.find(1).identifier_frozen?
143 end
143 end
144
144
145 def test_to_param_should_be_nil_for_new_records
145 def test_to_param_should_be_nil_for_new_records
146 project = Project.new
146 project = Project.new
147 project.identifier = "foo"
147 project.identifier = "foo"
148 assert_nil project.to_param
148 assert_nil project.to_param
149 end
149 end
150
150
151 def test_members_should_be_active_users
151 def test_members_should_be_active_users
152 Project.all.each do |project|
152 Project.all.each do |project|
153 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
153 assert_nil project.members.detect {|m| !(m.user.is_a?(User) && m.user.active?) }
154 end
154 end
155 end
155 end
156
156
157 def test_users_should_be_active_users
157 def test_users_should_be_active_users
158 Project.all.each do |project|
158 Project.all.each do |project|
159 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
159 assert_nil project.users.detect {|u| !(u.is_a?(User) && u.active?) }
160 end
160 end
161 end
161 end
162
162
163 def test_open_scope_on_issues_association
163 def test_open_scope_on_issues_association
164 assert_kind_of Issue, Project.find(1).issues.open.first
164 assert_kind_of Issue, Project.find(1).issues.open.first
165 end
165 end
166
166
167 def test_archive
167 def test_archive
168 user = @ecookbook.members.first.user
168 user = @ecookbook.members.first.user
169 @ecookbook.archive
169 @ecookbook.archive
170 @ecookbook.reload
170 @ecookbook.reload
171
171
172 assert !@ecookbook.active?
172 assert !@ecookbook.active?
173 assert @ecookbook.archived?
173 assert @ecookbook.archived?
174 assert !user.projects.include?(@ecookbook)
174 assert !user.projects.include?(@ecookbook)
175 # Subproject are also archived
175 # Subproject are also archived
176 assert !@ecookbook.children.empty?
176 assert !@ecookbook.children.empty?
177 assert @ecookbook.descendants.active.empty?
177 assert @ecookbook.descendants.active.empty?
178 end
178 end
179
179
180 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
180 def test_archive_should_fail_if_versions_are_used_by_non_descendant_projects
181 # Assign an issue of a project to a version of a child project
181 # Assign an issue of a project to a version of a child project
182 Issue.find(4).update_attribute :fixed_version_id, 4
182 Issue.find(4).update_attribute :fixed_version_id, 4
183
183
184 assert_no_difference "Project.where(:status => Project::STATUS_ARCHIVED).count" do
184 assert_no_difference "Project.where(:status => Project::STATUS_ARCHIVED).count" do
185 assert_equal false, @ecookbook.archive
185 assert_equal false, @ecookbook.archive
186 end
186 end
187 @ecookbook.reload
187 @ecookbook.reload
188 assert @ecookbook.active?
188 assert @ecookbook.active?
189 end
189 end
190
190
191 def test_unarchive
191 def test_unarchive
192 user = @ecookbook.members.first.user
192 user = @ecookbook.members.first.user
193 @ecookbook.archive
193 @ecookbook.archive
194 # A subproject of an archived project can not be unarchived
194 # A subproject of an archived project can not be unarchived
195 assert !@ecookbook_sub1.unarchive
195 assert !@ecookbook_sub1.unarchive
196
196
197 # Unarchive project
197 # Unarchive project
198 assert @ecookbook.unarchive
198 assert @ecookbook.unarchive
199 @ecookbook.reload
199 @ecookbook.reload
200 assert @ecookbook.active?
200 assert @ecookbook.active?
201 assert !@ecookbook.archived?
201 assert !@ecookbook.archived?
202 assert user.projects.include?(@ecookbook)
202 assert user.projects.include?(@ecookbook)
203 # Subproject can now be unarchived
203 # Subproject can now be unarchived
204 @ecookbook_sub1.reload
204 @ecookbook_sub1.reload
205 assert @ecookbook_sub1.unarchive
205 assert @ecookbook_sub1.unarchive
206 end
206 end
207
207
208 def test_destroy
208 def test_destroy
209 # 2 active members
209 # 2 active members
210 assert_equal 2, @ecookbook.members.size
210 assert_equal 2, @ecookbook.members.size
211 # and 1 is locked
211 # and 1 is locked
212 assert_equal 3, Member.where(:project_id => @ecookbook.id).count
212 assert_equal 3, Member.where(:project_id => @ecookbook.id).count
213 # some boards
213 # some boards
214 assert @ecookbook.boards.any?
214 assert @ecookbook.boards.any?
215
215
216 @ecookbook.destroy
216 @ecookbook.destroy
217 # make sure that the project non longer exists
217 # make sure that the project non longer exists
218 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
218 assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }
219 # make sure related data was removed
219 # make sure related data was removed
220 assert_nil Member.where(:project_id => @ecookbook.id).first
220 assert_nil Member.where(:project_id => @ecookbook.id).first
221 assert_nil Board.where(:project_id => @ecookbook.id).first
221 assert_nil Board.where(:project_id => @ecookbook.id).first
222 assert_nil Issue.where(:project_id => @ecookbook.id).first
222 assert_nil Issue.where(:project_id => @ecookbook.id).first
223 end
223 end
224
224
225 def test_destroy_should_destroy_subtasks
225 def test_destroy_should_destroy_subtasks
226 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
226 issues = (0..2).to_a.map {Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :subject => 'test')}
227 issues[0].update! :parent_issue_id => issues[1].id
227 issues[0].update! :parent_issue_id => issues[1].id
228 issues[2].update! :parent_issue_id => issues[1].id
228 issues[2].update! :parent_issue_id => issues[1].id
229 assert_equal 2, issues[1].children.count
229 assert_equal 2, issues[1].children.count
230
230
231 assert_nothing_raised do
231 assert_nothing_raised do
232 Project.find(1).destroy
232 Project.find(1).destroy
233 end
233 end
234 assert_equal 0, Issue.where(:id => issues.map(&:id)).count
234 assert_equal 0, Issue.where(:id => issues.map(&:id)).count
235 end
235 end
236
236
237 def test_destroying_root_projects_should_clear_data
237 def test_destroying_root_projects_should_clear_data
238 Project.roots.each do |root|
238 Project.roots.each do |root|
239 root.destroy
239 root.destroy
240 end
240 end
241
241
242 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
242 assert_equal 0, Project.count, "Projects were not deleted: #{Project.all.inspect}"
243 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
243 assert_equal 0, Member.count, "Members were not deleted: #{Member.all.inspect}"
244 assert_equal 0, MemberRole.count
244 assert_equal 0, MemberRole.count
245 assert_equal 0, Issue.count
245 assert_equal 0, Issue.count
246 assert_equal 0, Journal.count
246 assert_equal 0, Journal.count
247 assert_equal 0, JournalDetail.count
247 assert_equal 0, JournalDetail.count
248 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
248 assert_equal 0, Attachment.count, "Attachments were not deleted: #{Attachment.all.inspect}"
249 assert_equal 0, EnabledModule.count
249 assert_equal 0, EnabledModule.count
250 assert_equal 0, IssueCategory.count
250 assert_equal 0, IssueCategory.count
251 assert_equal 0, IssueRelation.count
251 assert_equal 0, IssueRelation.count
252 assert_equal 0, Board.count
252 assert_equal 0, Board.count
253 assert_equal 0, Message.count
253 assert_equal 0, Message.count
254 assert_equal 0, News.count
254 assert_equal 0, News.count
255 assert_equal 0, Query.where("project_id IS NOT NULL").count
255 assert_equal 0, Query.where("project_id IS NOT NULL").count
256 assert_equal 0, Repository.count
256 assert_equal 0, Repository.count
257 assert_equal 0, Changeset.count
257 assert_equal 0, Changeset.count
258 assert_equal 0, Change.count
258 assert_equal 0, Change.count
259 assert_equal 0, Comment.count
259 assert_equal 0, Comment.count
260 assert_equal 0, TimeEntry.count
260 assert_equal 0, TimeEntry.count
261 assert_equal 0, Version.count
261 assert_equal 0, Version.count
262 assert_equal 0, Watcher.count
262 assert_equal 0, Watcher.count
263 assert_equal 0, Wiki.count
263 assert_equal 0, Wiki.count
264 assert_equal 0, WikiPage.count
264 assert_equal 0, WikiPage.count
265 assert_equal 0, WikiContent.count
265 assert_equal 0, WikiContent.count
266 assert_equal 0, WikiContent::Version.count
266 assert_equal 0, WikiContent::Version.count
267 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").count
267 assert_equal 0, Project.connection.select_all("SELECT * FROM projects_trackers").count
268 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").count
268 assert_equal 0, Project.connection.select_all("SELECT * FROM custom_fields_projects").count
269 assert_equal 0, CustomValue.where(:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']).count
269 assert_equal 0, CustomValue.where(:customized_type => ['Project', 'Issue', 'TimeEntry', 'Version']).count
270 end
270 end
271
271
272 def test_destroy_should_delete_time_entries_custom_values
272 def test_destroy_should_delete_time_entries_custom_values
273 project = Project.generate!
273 project = Project.generate!
274 time_entry = TimeEntry.generate!(:project => project, :custom_field_values => {10 => '1'})
274 time_entry = TimeEntry.generate!(:project => project, :custom_field_values => {10 => '1'})
275
275
276 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
276 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
277 assert project.destroy
277 assert project.destroy
278 end
278 end
279 end
279 end
280
280
281 def test_move_an_orphan_project_to_a_root_project
281 def test_move_an_orphan_project_to_a_root_project
282 sub = Project.find(2)
282 sub = Project.find(2)
283 sub.set_parent! @ecookbook
283 sub.set_parent! @ecookbook
284 assert_equal @ecookbook.id, sub.parent.id
284 assert_equal @ecookbook.id, sub.parent.id
285 @ecookbook.reload
285 @ecookbook.reload
286 assert_equal 4, @ecookbook.children.size
286 assert_equal 4, @ecookbook.children.size
287 end
287 end
288
288
289 def test_move_an_orphan_project_to_a_subproject
289 def test_move_an_orphan_project_to_a_subproject
290 sub = Project.find(2)
290 sub = Project.find(2)
291 assert sub.set_parent!(@ecookbook_sub1)
291 assert sub.set_parent!(@ecookbook_sub1)
292 end
292 end
293
293
294 def test_move_a_root_project_to_a_project
294 def test_move_a_root_project_to_a_project
295 sub = @ecookbook
295 sub = @ecookbook
296 assert sub.set_parent!(Project.find(2))
296 assert sub.set_parent!(Project.find(2))
297 end
297 end
298
298
299 def test_should_not_move_a_project_to_its_children
299 def test_should_not_move_a_project_to_its_children
300 sub = @ecookbook
300 sub = @ecookbook
301 assert !(sub.set_parent!(Project.find(3)))
301 assert !(sub.set_parent!(Project.find(3)))
302 end
302 end
303
303
304 def test_set_parent_should_add_roots_in_alphabetical_order
304 def test_set_parent_should_add_roots_in_alphabetical_order
305 ProjectCustomField.delete_all
305 ProjectCustomField.delete_all
306 Project.delete_all
306 Project.delete_all
307 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
307 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(nil)
308 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
308 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(nil)
309 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
309 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(nil)
310 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
310 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(nil)
311
311
312 assert_equal 4, Project.count
312 assert_equal 4, Project.count
313 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
313 assert_equal Project.all.sort_by(&:name), Project.all.sort_by(&:lft)
314 end
314 end
315
315
316 def test_set_parent_should_add_children_in_alphabetical_order
316 def test_set_parent_should_add_children_in_alphabetical_order
317 ProjectCustomField.delete_all
317 ProjectCustomField.delete_all
318 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
318 parent = Project.create!(:name => 'Parent', :identifier => 'parent')
319 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
319 Project.create!(:name => 'Project C', :identifier => 'project-c').set_parent!(parent)
320 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
320 Project.create!(:name => 'Project B', :identifier => 'project-b').set_parent!(parent)
321 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
321 Project.create!(:name => 'Project D', :identifier => 'project-d').set_parent!(parent)
322 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
322 Project.create!(:name => 'Project A', :identifier => 'project-a').set_parent!(parent)
323
323
324 parent.reload
324 parent.reload
325 assert_equal 4, parent.children.size
325 assert_equal 4, parent.children.size
326 assert_equal parent.children.sort_by(&:name), parent.children.to_a
326 assert_equal parent.children.sort_by(&:name), parent.children.to_a
327 end
327 end
328
328
329 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
329 def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
330 # Parent issue with a hierarchy project's fixed version
330 # Parent issue with a hierarchy project's fixed version
331 parent_issue = Issue.find(1)
331 parent_issue = Issue.find(1)
332 parent_issue.update_attribute(:fixed_version_id, 4)
332 parent_issue.update_attribute(:fixed_version_id, 4)
333 parent_issue.reload
333 parent_issue.reload
334 assert_equal 4, parent_issue.fixed_version_id
334 assert_equal 4, parent_issue.fixed_version_id
335
335
336 # Should keep fixed versions for the issues
336 # Should keep fixed versions for the issues
337 issue_with_local_fixed_version = Issue.find(5)
337 issue_with_local_fixed_version = Issue.find(5)
338 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
338 issue_with_local_fixed_version.update_attribute(:fixed_version_id, 4)
339 issue_with_local_fixed_version.reload
339 issue_with_local_fixed_version.reload
340 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
340 assert_equal 4, issue_with_local_fixed_version.fixed_version_id
341
341
342 # Local issue with hierarchy fixed_version
342 # Local issue with hierarchy fixed_version
343 issue_with_hierarchy_fixed_version = Issue.find(13)
343 issue_with_hierarchy_fixed_version = Issue.find(13)
344 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
344 issue_with_hierarchy_fixed_version.update_attribute(:fixed_version_id, 6)
345 issue_with_hierarchy_fixed_version.reload
345 issue_with_hierarchy_fixed_version.reload
346 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
346 assert_equal 6, issue_with_hierarchy_fixed_version.fixed_version_id
347
347
348 # Move project out of the issue's hierarchy
348 # Move project out of the issue's hierarchy
349 moved_project = Project.find(3)
349 moved_project = Project.find(3)
350 moved_project.set_parent!(Project.find(2))
350 moved_project.set_parent!(Project.find(2))
351 parent_issue.reload
351 parent_issue.reload
352 issue_with_local_fixed_version.reload
352 issue_with_local_fixed_version.reload
353 issue_with_hierarchy_fixed_version.reload
353 issue_with_hierarchy_fixed_version.reload
354
354
355 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
355 assert_equal 4, issue_with_local_fixed_version.fixed_version_id, "Fixed version was not keep on an issue local to the moved project"
356 assert_equal nil, issue_with_hierarchy_fixed_version.fixed_version_id, "Fixed version is still set after moving the Project out of the hierarchy where the version is defined in"
356 assert_nil issue_with_hierarchy_fixed_version.fixed_version_id, "Fixed version is still set after moving the Project out of the hierarchy where the version is defined in"
357 assert_equal nil, parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
357 assert_nil parent_issue.fixed_version_id, "Fixed version is still set after moving the Version out of the hierarchy for the issue."
358 end
358 end
359
359
360 def test_parent
360 def test_parent
361 p = Project.find(6).parent
361 p = Project.find(6).parent
362 assert p.is_a?(Project)
362 assert p.is_a?(Project)
363 assert_equal 5, p.id
363 assert_equal 5, p.id
364 end
364 end
365
365
366 def test_ancestors
366 def test_ancestors
367 a = Project.find(6).ancestors
367 a = Project.find(6).ancestors
368 assert a.first.is_a?(Project)
368 assert a.first.is_a?(Project)
369 assert_equal [1, 5], a.collect(&:id)
369 assert_equal [1, 5], a.collect(&:id)
370 end
370 end
371
371
372 def test_root
372 def test_root
373 r = Project.find(6).root
373 r = Project.find(6).root
374 assert r.is_a?(Project)
374 assert r.is_a?(Project)
375 assert_equal 1, r.id
375 assert_equal 1, r.id
376 end
376 end
377
377
378 def test_children
378 def test_children
379 c = Project.find(1).children
379 c = Project.find(1).children
380 assert c.first.is_a?(Project)
380 assert c.first.is_a?(Project)
381 assert_equal [5, 3, 4], c.collect(&:id)
381 assert_equal [5, 3, 4], c.collect(&:id)
382 end
382 end
383
383
384 def test_descendants
384 def test_descendants
385 d = Project.find(1).descendants
385 d = Project.find(1).descendants
386 assert d.first.is_a?(Project)
386 assert d.first.is_a?(Project)
387 assert_equal [5, 6, 3, 4], d.collect(&:id)
387 assert_equal [5, 6, 3, 4], d.collect(&:id)
388 end
388 end
389
389
390 def test_allowed_parents_should_be_empty_for_non_member_user
390 def test_allowed_parents_should_be_empty_for_non_member_user
391 Role.non_member.add_permission!(:add_project)
391 Role.non_member.add_permission!(:add_project)
392 user = User.find(9)
392 user = User.find(9)
393 assert user.memberships.empty?
393 assert user.memberships.empty?
394 User.current = user
394 User.current = user
395 assert Project.new.allowed_parents.compact.empty?
395 assert Project.new.allowed_parents.compact.empty?
396 end
396 end
397
397
398 def test_allowed_parents_with_add_subprojects_permission
398 def test_allowed_parents_with_add_subprojects_permission
399 Role.find(1).remove_permission!(:add_project)
399 Role.find(1).remove_permission!(:add_project)
400 Role.find(1).add_permission!(:add_subprojects)
400 Role.find(1).add_permission!(:add_subprojects)
401 User.current = User.find(2)
401 User.current = User.find(2)
402 # new project
402 # new project
403 assert !Project.new.allowed_parents.include?(nil)
403 assert !Project.new.allowed_parents.include?(nil)
404 assert Project.new.allowed_parents.include?(Project.find(1))
404 assert Project.new.allowed_parents.include?(Project.find(1))
405 # existing root project
405 # existing root project
406 assert Project.find(1).allowed_parents.include?(nil)
406 assert Project.find(1).allowed_parents.include?(nil)
407 # existing child
407 # existing child
408 assert Project.find(3).allowed_parents.include?(Project.find(1))
408 assert Project.find(3).allowed_parents.include?(Project.find(1))
409 assert !Project.find(3).allowed_parents.include?(nil)
409 assert !Project.find(3).allowed_parents.include?(nil)
410 end
410 end
411
411
412 def test_allowed_parents_with_add_project_permission
412 def test_allowed_parents_with_add_project_permission
413 Role.find(1).add_permission!(:add_project)
413 Role.find(1).add_permission!(:add_project)
414 Role.find(1).remove_permission!(:add_subprojects)
414 Role.find(1).remove_permission!(:add_subprojects)
415 User.current = User.find(2)
415 User.current = User.find(2)
416 # new project
416 # new project
417 assert Project.new.allowed_parents.include?(nil)
417 assert Project.new.allowed_parents.include?(nil)
418 assert !Project.new.allowed_parents.include?(Project.find(1))
418 assert !Project.new.allowed_parents.include?(Project.find(1))
419 # existing root project
419 # existing root project
420 assert Project.find(1).allowed_parents.include?(nil)
420 assert Project.find(1).allowed_parents.include?(nil)
421 # existing child
421 # existing child
422 assert Project.find(3).allowed_parents.include?(Project.find(1))
422 assert Project.find(3).allowed_parents.include?(Project.find(1))
423 assert Project.find(3).allowed_parents.include?(nil)
423 assert Project.find(3).allowed_parents.include?(nil)
424 end
424 end
425
425
426 def test_allowed_parents_with_add_project_and_subprojects_permission
426 def test_allowed_parents_with_add_project_and_subprojects_permission
427 Role.find(1).add_permission!(:add_project)
427 Role.find(1).add_permission!(:add_project)
428 Role.find(1).add_permission!(:add_subprojects)
428 Role.find(1).add_permission!(:add_subprojects)
429 User.current = User.find(2)
429 User.current = User.find(2)
430 # new project
430 # new project
431 assert Project.new.allowed_parents.include?(nil)
431 assert Project.new.allowed_parents.include?(nil)
432 assert Project.new.allowed_parents.include?(Project.find(1))
432 assert Project.new.allowed_parents.include?(Project.find(1))
433 # existing root project
433 # existing root project
434 assert Project.find(1).allowed_parents.include?(nil)
434 assert Project.find(1).allowed_parents.include?(nil)
435 # existing child
435 # existing child
436 assert Project.find(3).allowed_parents.include?(Project.find(1))
436 assert Project.find(3).allowed_parents.include?(Project.find(1))
437 assert Project.find(3).allowed_parents.include?(nil)
437 assert Project.find(3).allowed_parents.include?(nil)
438 end
438 end
439
439
440 def test_users_by_role
440 def test_users_by_role
441 users_by_role = Project.find(1).users_by_role
441 users_by_role = Project.find(1).users_by_role
442 assert_kind_of Hash, users_by_role
442 assert_kind_of Hash, users_by_role
443 role = Role.find(1)
443 role = Role.find(1)
444 assert_kind_of Array, users_by_role[role]
444 assert_kind_of Array, users_by_role[role]
445 assert users_by_role[role].include?(User.find(2))
445 assert users_by_role[role].include?(User.find(2))
446 end
446 end
447
447
448 def test_rolled_up_trackers
448 def test_rolled_up_trackers
449 parent = Project.find(1)
449 parent = Project.find(1)
450 parent.trackers = Tracker.find([1,2])
450 parent.trackers = Tracker.find([1,2])
451 child = parent.children.find(3)
451 child = parent.children.find(3)
452
452
453 assert_equal [1, 2], parent.tracker_ids
453 assert_equal [1, 2], parent.tracker_ids
454 assert_equal [2, 3], child.trackers.collect(&:id)
454 assert_equal [2, 3], child.trackers.collect(&:id)
455
455
456 assert_kind_of Tracker, parent.rolled_up_trackers.first
456 assert_kind_of Tracker, parent.rolled_up_trackers.first
457 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
457 assert_equal Tracker.find(1), parent.rolled_up_trackers.first
458
458
459 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
459 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id)
460 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
460 assert_equal [2, 3], child.rolled_up_trackers.collect(&:id)
461 end
461 end
462
462
463 def test_rolled_up_trackers_should_ignore_archived_subprojects
463 def test_rolled_up_trackers_should_ignore_archived_subprojects
464 parent = Project.find(1)
464 parent = Project.find(1)
465 parent.trackers = Tracker.find([1,2])
465 parent.trackers = Tracker.find([1,2])
466 child = parent.children.find(3)
466 child = parent.children.find(3)
467 child.trackers = Tracker.find([1,3])
467 child.trackers = Tracker.find([1,3])
468 parent.children.each(&:archive)
468 parent.children.each(&:archive)
469
469
470 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
470 assert_equal [1,2], parent.rolled_up_trackers.collect(&:id)
471 end
471 end
472
472
473 test "#rolled_up_trackers should ignore projects with issue_tracking module disabled" do
473 test "#rolled_up_trackers should ignore projects with issue_tracking module disabled" do
474 parent = Project.generate!
474 parent = Project.generate!
475 parent.trackers = Tracker.find([1, 2])
475 parent.trackers = Tracker.find([1, 2])
476 child = Project.generate_with_parent!(parent)
476 child = Project.generate_with_parent!(parent)
477 child.trackers = Tracker.find([2, 3])
477 child.trackers = Tracker.find([2, 3])
478
478
479 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id).sort
479 assert_equal [1, 2, 3], parent.rolled_up_trackers.collect(&:id).sort
480
480
481 assert child.disable_module!(:issue_tracking)
481 assert child.disable_module!(:issue_tracking)
482 parent.reload
482 parent.reload
483 assert_equal [1, 2], parent.rolled_up_trackers.collect(&:id).sort
483 assert_equal [1, 2], parent.rolled_up_trackers.collect(&:id).sort
484 end
484 end
485
485
486 test "#rolled_up_versions should include the versions for the current project" do
486 test "#rolled_up_versions should include the versions for the current project" do
487 project = Project.generate!
487 project = Project.generate!
488 parent_version_1 = Version.generate!(:project => project)
488 parent_version_1 = Version.generate!(:project => project)
489 parent_version_2 = Version.generate!(:project => project)
489 parent_version_2 = Version.generate!(:project => project)
490 assert_equal [parent_version_1, parent_version_2].sort,
490 assert_equal [parent_version_1, parent_version_2].sort,
491 project.rolled_up_versions.sort
491 project.rolled_up_versions.sort
492 end
492 end
493
493
494 test "#rolled_up_versions should include versions for a subproject" do
494 test "#rolled_up_versions should include versions for a subproject" do
495 project = Project.generate!
495 project = Project.generate!
496 parent_version_1 = Version.generate!(:project => project)
496 parent_version_1 = Version.generate!(:project => project)
497 parent_version_2 = Version.generate!(:project => project)
497 parent_version_2 = Version.generate!(:project => project)
498 subproject = Project.generate_with_parent!(project)
498 subproject = Project.generate_with_parent!(project)
499 subproject_version = Version.generate!(:project => subproject)
499 subproject_version = Version.generate!(:project => subproject)
500
500
501 assert_equal [parent_version_1, parent_version_2, subproject_version].sort,
501 assert_equal [parent_version_1, parent_version_2, subproject_version].sort,
502 project.rolled_up_versions.sort
502 project.rolled_up_versions.sort
503 end
503 end
504
504
505 test "#rolled_up_versions should include versions for a sub-subproject" do
505 test "#rolled_up_versions should include versions for a sub-subproject" do
506 project = Project.generate!
506 project = Project.generate!
507 parent_version_1 = Version.generate!(:project => project)
507 parent_version_1 = Version.generate!(:project => project)
508 parent_version_2 = Version.generate!(:project => project)
508 parent_version_2 = Version.generate!(:project => project)
509 subproject = Project.generate_with_parent!(project)
509 subproject = Project.generate_with_parent!(project)
510 sub_subproject = Project.generate_with_parent!(subproject)
510 sub_subproject = Project.generate_with_parent!(subproject)
511 sub_subproject_version = Version.generate!(:project => sub_subproject)
511 sub_subproject_version = Version.generate!(:project => sub_subproject)
512 project.reload
512 project.reload
513
513
514 assert_equal [parent_version_1, parent_version_2, sub_subproject_version].sort,
514 assert_equal [parent_version_1, parent_version_2, sub_subproject_version].sort,
515 project.rolled_up_versions.sort
515 project.rolled_up_versions.sort
516 end
516 end
517
517
518 test "#rolled_up_versions should only check active projects" do
518 test "#rolled_up_versions should only check active projects" do
519 project = Project.generate!
519 project = Project.generate!
520 parent_version_1 = Version.generate!(:project => project)
520 parent_version_1 = Version.generate!(:project => project)
521 parent_version_2 = Version.generate!(:project => project)
521 parent_version_2 = Version.generate!(:project => project)
522 subproject = Project.generate_with_parent!(project)
522 subproject = Project.generate_with_parent!(project)
523 subproject_version = Version.generate!(:project => subproject)
523 subproject_version = Version.generate!(:project => subproject)
524 assert subproject.archive
524 assert subproject.archive
525 project.reload
525 project.reload
526
526
527 assert !subproject.active?
527 assert !subproject.active?
528 assert_equal [parent_version_1, parent_version_2].sort,
528 assert_equal [parent_version_1, parent_version_2].sort,
529 project.rolled_up_versions.sort
529 project.rolled_up_versions.sort
530 end
530 end
531
531
532 def test_shared_versions_none_sharing
532 def test_shared_versions_none_sharing
533 p = Project.find(5)
533 p = Project.find(5)
534 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
534 v = Version.create!(:name => 'none_sharing', :project => p, :sharing => 'none')
535 assert p.shared_versions.include?(v)
535 assert p.shared_versions.include?(v)
536 assert !p.children.first.shared_versions.include?(v)
536 assert !p.children.first.shared_versions.include?(v)
537 assert !p.root.shared_versions.include?(v)
537 assert !p.root.shared_versions.include?(v)
538 assert !p.siblings.first.shared_versions.include?(v)
538 assert !p.siblings.first.shared_versions.include?(v)
539 assert !p.root.siblings.first.shared_versions.include?(v)
539 assert !p.root.siblings.first.shared_versions.include?(v)
540 end
540 end
541
541
542 def test_shared_versions_descendants_sharing
542 def test_shared_versions_descendants_sharing
543 p = Project.find(5)
543 p = Project.find(5)
544 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
544 v = Version.create!(:name => 'descendants_sharing', :project => p, :sharing => 'descendants')
545 assert p.shared_versions.include?(v)
545 assert p.shared_versions.include?(v)
546 assert p.children.first.shared_versions.include?(v)
546 assert p.children.first.shared_versions.include?(v)
547 assert !p.root.shared_versions.include?(v)
547 assert !p.root.shared_versions.include?(v)
548 assert !p.siblings.first.shared_versions.include?(v)
548 assert !p.siblings.first.shared_versions.include?(v)
549 assert !p.root.siblings.first.shared_versions.include?(v)
549 assert !p.root.siblings.first.shared_versions.include?(v)
550 end
550 end
551
551
552 def test_shared_versions_hierarchy_sharing
552 def test_shared_versions_hierarchy_sharing
553 p = Project.find(5)
553 p = Project.find(5)
554 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
554 v = Version.create!(:name => 'hierarchy_sharing', :project => p, :sharing => 'hierarchy')
555 assert p.shared_versions.include?(v)
555 assert p.shared_versions.include?(v)
556 assert p.children.first.shared_versions.include?(v)
556 assert p.children.first.shared_versions.include?(v)
557 assert p.root.shared_versions.include?(v)
557 assert p.root.shared_versions.include?(v)
558 assert !p.siblings.first.shared_versions.include?(v)
558 assert !p.siblings.first.shared_versions.include?(v)
559 assert !p.root.siblings.first.shared_versions.include?(v)
559 assert !p.root.siblings.first.shared_versions.include?(v)
560 end
560 end
561
561
562 def test_shared_versions_tree_sharing
562 def test_shared_versions_tree_sharing
563 p = Project.find(5)
563 p = Project.find(5)
564 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
564 v = Version.create!(:name => 'tree_sharing', :project => p, :sharing => 'tree')
565 assert p.shared_versions.include?(v)
565 assert p.shared_versions.include?(v)
566 assert p.children.first.shared_versions.include?(v)
566 assert p.children.first.shared_versions.include?(v)
567 assert p.root.shared_versions.include?(v)
567 assert p.root.shared_versions.include?(v)
568 assert p.siblings.first.shared_versions.include?(v)
568 assert p.siblings.first.shared_versions.include?(v)
569 assert !p.root.siblings.first.shared_versions.include?(v)
569 assert !p.root.siblings.first.shared_versions.include?(v)
570 end
570 end
571
571
572 def test_shared_versions_system_sharing
572 def test_shared_versions_system_sharing
573 p = Project.find(5)
573 p = Project.find(5)
574 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
574 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
575 assert p.shared_versions.include?(v)
575 assert p.shared_versions.include?(v)
576 assert p.children.first.shared_versions.include?(v)
576 assert p.children.first.shared_versions.include?(v)
577 assert p.root.shared_versions.include?(v)
577 assert p.root.shared_versions.include?(v)
578 assert p.siblings.first.shared_versions.include?(v)
578 assert p.siblings.first.shared_versions.include?(v)
579 assert p.root.siblings.first.shared_versions.include?(v)
579 assert p.root.siblings.first.shared_versions.include?(v)
580 end
580 end
581
581
582 def test_shared_versions
582 def test_shared_versions
583 parent = Project.find(1)
583 parent = Project.find(1)
584 child = parent.children.find(3)
584 child = parent.children.find(3)
585 private_child = parent.children.find(5)
585 private_child = parent.children.find(5)
586
586
587 assert_equal [1,2,3], parent.version_ids.sort
587 assert_equal [1,2,3], parent.version_ids.sort
588 assert_equal [4], child.version_ids
588 assert_equal [4], child.version_ids
589 assert_equal [6], private_child.version_ids
589 assert_equal [6], private_child.version_ids
590 assert_equal [7], Version.where(:sharing => 'system').collect(&:id)
590 assert_equal [7], Version.where(:sharing => 'system').collect(&:id)
591
591
592 assert_equal 6, parent.shared_versions.size
592 assert_equal 6, parent.shared_versions.size
593 parent.shared_versions.each do |version|
593 parent.shared_versions.each do |version|
594 assert_kind_of Version, version
594 assert_kind_of Version, version
595 end
595 end
596
596
597 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
597 assert_equal [1,2,3,4,6,7], parent.shared_versions.collect(&:id).sort
598 end
598 end
599
599
600 def test_shared_versions_should_ignore_archived_subprojects
600 def test_shared_versions_should_ignore_archived_subprojects
601 parent = Project.find(1)
601 parent = Project.find(1)
602 child = parent.children.find(3)
602 child = parent.children.find(3)
603 child.archive
603 child.archive
604 parent.reload
604 parent.reload
605
605
606 assert_equal [1,2,3], parent.version_ids.sort
606 assert_equal [1,2,3], parent.version_ids.sort
607 assert_equal [4], child.version_ids
607 assert_equal [4], child.version_ids
608 assert !parent.shared_versions.collect(&:id).include?(4)
608 assert !parent.shared_versions.collect(&:id).include?(4)
609 end
609 end
610
610
611 def test_shared_versions_visible_to_user
611 def test_shared_versions_visible_to_user
612 user = User.find(3)
612 user = User.find(3)
613 parent = Project.find(1)
613 parent = Project.find(1)
614 child = parent.children.find(5)
614 child = parent.children.find(5)
615
615
616 assert_equal [1,2,3], parent.version_ids.sort
616 assert_equal [1,2,3], parent.version_ids.sort
617 assert_equal [6], child.version_ids
617 assert_equal [6], child.version_ids
618
618
619 versions = parent.shared_versions.visible(user)
619 versions = parent.shared_versions.visible(user)
620
620
621 assert_equal 4, versions.size
621 assert_equal 4, versions.size
622 versions.each do |version|
622 versions.each do |version|
623 assert_kind_of Version, version
623 assert_kind_of Version, version
624 end
624 end
625
625
626 assert !versions.collect(&:id).include?(6)
626 assert !versions.collect(&:id).include?(6)
627 end
627 end
628
628
629 def test_shared_versions_for_new_project_should_include_system_shared_versions
629 def test_shared_versions_for_new_project_should_include_system_shared_versions
630 p = Project.find(5)
630 p = Project.find(5)
631 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
631 v = Version.create!(:name => 'system_sharing', :project => p, :sharing => 'system')
632
632
633 assert_include v, Project.new.shared_versions
633 assert_include v, Project.new.shared_versions
634 end
634 end
635
635
636 def test_next_identifier
636 def test_next_identifier
637 ProjectCustomField.delete_all
637 ProjectCustomField.delete_all
638 Project.create!(:name => 'last', :identifier => 'p2008040')
638 Project.create!(:name => 'last', :identifier => 'p2008040')
639 assert_equal 'p2008041', Project.next_identifier
639 assert_equal 'p2008041', Project.next_identifier
640 end
640 end
641
641
642 def test_next_identifier_first_project
642 def test_next_identifier_first_project
643 Project.delete_all
643 Project.delete_all
644 assert_nil Project.next_identifier
644 assert_nil Project.next_identifier
645 end
645 end
646
646
647 def test_enabled_module_names
647 def test_enabled_module_names
648 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
648 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
649 project = Project.new
649 project = Project.new
650
650
651 project.enabled_module_names = %w(issue_tracking news)
651 project.enabled_module_names = %w(issue_tracking news)
652 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
652 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
653 end
653 end
654 end
654 end
655
655
656 def test_enabled_modules_names_with_nil_should_clear_modules
656 def test_enabled_modules_names_with_nil_should_clear_modules
657 p = Project.find(1)
657 p = Project.find(1)
658 p.enabled_module_names = nil
658 p.enabled_module_names = nil
659 assert_equal [], p.enabled_modules
659 assert_equal [], p.enabled_modules
660 end
660 end
661
661
662 test "enabled_modules should define module by names and preserve ids" do
662 test "enabled_modules should define module by names and preserve ids" do
663 @project = Project.find(1)
663 @project = Project.find(1)
664 # Remove one module
664 # Remove one module
665 modules = @project.enabled_modules.to_a.slice(0..-2)
665 modules = @project.enabled_modules.to_a.slice(0..-2)
666 assert modules.any?
666 assert modules.any?
667 assert_difference 'EnabledModule.count', -1 do
667 assert_difference 'EnabledModule.count', -1 do
668 @project.enabled_module_names = modules.collect(&:name)
668 @project.enabled_module_names = modules.collect(&:name)
669 end
669 end
670 @project.reload
670 @project.reload
671 # Ids should be preserved
671 # Ids should be preserved
672 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
672 assert_equal @project.enabled_module_ids.sort, modules.collect(&:id).sort
673 end
673 end
674
674
675 test "enabled_modules should enable a module" do
675 test "enabled_modules should enable a module" do
676 @project = Project.find(1)
676 @project = Project.find(1)
677 @project.enabled_module_names = []
677 @project.enabled_module_names = []
678 @project.reload
678 @project.reload
679 assert_equal [], @project.enabled_module_names
679 assert_equal [], @project.enabled_module_names
680 #with string
680 #with string
681 @project.enable_module!("issue_tracking")
681 @project.enable_module!("issue_tracking")
682 assert_equal ["issue_tracking"], @project.enabled_module_names
682 assert_equal ["issue_tracking"], @project.enabled_module_names
683 #with symbol
683 #with symbol
684 @project.enable_module!(:gantt)
684 @project.enable_module!(:gantt)
685 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
685 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
686 #don't add a module twice
686 #don't add a module twice
687 @project.enable_module!("issue_tracking")
687 @project.enable_module!("issue_tracking")
688 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
688 assert_equal ["issue_tracking", "gantt"], @project.enabled_module_names
689 end
689 end
690
690
691 test "enabled_modules should disable a module" do
691 test "enabled_modules should disable a module" do
692 @project = Project.find(1)
692 @project = Project.find(1)
693 #with string
693 #with string
694 assert @project.enabled_module_names.include?("issue_tracking")
694 assert @project.enabled_module_names.include?("issue_tracking")
695 @project.disable_module!("issue_tracking")
695 @project.disable_module!("issue_tracking")
696 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
696 assert ! @project.reload.enabled_module_names.include?("issue_tracking")
697 #with symbol
697 #with symbol
698 assert @project.enabled_module_names.include?("gantt")
698 assert @project.enabled_module_names.include?("gantt")
699 @project.disable_module!(:gantt)
699 @project.disable_module!(:gantt)
700 assert ! @project.reload.enabled_module_names.include?("gantt")
700 assert ! @project.reload.enabled_module_names.include?("gantt")
701 #with EnabledModule object
701 #with EnabledModule object
702 first_module = @project.enabled_modules.first
702 first_module = @project.enabled_modules.first
703 @project.disable_module!(first_module)
703 @project.disable_module!(first_module)
704 assert ! @project.reload.enabled_module_names.include?(first_module.name)
704 assert ! @project.reload.enabled_module_names.include?(first_module.name)
705 end
705 end
706
706
707 def test_enabled_module_names_should_not_recreate_enabled_modules
707 def test_enabled_module_names_should_not_recreate_enabled_modules
708 project = Project.find(1)
708 project = Project.find(1)
709 # Remove one module
709 # Remove one module
710 modules = project.enabled_modules.to_a.slice(0..-2)
710 modules = project.enabled_modules.to_a.slice(0..-2)
711 assert modules.any?
711 assert modules.any?
712 assert_difference 'EnabledModule.count', -1 do
712 assert_difference 'EnabledModule.count', -1 do
713 project.enabled_module_names = modules.collect(&:name)
713 project.enabled_module_names = modules.collect(&:name)
714 end
714 end
715 project.reload
715 project.reload
716 # Ids should be preserved
716 # Ids should be preserved
717 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
717 assert_equal project.enabled_module_ids.sort, modules.collect(&:id).sort
718 end
718 end
719
719
720 def test_copy_from_existing_project
720 def test_copy_from_existing_project
721 source_project = Project.find(1)
721 source_project = Project.find(1)
722 copied_project = Project.copy_from(1)
722 copied_project = Project.copy_from(1)
723
723
724 assert copied_project
724 assert copied_project
725 # Cleared attributes
725 # Cleared attributes
726 assert copied_project.id.blank?
726 assert copied_project.id.blank?
727 assert copied_project.name.blank?
727 assert copied_project.name.blank?
728 assert copied_project.identifier.blank?
728 assert copied_project.identifier.blank?
729
729
730 # Duplicated attributes
730 # Duplicated attributes
731 assert_equal source_project.description, copied_project.description
731 assert_equal source_project.description, copied_project.description
732 assert_equal source_project.trackers, copied_project.trackers
732 assert_equal source_project.trackers, copied_project.trackers
733
733
734 # Default attributes
734 # Default attributes
735 assert_equal 1, copied_project.status
735 assert_equal 1, copied_project.status
736 end
736 end
737
737
738 def test_copy_from_should_copy_enabled_modules
738 def test_copy_from_should_copy_enabled_modules
739 source = Project.generate!
739 source = Project.generate!
740 source.enabled_module_names = %w(issue_tracking wiki)
740 source.enabled_module_names = %w(issue_tracking wiki)
741
741
742 copy = Project.copy_from(source)
742 copy = Project.copy_from(source)
743 copy.name = 'Copy'
743 copy.name = 'Copy'
744 copy.identifier = 'copy'
744 copy.identifier = 'copy'
745 assert_difference 'EnabledModule.count', 2 do
745 assert_difference 'EnabledModule.count', 2 do
746 copy.save!
746 copy.save!
747 end
747 end
748 assert_equal 2, copy.reload.enabled_modules.count
748 assert_equal 2, copy.reload.enabled_modules.count
749 assert_equal 2, source.reload.enabled_modules.count
749 assert_equal 2, source.reload.enabled_modules.count
750 end
750 end
751
751
752 def test_activities_should_use_the_system_activities
752 def test_activities_should_use_the_system_activities
753 project = Project.find(1)
753 project = Project.find(1)
754 assert_equal project.activities.to_a, TimeEntryActivity.where(:active => true).to_a
754 assert_equal project.activities.to_a, TimeEntryActivity.where(:active => true).to_a
755 assert_kind_of ActiveRecord::Relation, project.activities
755 assert_kind_of ActiveRecord::Relation, project.activities
756 end
756 end
757
757
758
758
759 def test_activities_should_use_the_project_specific_activities
759 def test_activities_should_use_the_project_specific_activities
760 project = Project.find(1)
760 project = Project.find(1)
761 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
761 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project})
762 assert overridden_activity.save!
762 assert overridden_activity.save!
763
763
764 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
764 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
765 assert_kind_of ActiveRecord::Relation, project.activities
765 assert_kind_of ActiveRecord::Relation, project.activities
766 end
766 end
767
767
768 def test_activities_should_not_include_the_inactive_project_specific_activities
768 def test_activities_should_not_include_the_inactive_project_specific_activities
769 project = Project.find(1)
769 project = Project.find(1)
770 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
770 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
771 assert overridden_activity.save!
771 assert overridden_activity.save!
772
772
773 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
773 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity found"
774 end
774 end
775
775
776 def test_activities_should_not_include_project_specific_activities_from_other_projects
776 def test_activities_should_not_include_project_specific_activities_from_other_projects
777 project = Project.find(1)
777 project = Project.find(1)
778 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
778 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(2)})
779 assert overridden_activity.save!
779 assert overridden_activity.save!
780
780
781 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
781 assert !project.activities.include?(overridden_activity), "Project specific Activity found on a different project"
782 end
782 end
783
783
784 def test_activities_should_handle_nils
784 def test_activities_should_handle_nils
785 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.first})
785 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => Project.find(1), :parent => TimeEntryActivity.first})
786 TimeEntryActivity.delete_all
786 TimeEntryActivity.delete_all
787
787
788 # No activities
788 # No activities
789 project = Project.find(1)
789 project = Project.find(1)
790 assert project.activities.empty?
790 assert project.activities.empty?
791
791
792 # No system, one overridden
792 # No system, one overridden
793 assert overridden_activity.save!
793 assert overridden_activity.save!
794 project.reload
794 project.reload
795 assert_equal [overridden_activity], project.activities
795 assert_equal [overridden_activity], project.activities
796 end
796 end
797
797
798 def test_activities_should_override_system_activities_with_project_activities
798 def test_activities_should_override_system_activities_with_project_activities
799 project = Project.find(1)
799 project = Project.find(1)
800 parent_activity = TimeEntryActivity.first
800 parent_activity = TimeEntryActivity.first
801 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
801 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => parent_activity})
802 assert overridden_activity.save!
802 assert overridden_activity.save!
803
803
804 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
804 assert project.activities.include?(overridden_activity), "Project specific Activity not found"
805 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
805 assert !project.activities.include?(parent_activity), "System Activity found when it should have been overridden"
806 end
806 end
807
807
808 def test_activities_should_include_inactive_activities_if_specified
808 def test_activities_should_include_inactive_activities_if_specified
809 project = Project.find(1)
809 project = Project.find(1)
810 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
810 overridden_activity = TimeEntryActivity.new({:name => "Project", :project => project, :parent => TimeEntryActivity.first, :active => false})
811 assert overridden_activity.save!
811 assert overridden_activity.save!
812
812
813 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
813 assert project.activities(true).include?(overridden_activity), "Inactive Project specific Activity not found"
814 end
814 end
815
815
816 test 'activities should not include active System activities if the project has an override that is inactive' do
816 test 'activities should not include active System activities if the project has an override that is inactive' do
817 project = Project.find(1)
817 project = Project.find(1)
818 system_activity = TimeEntryActivity.find_by_name('Design')
818 system_activity = TimeEntryActivity.find_by_name('Design')
819 assert system_activity.active?
819 assert system_activity.active?
820 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
820 overridden_activity = TimeEntryActivity.create!(:name => "Project", :project => project, :parent => system_activity, :active => false)
821 assert overridden_activity.save!
821 assert overridden_activity.save!
822
822
823 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
823 assert !project.activities.include?(overridden_activity), "Inactive Project specific Activity not found"
824 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
824 assert !project.activities.include?(system_activity), "System activity found when the project has an inactive override"
825 end
825 end
826
826
827 def test_close_completed_versions
827 def test_close_completed_versions
828 Version.update_all("status = 'open'")
828 Version.update_all("status = 'open'")
829 project = Project.find(1)
829 project = Project.find(1)
830 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
830 assert_not_nil project.versions.detect {|v| v.completed? && v.status == 'open'}
831 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
831 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
832 project.close_completed_versions
832 project.close_completed_versions
833 project.reload
833 project.reload
834 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
834 assert_nil project.versions.detect {|v| v.completed? && v.status != 'closed'}
835 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
835 assert_not_nil project.versions.detect {|v| !v.completed? && v.status == 'open'}
836 end
836 end
837
837
838 test "#start_date should be nil if there are no issues on the project" do
838 test "#start_date should be nil if there are no issues on the project" do
839 project = Project.generate!
839 project = Project.generate!
840 assert_nil project.start_date
840 assert_nil project.start_date
841 end
841 end
842
842
843 test "#start_date should be nil when issues have no start date" do
843 test "#start_date should be nil when issues have no start date" do
844 project = Project.generate!
844 project = Project.generate!
845 project.trackers << Tracker.generate!
845 project.trackers << Tracker.generate!
846 early = 7.days.ago.to_date
846 early = 7.days.ago.to_date
847 Issue.generate!(:project => project, :start_date => nil)
847 Issue.generate!(:project => project, :start_date => nil)
848
848
849 assert_nil project.start_date
849 assert_nil project.start_date
850 end
850 end
851
851
852 test "#start_date should be the earliest start date of it's issues" do
852 test "#start_date should be the earliest start date of it's issues" do
853 project = Project.generate!
853 project = Project.generate!
854 project.trackers << Tracker.generate!
854 project.trackers << Tracker.generate!
855 early = 7.days.ago.to_date
855 early = 7.days.ago.to_date
856 Issue.generate!(:project => project, :start_date => Date.today)
856 Issue.generate!(:project => project, :start_date => Date.today)
857 Issue.generate!(:project => project, :start_date => early)
857 Issue.generate!(:project => project, :start_date => early)
858
858
859 assert_equal early, project.start_date
859 assert_equal early, project.start_date
860 end
860 end
861
861
862 test "#due_date should be nil if there are no issues on the project" do
862 test "#due_date should be nil if there are no issues on the project" do
863 project = Project.generate!
863 project = Project.generate!
864 assert_nil project.due_date
864 assert_nil project.due_date
865 end
865 end
866
866
867 test "#due_date should be nil if there are no issues with due dates" do
867 test "#due_date should be nil if there are no issues with due dates" do
868 project = Project.generate!
868 project = Project.generate!
869 project.trackers << Tracker.generate!
869 project.trackers << Tracker.generate!
870 Issue.generate!(:project => project, :due_date => nil)
870 Issue.generate!(:project => project, :due_date => nil)
871
871
872 assert_nil project.due_date
872 assert_nil project.due_date
873 end
873 end
874
874
875 test "#due_date should be the latest due date of it's issues" do
875 test "#due_date should be the latest due date of it's issues" do
876 project = Project.generate!
876 project = Project.generate!
877 project.trackers << Tracker.generate!
877 project.trackers << Tracker.generate!
878 future = 7.days.from_now.to_date
878 future = 7.days.from_now.to_date
879 Issue.generate!(:project => project, :due_date => future)
879 Issue.generate!(:project => project, :due_date => future)
880 Issue.generate!(:project => project, :due_date => Date.today)
880 Issue.generate!(:project => project, :due_date => Date.today)
881
881
882 assert_equal future, project.due_date
882 assert_equal future, project.due_date
883 end
883 end
884
884
885 test "#due_date should be the latest due date of it's versions" do
885 test "#due_date should be the latest due date of it's versions" do
886 project = Project.generate!
886 project = Project.generate!
887 future = 7.days.from_now.to_date
887 future = 7.days.from_now.to_date
888 project.versions << Version.generate!(:effective_date => future)
888 project.versions << Version.generate!(:effective_date => future)
889 project.versions << Version.generate!(:effective_date => Date.today)
889 project.versions << Version.generate!(:effective_date => Date.today)
890
890
891 assert_equal future, project.due_date
891 assert_equal future, project.due_date
892 end
892 end
893
893
894 test "#due_date should pick the latest date from it's issues and versions" do
894 test "#due_date should pick the latest date from it's issues and versions" do
895 project = Project.generate!
895 project = Project.generate!
896 project.trackers << Tracker.generate!
896 project.trackers << Tracker.generate!
897 future = 7.days.from_now.to_date
897 future = 7.days.from_now.to_date
898 far_future = 14.days.from_now.to_date
898 far_future = 14.days.from_now.to_date
899 Issue.generate!(:project => project, :due_date => far_future)
899 Issue.generate!(:project => project, :due_date => far_future)
900 project.versions << Version.generate!(:effective_date => future)
900 project.versions << Version.generate!(:effective_date => future)
901
901
902 assert_equal far_future, project.due_date
902 assert_equal far_future, project.due_date
903 end
903 end
904
904
905 test "#completed_percent with no versions should be 100" do
905 test "#completed_percent with no versions should be 100" do
906 project = Project.generate!
906 project = Project.generate!
907 assert_equal 100, project.completed_percent
907 assert_equal 100, project.completed_percent
908 end
908 end
909
909
910 test "#completed_percent with versions should return 0 if the versions have no issues" do
910 test "#completed_percent with versions should return 0 if the versions have no issues" do
911 project = Project.generate!
911 project = Project.generate!
912 Version.generate!(:project => project)
912 Version.generate!(:project => project)
913 Version.generate!(:project => project)
913 Version.generate!(:project => project)
914
914
915 assert_equal 0, project.completed_percent
915 assert_equal 0, project.completed_percent
916 end
916 end
917
917
918 test "#completed_percent with versions should return 100 if the version has only closed issues" do
918 test "#completed_percent with versions should return 100 if the version has only closed issues" do
919 project = Project.generate!
919 project = Project.generate!
920 project.trackers << Tracker.generate!
920 project.trackers << Tracker.generate!
921 v1 = Version.generate!(:project => project)
921 v1 = Version.generate!(:project => project)
922 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
922 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
923 v2 = Version.generate!(:project => project)
923 v2 = Version.generate!(:project => project)
924 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
924 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
925
925
926 assert_equal 100, project.completed_percent
926 assert_equal 100, project.completed_percent
927 end
927 end
928
928
929 test "#completed_percent with versions should return the averaged completed percent of the versions (not weighted)" do
929 test "#completed_percent with versions should return the averaged completed percent of the versions (not weighted)" do
930 project = Project.generate!
930 project = Project.generate!
931 project.trackers << Tracker.generate!
931 project.trackers << Tracker.generate!
932 v1 = Version.generate!(:project => project)
932 v1 = Version.generate!(:project => project)
933 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
933 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
934 v2 = Version.generate!(:project => project)
934 v2 = Version.generate!(:project => project)
935 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
935 Issue.generate!(:project => project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
936
936
937 assert_equal 50, project.completed_percent
937 assert_equal 50, project.completed_percent
938 end
938 end
939
939
940 test "#notified_users" do
940 test "#notified_users" do
941 project = Project.generate!
941 project = Project.generate!
942 role = Role.generate!
942 role = Role.generate!
943
943
944 user_with_membership_notification = User.generate!(:mail_notification => 'selected')
944 user_with_membership_notification = User.generate!(:mail_notification => 'selected')
945 Member.create!(:project => project, :roles => [role], :principal => user_with_membership_notification, :mail_notification => true)
945 Member.create!(:project => project, :roles => [role], :principal => user_with_membership_notification, :mail_notification => true)
946
946
947 all_events_user = User.generate!(:mail_notification => 'all')
947 all_events_user = User.generate!(:mail_notification => 'all')
948 Member.create!(:project => project, :roles => [role], :principal => all_events_user)
948 Member.create!(:project => project, :roles => [role], :principal => all_events_user)
949
949
950 no_events_user = User.generate!(:mail_notification => 'none')
950 no_events_user = User.generate!(:mail_notification => 'none')
951 Member.create!(:project => project, :roles => [role], :principal => no_events_user)
951 Member.create!(:project => project, :roles => [role], :principal => no_events_user)
952
952
953 only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
953 only_my_events_user = User.generate!(:mail_notification => 'only_my_events')
954 Member.create!(:project => project, :roles => [role], :principal => only_my_events_user)
954 Member.create!(:project => project, :roles => [role], :principal => only_my_events_user)
955
955
956 only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
956 only_assigned_user = User.generate!(:mail_notification => 'only_assigned')
957 Member.create!(:project => project, :roles => [role], :principal => only_assigned_user)
957 Member.create!(:project => project, :roles => [role], :principal => only_assigned_user)
958
958
959 only_owned_user = User.generate!(:mail_notification => 'only_owner')
959 only_owned_user = User.generate!(:mail_notification => 'only_owner')
960 Member.create!(:project => project, :roles => [role], :principal => only_owned_user)
960 Member.create!(:project => project, :roles => [role], :principal => only_owned_user)
961
961
962 assert project.notified_users.include?(user_with_membership_notification), "should include members with a mail notification"
962 assert project.notified_users.include?(user_with_membership_notification), "should include members with a mail notification"
963 assert project.notified_users.include?(all_events_user), "should include users with the 'all' notification option"
963 assert project.notified_users.include?(all_events_user), "should include users with the 'all' notification option"
964 assert !project.notified_users.include?(no_events_user), "should not include users with the 'none' notification option"
964 assert !project.notified_users.include?(no_events_user), "should not include users with the 'none' notification option"
965 assert !project.notified_users.include?(only_my_events_user), "should not include users with the 'only_my_events' notification option"
965 assert !project.notified_users.include?(only_my_events_user), "should not include users with the 'only_my_events' notification option"
966 assert !project.notified_users.include?(only_assigned_user), "should not include users with the 'only_assigned' notification option"
966 assert !project.notified_users.include?(only_assigned_user), "should not include users with the 'only_assigned' notification option"
967 assert !project.notified_users.include?(only_owned_user), "should not include users with the 'only_owner' notification option"
967 assert !project.notified_users.include?(only_owned_user), "should not include users with the 'only_owner' notification option"
968 end
968 end
969
969
970 def test_override_roles_without_builtin_group_memberships
970 def test_override_roles_without_builtin_group_memberships
971 project = Project.generate!
971 project = Project.generate!
972 assert_equal [Role.anonymous], project.override_roles(Role.anonymous)
972 assert_equal [Role.anonymous], project.override_roles(Role.anonymous)
973 assert_equal [Role.non_member], project.override_roles(Role.non_member)
973 assert_equal [Role.non_member], project.override_roles(Role.non_member)
974 end
974 end
975
975
976 def test_css_classes
976 def test_css_classes
977 p = Project.new
977 p = Project.new
978 assert_kind_of String, p.css_classes
978 assert_kind_of String, p.css_classes
979 assert_not_include 'archived', p.css_classes.split
979 assert_not_include 'archived', p.css_classes.split
980 assert_not_include 'closed', p.css_classes.split
980 assert_not_include 'closed', p.css_classes.split
981 end
981 end
982
982
983 def test_css_classes_for_archived_project
983 def test_css_classes_for_archived_project
984 p = Project.new
984 p = Project.new
985 p.status = Project::STATUS_ARCHIVED
985 p.status = Project::STATUS_ARCHIVED
986 assert_include 'archived', p.css_classes.split
986 assert_include 'archived', p.css_classes.split
987 end
987 end
988
988
989 def test_css_classes_for_closed_project
989 def test_css_classes_for_closed_project
990 p = Project.new
990 p = Project.new
991 p.status = Project::STATUS_CLOSED
991 p.status = Project::STATUS_CLOSED
992 assert_include 'closed', p.css_classes.split
992 assert_include 'closed', p.css_classes.split
993 end
993 end
994
994
995 def test_combination_of_visible_and_distinct_scopes_in_case_anonymous_group_has_memberships_should_not_error
995 def test_combination_of_visible_and_distinct_scopes_in_case_anonymous_group_has_memberships_should_not_error
996 project = Project.find(1)
996 project = Project.find(1)
997 member = Member.create!(:project => project, :principal => Group.anonymous, :roles => [Role.generate!])
997 member = Member.create!(:project => project, :principal => Group.anonymous, :roles => [Role.generate!])
998 project.members << member
998 project.members << member
999 assert_nothing_raised do
999 assert_nothing_raised do
1000 Project.distinct.visible.to_a
1000 Project.distinct.visible.to_a
1001 end
1001 end
1002 end
1002 end
1003 end
1003 end
@@ -1,614 +1,614
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositoryGitTest < ActiveSupport::TestCase
20 class RepositoryGitTest < ActiveSupport::TestCase
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
22
22
23 include Redmine::I18n
23 include Redmine::I18n
24
24
25 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
25 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
26 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
26 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
27
27
28 NUM_REV = 28
28 NUM_REV = 28
29 NUM_HEAD = 6
29 NUM_HEAD = 6
30
30
31 FELIX_HEX = "Felix Sch\xC3\xA4fer".force_encoding('UTF-8')
31 FELIX_HEX = "Felix Sch\xC3\xA4fer".force_encoding('UTF-8')
32 CHAR_1_HEX = "\xc3\x9c".force_encoding('UTF-8')
32 CHAR_1_HEX = "\xc3\x9c".force_encoding('UTF-8')
33
33
34 ## Git, Mercurial and CVS path encodings are binary.
34 ## Git, Mercurial and CVS path encodings are binary.
35 ## Subversion supports URL encoding for path.
35 ## Subversion supports URL encoding for path.
36 ## Redmine Mercurial adapter and extension use URL encoding.
36 ## Redmine Mercurial adapter and extension use URL encoding.
37 ## Git accepts only binary path in command line parameter.
37 ## Git accepts only binary path in command line parameter.
38 ## So, there is no way to use binary command line parameter in JRuby.
38 ## So, there is no way to use binary command line parameter in JRuby.
39 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
39 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
40 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
40 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
41
41
42 def setup
42 def setup
43 @project = Project.find(3)
43 @project = Project.find(3)
44 @repository = Repository::Git.create(
44 @repository = Repository::Git.create(
45 :project => @project,
45 :project => @project,
46 :url => REPOSITORY_PATH,
46 :url => REPOSITORY_PATH,
47 :path_encoding => 'ISO-8859-1'
47 :path_encoding => 'ISO-8859-1'
48 )
48 )
49 assert @repository
49 assert @repository
50 end
50 end
51
51
52 def test_nondefault_repo_with_blank_identifier_destruction
52 def test_nondefault_repo_with_blank_identifier_destruction
53 Repository.delete_all
53 Repository.delete_all
54
54
55 repo1 = Repository::Git.new(
55 repo1 = Repository::Git.new(
56 :project => @project,
56 :project => @project,
57 :url => REPOSITORY_PATH,
57 :url => REPOSITORY_PATH,
58 :identifier => '',
58 :identifier => '',
59 :is_default => true
59 :is_default => true
60 )
60 )
61 assert repo1.save
61 assert repo1.save
62 repo1.fetch_changesets
62 repo1.fetch_changesets
63
63
64 repo2 = Repository::Git.new(
64 repo2 = Repository::Git.new(
65 :project => @project,
65 :project => @project,
66 :url => REPOSITORY_PATH,
66 :url => REPOSITORY_PATH,
67 :identifier => 'repo2',
67 :identifier => 'repo2',
68 :is_default => true
68 :is_default => true
69 )
69 )
70 assert repo2.save
70 assert repo2.save
71 repo2.fetch_changesets
71 repo2.fetch_changesets
72
72
73 repo1.reload
73 repo1.reload
74 repo2.reload
74 repo2.reload
75 assert !repo1.is_default?
75 assert !repo1.is_default?
76 assert repo2.is_default?
76 assert repo2.is_default?
77
77
78 assert_difference 'Repository.count', -1 do
78 assert_difference 'Repository.count', -1 do
79 repo1.destroy
79 repo1.destroy
80 end
80 end
81 end
81 end
82
82
83 def test_blank_path_to_repository_error_message
83 def test_blank_path_to_repository_error_message
84 set_language_if_valid 'en'
84 set_language_if_valid 'en'
85 repo = Repository::Git.new(
85 repo = Repository::Git.new(
86 :project => @project,
86 :project => @project,
87 :identifier => 'test'
87 :identifier => 'test'
88 )
88 )
89 assert !repo.save
89 assert !repo.save
90 assert_include "Path to repository cannot be blank",
90 assert_include "Path to repository cannot be blank",
91 repo.errors.full_messages
91 repo.errors.full_messages
92 end
92 end
93
93
94 def test_blank_path_to_repository_error_message_fr
94 def test_blank_path_to_repository_error_message_fr
95 set_language_if_valid 'fr'
95 set_language_if_valid 'fr'
96 str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
96 str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)".force_encoding('UTF-8')
97 repo = Repository::Git.new(
97 repo = Repository::Git.new(
98 :project => @project,
98 :project => @project,
99 :url => "",
99 :url => "",
100 :identifier => 'test',
100 :identifier => 'test',
101 :path_encoding => ''
101 :path_encoding => ''
102 )
102 )
103 assert !repo.save
103 assert !repo.save
104 assert_include str, repo.errors.full_messages
104 assert_include str, repo.errors.full_messages
105 end
105 end
106
106
107 if File.directory?(REPOSITORY_PATH)
107 if File.directory?(REPOSITORY_PATH)
108 ## Ruby uses ANSI api to fork a process on Windows.
108 ## Ruby uses ANSI api to fork a process on Windows.
109 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
109 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
110 ## and these are incompatible with ASCII.
110 ## and these are incompatible with ASCII.
111 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
111 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
112 ## http://code.google.com/p/msysgit/issues/detail?id=80
112 ## http://code.google.com/p/msysgit/issues/detail?id=80
113 ## So, Latin-1 path tests fail on Japanese Windows
113 ## So, Latin-1 path tests fail on Japanese Windows
114 WINDOWS_PASS = (Redmine::Platform.mswin? &&
114 WINDOWS_PASS = (Redmine::Platform.mswin? &&
115 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
115 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
116 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
116 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
117
117
118 def test_scm_available
118 def test_scm_available
119 klass = Repository::Git
119 klass = Repository::Git
120 assert_equal "Git", klass.scm_name
120 assert_equal "Git", klass.scm_name
121 assert klass.scm_adapter_class
121 assert klass.scm_adapter_class
122 assert_not_equal "", klass.scm_command
122 assert_not_equal "", klass.scm_command
123 assert_equal true, klass.scm_available
123 assert_equal true, klass.scm_available
124 end
124 end
125
125
126 def test_entries
126 def test_entries
127 entries = @repository.entries
127 entries = @repository.entries
128 assert_kind_of Redmine::Scm::Adapters::Entries, entries
128 assert_kind_of Redmine::Scm::Adapters::Entries, entries
129 end
129 end
130
130
131 def test_fetch_changesets_from_scratch
131 def test_fetch_changesets_from_scratch
132 assert_nil @repository.extra_info
132 assert_nil @repository.extra_info
133
133
134 assert_equal 0, @repository.changesets.count
134 assert_equal 0, @repository.changesets.count
135 @repository.fetch_changesets
135 @repository.fetch_changesets
136 @project.reload
136 @project.reload
137
137
138 assert_equal NUM_REV, @repository.changesets.count
138 assert_equal NUM_REV, @repository.changesets.count
139 assert_equal 39, @repository.filechanges.count
139 assert_equal 39, @repository.filechanges.count
140
140
141 commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518")
141 commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518")
142 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
142 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
143 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
143 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
144 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
144 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
145 assert_equal User.find_by_login('jsmith'), commit.user
145 assert_equal User.find_by_login('jsmith'), commit.user
146 # TODO: add a commit with commit time <> author time to the test repository
146 # TODO: add a commit with commit time <> author time to the test repository
147 assert_equal Time.gm(2007, 12, 14, 9, 22, 52), commit.committed_on
147 assert_equal Time.gm(2007, 12, 14, 9, 22, 52), commit.committed_on
148 assert_equal "2007-12-14".to_date, commit.commit_date
148 assert_equal "2007-12-14".to_date, commit.commit_date
149 assert_equal 3, commit.filechanges.count
149 assert_equal 3, commit.filechanges.count
150 change = commit.filechanges.sort_by(&:path).first
150 change = commit.filechanges.sort_by(&:path).first
151 assert_equal "README", change.path
151 assert_equal "README", change.path
152 assert_equal nil, change.from_path
152 assert_nil change.from_path
153 assert_equal "A", change.action
153 assert_equal "A", change.action
154
154
155 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
155 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
156 end
156 end
157
157
158 def test_fetch_changesets_incremental
158 def test_fetch_changesets_incremental
159 assert_equal 0, @repository.changesets.count
159 assert_equal 0, @repository.changesets.count
160 @repository.fetch_changesets
160 @repository.fetch_changesets
161 @project.reload
161 @project.reload
162 assert_equal NUM_REV, @repository.changesets.count
162 assert_equal NUM_REV, @repository.changesets.count
163 extra_info_heads = @repository.extra_info["heads"].dup
163 extra_info_heads = @repository.extra_info["heads"].dup
164 assert_equal NUM_HEAD, extra_info_heads.size
164 assert_equal NUM_HEAD, extra_info_heads.size
165 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
165 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
166 assert_equal 4, extra_info_heads.size
166 assert_equal 4, extra_info_heads.size
167
167
168 del_revs = [
168 del_revs = [
169 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
169 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
170 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
170 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
171 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
171 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
172 "deff712f05a90d96edbd70facc47d944be5897e3",
172 "deff712f05a90d96edbd70facc47d944be5897e3",
173 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
173 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
174 "7e61ac704deecde634b51e59daa8110435dcb3da",
174 "7e61ac704deecde634b51e59daa8110435dcb3da",
175 ]
175 ]
176 @repository.changesets.each do |rev|
176 @repository.changesets.each do |rev|
177 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
177 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
178 end
178 end
179 @project.reload
179 @project.reload
180 cs1 = @repository.changesets
180 cs1 = @repository.changesets
181 assert_equal NUM_REV - 6, cs1.count
181 assert_equal NUM_REV - 6, cs1.count
182 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
182 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
183 h = {}
183 h = {}
184 h["heads"] = extra_info_heads
184 h["heads"] = extra_info_heads
185 @repository.merge_extra_info(h)
185 @repository.merge_extra_info(h)
186 @repository.save
186 @repository.save
187 @project.reload
187 @project.reload
188 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
188 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
189 @repository.fetch_changesets
189 @repository.fetch_changesets
190 @project.reload
190 @project.reload
191 assert_equal NUM_REV, @repository.changesets.count
191 assert_equal NUM_REV, @repository.changesets.count
192 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
192 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
193 assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
193 assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
194 end
194 end
195
195
196 def test_fetch_changesets_history_editing
196 def test_fetch_changesets_history_editing
197 assert_equal 0, @repository.changesets.count
197 assert_equal 0, @repository.changesets.count
198 @repository.fetch_changesets
198 @repository.fetch_changesets
199 @project.reload
199 @project.reload
200 assert_equal NUM_REV, @repository.changesets.count
200 assert_equal NUM_REV, @repository.changesets.count
201 extra_info_heads = @repository.extra_info["heads"].dup
201 extra_info_heads = @repository.extra_info["heads"].dup
202 assert_equal NUM_HEAD, extra_info_heads.size
202 assert_equal NUM_HEAD, extra_info_heads.size
203 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
203 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
204 assert_equal 4, extra_info_heads.size
204 assert_equal 4, extra_info_heads.size
205
205
206 del_revs = [
206 del_revs = [
207 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
207 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
208 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
208 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
209 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
209 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
210 "deff712f05a90d96edbd70facc47d944be5897e3",
210 "deff712f05a90d96edbd70facc47d944be5897e3",
211 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
211 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
212 "7e61ac704deecde634b51e59daa8110435dcb3da",
212 "7e61ac704deecde634b51e59daa8110435dcb3da",
213 ]
213 ]
214 @repository.changesets.each do |rev|
214 @repository.changesets.each do |rev|
215 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
215 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
216 end
216 end
217 @project.reload
217 @project.reload
218 assert_equal NUM_REV - 6, @repository.changesets.count
218 assert_equal NUM_REV - 6, @repository.changesets.count
219
219
220 c = Changeset.new(:repository => @repository,
220 c = Changeset.new(:repository => @repository,
221 :committed_on => Time.now,
221 :committed_on => Time.now,
222 :revision => "abcd1234efgh",
222 :revision => "abcd1234efgh",
223 :scmid => "abcd1234efgh",
223 :scmid => "abcd1234efgh",
224 :comments => 'test')
224 :comments => 'test')
225 assert c.save
225 assert c.save
226 @project.reload
226 @project.reload
227 assert_equal NUM_REV - 5, @repository.changesets.count
227 assert_equal NUM_REV - 5, @repository.changesets.count
228
228
229 extra_info_heads << "1234abcd5678"
229 extra_info_heads << "1234abcd5678"
230 h = {}
230 h = {}
231 h["heads"] = extra_info_heads
231 h["heads"] = extra_info_heads
232 @repository.merge_extra_info(h)
232 @repository.merge_extra_info(h)
233 @repository.save
233 @repository.save
234 @project.reload
234 @project.reload
235 h1 = @repository.extra_info["heads"].dup
235 h1 = @repository.extra_info["heads"].dup
236 assert h1.index("1234abcd5678")
236 assert h1.index("1234abcd5678")
237 assert_equal 5, h1.size
237 assert_equal 5, h1.size
238
238
239 @repository.fetch_changesets
239 @repository.fetch_changesets
240 @project.reload
240 @project.reload
241 assert_equal NUM_REV - 5, @repository.changesets.count
241 assert_equal NUM_REV - 5, @repository.changesets.count
242 h2 = @repository.extra_info["heads"].dup
242 h2 = @repository.extra_info["heads"].dup
243 assert_equal h1, h2
243 assert_equal h1, h2
244 end
244 end
245
245
246 def test_clear_changesets_should_keep_report_last_commit
246 def test_clear_changesets_should_keep_report_last_commit
247 assert_nil @repository.extra_info
247 assert_nil @repository.extra_info
248 @repository.report_last_commit = "1"
248 @repository.report_last_commit = "1"
249 @repository.save
249 @repository.save
250 @repository.send(:clear_changesets)
250 @repository.send(:clear_changesets)
251
251
252 assert_equal true, @repository.report_last_commit
252 assert_equal true, @repository.report_last_commit
253 end
253 end
254
254
255 def test_refetch_after_clear_changesets
255 def test_refetch_after_clear_changesets
256 assert_nil @repository.extra_info
256 assert_nil @repository.extra_info
257 assert_equal 0, @repository.changesets.count
257 assert_equal 0, @repository.changesets.count
258 @repository.fetch_changesets
258 @repository.fetch_changesets
259 @project.reload
259 @project.reload
260 assert_equal NUM_REV, @repository.changesets.count
260 assert_equal NUM_REV, @repository.changesets.count
261
261
262 @repository.send(:clear_changesets)
262 @repository.send(:clear_changesets)
263 @project.reload
263 @project.reload
264 assert_equal 0, @repository.changesets.count
264 assert_equal 0, @repository.changesets.count
265
265
266 @repository.fetch_changesets
266 @repository.fetch_changesets
267 @project.reload
267 @project.reload
268 assert_equal NUM_REV, @repository.changesets.count
268 assert_equal NUM_REV, @repository.changesets.count
269 end
269 end
270
270
271 def test_parents
271 def test_parents
272 assert_equal 0, @repository.changesets.count
272 assert_equal 0, @repository.changesets.count
273 @repository.fetch_changesets
273 @repository.fetch_changesets
274 @project.reload
274 @project.reload
275 assert_equal NUM_REV, @repository.changesets.count
275 assert_equal NUM_REV, @repository.changesets.count
276 r1 = @repository.find_changeset_by_name("7234cb2750b63")
276 r1 = @repository.find_changeset_by_name("7234cb2750b63")
277 assert_equal [], r1.parents
277 assert_equal [], r1.parents
278 r2 = @repository.find_changeset_by_name("899a15dba03a3")
278 r2 = @repository.find_changeset_by_name("899a15dba03a3")
279 assert_equal 1, r2.parents.length
279 assert_equal 1, r2.parents.length
280 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
280 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
281 r2.parents[0].identifier
281 r2.parents[0].identifier
282 r3 = @repository.find_changeset_by_name("32ae898b720c2")
282 r3 = @repository.find_changeset_by_name("32ae898b720c2")
283 assert_equal 2, r3.parents.length
283 assert_equal 2, r3.parents.length
284 r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort
284 r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort
285 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0]
285 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0]
286 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1]
286 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1]
287 end
287 end
288
288
289 def test_db_consistent_ordering_init
289 def test_db_consistent_ordering_init
290 assert_nil @repository.extra_info
290 assert_nil @repository.extra_info
291 assert_equal 0, @repository.changesets.count
291 assert_equal 0, @repository.changesets.count
292 @repository.fetch_changesets
292 @repository.fetch_changesets
293 @project.reload
293 @project.reload
294 assert_equal 1, @repository.extra_info["db_consistent"]["ordering"]
294 assert_equal 1, @repository.extra_info["db_consistent"]["ordering"]
295 end
295 end
296
296
297 def test_db_consistent_ordering_before_1_2
297 def test_db_consistent_ordering_before_1_2
298 assert_nil @repository.extra_info
298 assert_nil @repository.extra_info
299 assert_equal 0, @repository.changesets.count
299 assert_equal 0, @repository.changesets.count
300 @repository.fetch_changesets
300 @repository.fetch_changesets
301 @project.reload
301 @project.reload
302 assert_equal NUM_REV, @repository.changesets.count
302 assert_equal NUM_REV, @repository.changesets.count
303 assert_not_nil @repository.extra_info
303 assert_not_nil @repository.extra_info
304 h = {}
304 h = {}
305 h["heads"] = []
305 h["heads"] = []
306 h["branches"] = {}
306 h["branches"] = {}
307 h["db_consistent"] = {}
307 h["db_consistent"] = {}
308 @repository.merge_extra_info(h)
308 @repository.merge_extra_info(h)
309 @repository.save
309 @repository.save
310 assert_equal NUM_REV, @repository.changesets.count
310 assert_equal NUM_REV, @repository.changesets.count
311 @repository.fetch_changesets
311 @repository.fetch_changesets
312 @project.reload
312 @project.reload
313 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
313 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
314
314
315 extra_info_heads = @repository.extra_info["heads"].dup
315 extra_info_heads = @repository.extra_info["heads"].dup
316 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
316 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
317 del_revs = [
317 del_revs = [
318 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
318 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
319 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
319 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
320 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
320 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
321 "deff712f05a90d96edbd70facc47d944be5897e3",
321 "deff712f05a90d96edbd70facc47d944be5897e3",
322 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
322 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
323 "7e61ac704deecde634b51e59daa8110435dcb3da",
323 "7e61ac704deecde634b51e59daa8110435dcb3da",
324 ]
324 ]
325 @repository.changesets.each do |rev|
325 @repository.changesets.each do |rev|
326 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
326 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
327 end
327 end
328 @project.reload
328 @project.reload
329 cs1 = @repository.changesets
329 cs1 = @repository.changesets
330 assert_equal NUM_REV - 6, cs1.count
330 assert_equal NUM_REV - 6, cs1.count
331 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
331 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
332
332
333 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
333 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
334 h = {}
334 h = {}
335 h["heads"] = extra_info_heads
335 h["heads"] = extra_info_heads
336 @repository.merge_extra_info(h)
336 @repository.merge_extra_info(h)
337 @repository.save
337 @repository.save
338 @project.reload
338 @project.reload
339 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
339 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
340 @repository.fetch_changesets
340 @repository.fetch_changesets
341 @project.reload
341 @project.reload
342 assert_equal NUM_REV, @repository.changesets.count
342 assert_equal NUM_REV, @repository.changesets.count
343 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
343 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
344
344
345 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
345 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
346 end
346 end
347
347
348 def test_heads_from_branches_hash
348 def test_heads_from_branches_hash
349 assert_nil @repository.extra_info
349 assert_nil @repository.extra_info
350 assert_equal 0, @repository.changesets.count
350 assert_equal 0, @repository.changesets.count
351 assert_equal [], @repository.heads_from_branches_hash
351 assert_equal [], @repository.heads_from_branches_hash
352 h = {}
352 h = {}
353 h["branches"] = {}
353 h["branches"] = {}
354 h["branches"]["test1"] = {}
354 h["branches"]["test1"] = {}
355 h["branches"]["test1"]["last_scmid"] = "1234abcd"
355 h["branches"]["test1"]["last_scmid"] = "1234abcd"
356 h["branches"]["test2"] = {}
356 h["branches"]["test2"] = {}
357 h["branches"]["test2"]["last_scmid"] = "abcd1234"
357 h["branches"]["test2"]["last_scmid"] = "abcd1234"
358 @repository.merge_extra_info(h)
358 @repository.merge_extra_info(h)
359 @repository.save
359 @repository.save
360 @project.reload
360 @project.reload
361 assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort
361 assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort
362 end
362 end
363
363
364 def test_latest_changesets
364 def test_latest_changesets
365 assert_equal 0, @repository.changesets.count
365 assert_equal 0, @repository.changesets.count
366 @repository.fetch_changesets
366 @repository.fetch_changesets
367 @project.reload
367 @project.reload
368 assert_equal NUM_REV, @repository.changesets.count
368 assert_equal NUM_REV, @repository.changesets.count
369 # with limit
369 # with limit
370 changesets = @repository.latest_changesets('', 'master', 2)
370 changesets = @repository.latest_changesets('', 'master', 2)
371 assert_equal 2, changesets.size
371 assert_equal 2, changesets.size
372
372
373 # with path
373 # with path
374 changesets = @repository.latest_changesets('images', 'master')
374 changesets = @repository.latest_changesets('images', 'master')
375 assert_equal [
375 assert_equal [
376 'deff712f05a90d96edbd70facc47d944be5897e3',
376 'deff712f05a90d96edbd70facc47d944be5897e3',
377 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
377 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
378 '7234cb2750b63f47bff735edc50a1c0a433c2518',
378 '7234cb2750b63f47bff735edc50a1c0a433c2518',
379 ], changesets.collect(&:revision)
379 ], changesets.collect(&:revision)
380
380
381 changesets = @repository.latest_changesets('README', nil)
381 changesets = @repository.latest_changesets('README', nil)
382 assert_equal [
382 assert_equal [
383 '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf',
383 '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf',
384 '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8',
384 '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8',
385 '713f4944648826f558cf548222f813dabe7cbb04',
385 '713f4944648826f558cf548222f813dabe7cbb04',
386 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
386 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
387 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
387 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
388 '7234cb2750b63f47bff735edc50a1c0a433c2518',
388 '7234cb2750b63f47bff735edc50a1c0a433c2518',
389 ], changesets.collect(&:revision)
389 ], changesets.collect(&:revision)
390
390
391 # with path, revision and limit
391 # with path, revision and limit
392 changesets = @repository.latest_changesets('images', '899a15dba')
392 changesets = @repository.latest_changesets('images', '899a15dba')
393 assert_equal [
393 assert_equal [
394 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
394 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
395 '7234cb2750b63f47bff735edc50a1c0a433c2518',
395 '7234cb2750b63f47bff735edc50a1c0a433c2518',
396 ], changesets.collect(&:revision)
396 ], changesets.collect(&:revision)
397
397
398 changesets = @repository.latest_changesets('images', '899a15dba', 1)
398 changesets = @repository.latest_changesets('images', '899a15dba', 1)
399 assert_equal [
399 assert_equal [
400 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
400 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
401 ], changesets.collect(&:revision)
401 ], changesets.collect(&:revision)
402
402
403 changesets = @repository.latest_changesets('README', '899a15dba')
403 changesets = @repository.latest_changesets('README', '899a15dba')
404 assert_equal [
404 assert_equal [
405 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
405 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
406 '7234cb2750b63f47bff735edc50a1c0a433c2518',
406 '7234cb2750b63f47bff735edc50a1c0a433c2518',
407 ], changesets.collect(&:revision)
407 ], changesets.collect(&:revision)
408
408
409 changesets = @repository.latest_changesets('README', '899a15dba', 1)
409 changesets = @repository.latest_changesets('README', '899a15dba', 1)
410 assert_equal [
410 assert_equal [
411 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
411 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
412 ], changesets.collect(&:revision)
412 ], changesets.collect(&:revision)
413
413
414 # with path, tag and limit
414 # with path, tag and limit
415 changesets = @repository.latest_changesets('images', 'tag01.annotated')
415 changesets = @repository.latest_changesets('images', 'tag01.annotated')
416 assert_equal [
416 assert_equal [
417 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
417 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
418 '7234cb2750b63f47bff735edc50a1c0a433c2518',
418 '7234cb2750b63f47bff735edc50a1c0a433c2518',
419 ], changesets.collect(&:revision)
419 ], changesets.collect(&:revision)
420
420
421 changesets = @repository.latest_changesets('images', 'tag01.annotated', 1)
421 changesets = @repository.latest_changesets('images', 'tag01.annotated', 1)
422 assert_equal [
422 assert_equal [
423 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
423 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
424 ], changesets.collect(&:revision)
424 ], changesets.collect(&:revision)
425
425
426 changesets = @repository.latest_changesets('README', 'tag01.annotated')
426 changesets = @repository.latest_changesets('README', 'tag01.annotated')
427 assert_equal [
427 assert_equal [
428 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
428 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
429 '7234cb2750b63f47bff735edc50a1c0a433c2518',
429 '7234cb2750b63f47bff735edc50a1c0a433c2518',
430 ], changesets.collect(&:revision)
430 ], changesets.collect(&:revision)
431
431
432 changesets = @repository.latest_changesets('README', 'tag01.annotated', 1)
432 changesets = @repository.latest_changesets('README', 'tag01.annotated', 1)
433 assert_equal [
433 assert_equal [
434 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
434 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
435 ], changesets.collect(&:revision)
435 ], changesets.collect(&:revision)
436
436
437 # with path, branch and limit
437 # with path, branch and limit
438 changesets = @repository.latest_changesets('images', 'test_branch')
438 changesets = @repository.latest_changesets('images', 'test_branch')
439 assert_equal [
439 assert_equal [
440 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
440 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
441 '7234cb2750b63f47bff735edc50a1c0a433c2518',
441 '7234cb2750b63f47bff735edc50a1c0a433c2518',
442 ], changesets.collect(&:revision)
442 ], changesets.collect(&:revision)
443
443
444 changesets = @repository.latest_changesets('images', 'test_branch', 1)
444 changesets = @repository.latest_changesets('images', 'test_branch', 1)
445 assert_equal [
445 assert_equal [
446 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
446 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
447 ], changesets.collect(&:revision)
447 ], changesets.collect(&:revision)
448
448
449 changesets = @repository.latest_changesets('README', 'test_branch')
449 changesets = @repository.latest_changesets('README', 'test_branch')
450 assert_equal [
450 assert_equal [
451 '713f4944648826f558cf548222f813dabe7cbb04',
451 '713f4944648826f558cf548222f813dabe7cbb04',
452 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
452 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
453 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
453 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
454 '7234cb2750b63f47bff735edc50a1c0a433c2518',
454 '7234cb2750b63f47bff735edc50a1c0a433c2518',
455 ], changesets.collect(&:revision)
455 ], changesets.collect(&:revision)
456
456
457 changesets = @repository.latest_changesets('README', 'test_branch', 2)
457 changesets = @repository.latest_changesets('README', 'test_branch', 2)
458 assert_equal [
458 assert_equal [
459 '713f4944648826f558cf548222f813dabe7cbb04',
459 '713f4944648826f558cf548222f813dabe7cbb04',
460 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
460 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
461 ], changesets.collect(&:revision)
461 ], changesets.collect(&:revision)
462
462
463 if WINDOWS_PASS
463 if WINDOWS_PASS
464 puts WINDOWS_SKIP_STR
464 puts WINDOWS_SKIP_STR
465 elsif JRUBY_SKIP
465 elsif JRUBY_SKIP
466 puts JRUBY_SKIP_STR
466 puts JRUBY_SKIP_STR
467 else
467 else
468 # latin-1 encoding path
468 # latin-1 encoding path
469 changesets = @repository.latest_changesets(
469 changesets = @repository.latest_changesets(
470 "latin-1-dir/test-#{CHAR_1_HEX}-2.txt", '64f1f3e89')
470 "latin-1-dir/test-#{CHAR_1_HEX}-2.txt", '64f1f3e89')
471 assert_equal [
471 assert_equal [
472 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
472 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
473 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e',
473 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e',
474 ], changesets.collect(&:revision)
474 ], changesets.collect(&:revision)
475
475
476 changesets = @repository.latest_changesets(
476 changesets = @repository.latest_changesets(
477 "latin-1-dir/test-#{CHAR_1_HEX}-2.txt", '64f1f3e89', 1)
477 "latin-1-dir/test-#{CHAR_1_HEX}-2.txt", '64f1f3e89', 1)
478 assert_equal [
478 assert_equal [
479 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
479 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
480 ], changesets.collect(&:revision)
480 ], changesets.collect(&:revision)
481 end
481 end
482 end
482 end
483
483
484 def test_latest_changesets_latin_1_dir
484 def test_latest_changesets_latin_1_dir
485 if WINDOWS_PASS
485 if WINDOWS_PASS
486 puts WINDOWS_SKIP_STR
486 puts WINDOWS_SKIP_STR
487 elsif JRUBY_SKIP
487 elsif JRUBY_SKIP
488 puts JRUBY_SKIP_STR
488 puts JRUBY_SKIP_STR
489 else
489 else
490 assert_equal 0, @repository.changesets.count
490 assert_equal 0, @repository.changesets.count
491 @repository.fetch_changesets
491 @repository.fetch_changesets
492 @project.reload
492 @project.reload
493 assert_equal NUM_REV, @repository.changesets.count
493 assert_equal NUM_REV, @repository.changesets.count
494 changesets = @repository.latest_changesets(
494 changesets = @repository.latest_changesets(
495 "latin-1-dir/test-#{CHAR_1_HEX}-subdir", '1ca7f5ed')
495 "latin-1-dir/test-#{CHAR_1_HEX}-subdir", '1ca7f5ed')
496 assert_equal [
496 assert_equal [
497 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127',
497 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127',
498 ], changesets.collect(&:revision)
498 ], changesets.collect(&:revision)
499 end
499 end
500 end
500 end
501
501
502 def test_find_changeset_by_name
502 def test_find_changeset_by_name
503 assert_equal 0, @repository.changesets.count
503 assert_equal 0, @repository.changesets.count
504 @repository.fetch_changesets
504 @repository.fetch_changesets
505 @project.reload
505 @project.reload
506 assert_equal NUM_REV, @repository.changesets.count
506 assert_equal NUM_REV, @repository.changesets.count
507 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
507 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
508 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
508 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
509 @repository.find_changeset_by_name(r).revision
509 @repository.find_changeset_by_name(r).revision
510 end
510 end
511 end
511 end
512
512
513 def test_find_changeset_by_empty_name
513 def test_find_changeset_by_empty_name
514 assert_equal 0, @repository.changesets.count
514 assert_equal 0, @repository.changesets.count
515 @repository.fetch_changesets
515 @repository.fetch_changesets
516 @project.reload
516 @project.reload
517 assert_equal NUM_REV, @repository.changesets.count
517 assert_equal NUM_REV, @repository.changesets.count
518 ['', ' ', nil].each do |r|
518 ['', ' ', nil].each do |r|
519 assert_nil @repository.find_changeset_by_name(r)
519 assert_nil @repository.find_changeset_by_name(r)
520 end
520 end
521 end
521 end
522
522
523 def test_identifier
523 def test_identifier
524 assert_equal 0, @repository.changesets.count
524 assert_equal 0, @repository.changesets.count
525 @repository.fetch_changesets
525 @repository.fetch_changesets
526 @project.reload
526 @project.reload
527 assert_equal NUM_REV, @repository.changesets.count
527 assert_equal NUM_REV, @repository.changesets.count
528 c = @repository.changesets.find_by_revision(
528 c = @repository.changesets.find_by_revision(
529 '7234cb2750b63f47bff735edc50a1c0a433c2518')
529 '7234cb2750b63f47bff735edc50a1c0a433c2518')
530 assert_equal c.scmid, c.identifier
530 assert_equal c.scmid, c.identifier
531 end
531 end
532
532
533 def test_format_identifier
533 def test_format_identifier
534 assert_equal 0, @repository.changesets.count
534 assert_equal 0, @repository.changesets.count
535 @repository.fetch_changesets
535 @repository.fetch_changesets
536 @project.reload
536 @project.reload
537 assert_equal NUM_REV, @repository.changesets.count
537 assert_equal NUM_REV, @repository.changesets.count
538 c = @repository.changesets.find_by_revision(
538 c = @repository.changesets.find_by_revision(
539 '7234cb2750b63f47bff735edc50a1c0a433c2518')
539 '7234cb2750b63f47bff735edc50a1c0a433c2518')
540 assert_equal '7234cb27', c.format_identifier
540 assert_equal '7234cb27', c.format_identifier
541 end
541 end
542
542
543 def test_activities
543 def test_activities
544 c = Changeset.new(:repository => @repository,
544 c = Changeset.new(:repository => @repository,
545 :committed_on => Time.now,
545 :committed_on => Time.now,
546 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
546 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
547 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
547 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
548 :comments => 'test')
548 :comments => 'test')
549 assert c.event_title.include?('abc7234c:')
549 assert c.event_title.include?('abc7234c:')
550 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
550 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
551 end
551 end
552
552
553 def test_log_utf8
553 def test_log_utf8
554 assert_equal 0, @repository.changesets.count
554 assert_equal 0, @repository.changesets.count
555 @repository.fetch_changesets
555 @repository.fetch_changesets
556 @project.reload
556 @project.reload
557 assert_equal NUM_REV, @repository.changesets.count
557 assert_equal NUM_REV, @repository.changesets.count
558 c = @repository.changesets.find_by_revision(
558 c = @repository.changesets.find_by_revision(
559 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
559 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
560 assert_equal "#{FELIX_HEX} <felix@fachschaften.org>", c.committer
560 assert_equal "#{FELIX_HEX} <felix@fachschaften.org>", c.committer
561 end
561 end
562
562
563 def test_previous
563 def test_previous
564 assert_equal 0, @repository.changesets.count
564 assert_equal 0, @repository.changesets.count
565 @repository.fetch_changesets
565 @repository.fetch_changesets
566 @project.reload
566 @project.reload
567 assert_equal NUM_REV, @repository.changesets.count
567 assert_equal NUM_REV, @repository.changesets.count
568 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
568 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
569 changeset = @repository.find_changeset_by_name(r1)
569 changeset = @repository.find_changeset_by_name(r1)
570 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
570 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
571 assert_equal @repository.find_changeset_by_name(r2), changeset.previous
571 assert_equal @repository.find_changeset_by_name(r2), changeset.previous
572 end
572 end
573 end
573 end
574 end
574 end
575
575
576 def test_previous_nil
576 def test_previous_nil
577 assert_equal 0, @repository.changesets.count
577 assert_equal 0, @repository.changesets.count
578 @repository.fetch_changesets
578 @repository.fetch_changesets
579 @project.reload
579 @project.reload
580 assert_equal NUM_REV, @repository.changesets.count
580 assert_equal NUM_REV, @repository.changesets.count
581 %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1|
581 %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1|
582 changeset = @repository.find_changeset_by_name(r1)
582 changeset = @repository.find_changeset_by_name(r1)
583 assert_nil changeset.previous
583 assert_nil changeset.previous
584 end
584 end
585 end
585 end
586
586
587 def test_next
587 def test_next
588 assert_equal 0, @repository.changesets.count
588 assert_equal 0, @repository.changesets.count
589 @repository.fetch_changesets
589 @repository.fetch_changesets
590 @project.reload
590 @project.reload
591 assert_equal NUM_REV, @repository.changesets.count
591 assert_equal NUM_REV, @repository.changesets.count
592 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
592 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
593 changeset = @repository.find_changeset_by_name(r2)
593 changeset = @repository.find_changeset_by_name(r2)
594 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
594 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
595 assert_equal @repository.find_changeset_by_name(r1), changeset.next
595 assert_equal @repository.find_changeset_by_name(r1), changeset.next
596 end
596 end
597 end
597 end
598 end
598 end
599
599
600 def test_next_nil
600 def test_next_nil
601 assert_equal 0, @repository.changesets.count
601 assert_equal 0, @repository.changesets.count
602 @repository.fetch_changesets
602 @repository.fetch_changesets
603 @project.reload
603 @project.reload
604 assert_equal NUM_REV, @repository.changesets.count
604 assert_equal NUM_REV, @repository.changesets.count
605 %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1|
605 %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1|
606 changeset = @repository.find_changeset_by_name(r1)
606 changeset = @repository.find_changeset_by_name(r1)
607 assert_nil changeset.next
607 assert_nil changeset.next
608 end
608 end
609 end
609 end
610 else
610 else
611 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
611 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
612 def test_fake; assert true end
612 def test_fake; assert true end
613 end
613 end
614 end
614 end
@@ -1,1240 +1,1240
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UserTest < ActiveSupport::TestCase
20 class UserTest < ActiveSupport::TestCase
21 fixtures :users, :email_addresses, :members, :projects, :roles, :member_roles, :auth_sources,
21 fixtures :users, :email_addresses, :members, :projects, :roles, :member_roles, :auth_sources,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :projects_trackers,
23 :projects_trackers,
24 :watchers,
24 :watchers,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :journals, :journal_details,
26 :journals, :journal_details,
27 :groups_users,
27 :groups_users,
28 :enabled_modules,
28 :enabled_modules,
29 :tokens
29 :tokens
30
30
31 include Redmine::I18n
31 include Redmine::I18n
32
32
33 def setup
33 def setup
34 @admin = User.find(1)
34 @admin = User.find(1)
35 @jsmith = User.find(2)
35 @jsmith = User.find(2)
36 @dlopper = User.find(3)
36 @dlopper = User.find(3)
37 end
37 end
38
38
39 def test_sorted_scope_should_sort_user_by_display_name
39 def test_sorted_scope_should_sort_user_by_display_name
40 # Use .active to ignore anonymous with localized display name
40 # Use .active to ignore anonymous with localized display name
41 assert_equal User.active.map(&:name).map(&:downcase).sort,
41 assert_equal User.active.map(&:name).map(&:downcase).sort,
42 User.active.sorted.map(&:name).map(&:downcase)
42 User.active.sorted.map(&:name).map(&:downcase)
43 end
43 end
44
44
45 def test_generate
45 def test_generate
46 User.generate!(:firstname => 'Testing connection')
46 User.generate!(:firstname => 'Testing connection')
47 User.generate!(:firstname => 'Testing connection')
47 User.generate!(:firstname => 'Testing connection')
48 assert_equal 2, User.where(:firstname => 'Testing connection').count
48 assert_equal 2, User.where(:firstname => 'Testing connection').count
49 end
49 end
50
50
51 def test_truth
51 def test_truth
52 assert_kind_of User, @jsmith
52 assert_kind_of User, @jsmith
53 end
53 end
54
54
55 def test_should_validate_status
55 def test_should_validate_status
56 user = User.new
56 user = User.new
57 user.status = 0
57 user.status = 0
58
58
59 assert !user.save
59 assert !user.save
60 assert_include I18n.translate('activerecord.errors.messages.invalid'), user.errors[:status]
60 assert_include I18n.translate('activerecord.errors.messages.invalid'), user.errors[:status]
61 end
61 end
62
62
63 def test_mail_should_be_stripped
63 def test_mail_should_be_stripped
64 u = User.new
64 u = User.new
65 u.mail = " foo@bar.com "
65 u.mail = " foo@bar.com "
66 assert_equal "foo@bar.com", u.mail
66 assert_equal "foo@bar.com", u.mail
67 end
67 end
68
68
69 def test_should_create_email_address
69 def test_should_create_email_address
70 u = User.new(:firstname => "new", :lastname => "user")
70 u = User.new(:firstname => "new", :lastname => "user")
71 u.login = "create_email_address"
71 u.login = "create_email_address"
72 u.mail = "defaultemail@somenet.foo"
72 u.mail = "defaultemail@somenet.foo"
73 assert u.save
73 assert u.save
74 u.reload
74 u.reload
75 assert u.email_address
75 assert u.email_address
76 assert_equal "defaultemail@somenet.foo", u.email_address.address
76 assert_equal "defaultemail@somenet.foo", u.email_address.address
77 assert_equal true, u.email_address.is_default
77 assert_equal true, u.email_address.is_default
78 assert_equal true, u.email_address.notify
78 assert_equal true, u.email_address.notify
79 end
79 end
80
80
81 def test_should_not_create_user_without_mail
81 def test_should_not_create_user_without_mail
82 set_language_if_valid 'en'
82 set_language_if_valid 'en'
83 u = User.new(:firstname => "new", :lastname => "user")
83 u = User.new(:firstname => "new", :lastname => "user")
84 u.login = "user_without_mail"
84 u.login = "user_without_mail"
85 assert !u.save
85 assert !u.save
86 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
86 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
87 end
87 end
88
88
89 def test_should_not_create_user_with_blank_mail
89 def test_should_not_create_user_with_blank_mail
90 set_language_if_valid 'en'
90 set_language_if_valid 'en'
91 u = User.new(:firstname => "new", :lastname => "user")
91 u = User.new(:firstname => "new", :lastname => "user")
92 u.login = "user_with_blank_mail"
92 u.login = "user_with_blank_mail"
93 u.mail = ''
93 u.mail = ''
94 assert !u.save
94 assert !u.save
95 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
95 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
96 end
96 end
97
97
98 def test_should_not_update_user_with_blank_mail
98 def test_should_not_update_user_with_blank_mail
99 set_language_if_valid 'en'
99 set_language_if_valid 'en'
100 u = User.find(2)
100 u = User.find(2)
101 u.mail = ''
101 u.mail = ''
102 assert !u.save
102 assert !u.save
103 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
103 assert_equal ["Email #{I18n.translate('activerecord.errors.messages.blank')}"], u.errors.full_messages
104 end
104 end
105
105
106 def test_login_length_validation
106 def test_login_length_validation
107 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
107 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
108 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
108 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
109 assert !user.valid?
109 assert !user.valid?
110
110
111 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
111 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
112 assert user.valid?
112 assert user.valid?
113 assert user.save
113 assert user.save
114 end
114 end
115
115
116 def test_generate_password_should_respect_minimum_password_length
116 def test_generate_password_should_respect_minimum_password_length
117 with_settings :password_min_length => 15 do
117 with_settings :password_min_length => 15 do
118 user = User.generate!(:generate_password => true)
118 user = User.generate!(:generate_password => true)
119 assert user.password.length >= 15
119 assert user.password.length >= 15
120 end
120 end
121 end
121 end
122
122
123 def test_generate_password_should_not_generate_password_with_less_than_10_characters
123 def test_generate_password_should_not_generate_password_with_less_than_10_characters
124 with_settings :password_min_length => 4 do
124 with_settings :password_min_length => 4 do
125 user = User.generate!(:generate_password => true)
125 user = User.generate!(:generate_password => true)
126 assert user.password.length >= 10
126 assert user.password.length >= 10
127 end
127 end
128 end
128 end
129
129
130 def test_generate_password_on_create_should_set_password
130 def test_generate_password_on_create_should_set_password
131 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
131 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
132 user.login = "newuser"
132 user.login = "newuser"
133 user.generate_password = true
133 user.generate_password = true
134 assert user.save
134 assert user.save
135
135
136 password = user.password
136 password = user.password
137 assert user.check_password?(password)
137 assert user.check_password?(password)
138 end
138 end
139
139
140 def test_generate_password_on_update_should_update_password
140 def test_generate_password_on_update_should_update_password
141 user = User.find(2)
141 user = User.find(2)
142 hash = user.hashed_password
142 hash = user.hashed_password
143 user.generate_password = true
143 user.generate_password = true
144 assert user.save
144 assert user.save
145
145
146 password = user.password
146 password = user.password
147 assert user.check_password?(password)
147 assert user.check_password?(password)
148 assert_not_equal hash, user.reload.hashed_password
148 assert_not_equal hash, user.reload.hashed_password
149 end
149 end
150
150
151 def test_create
151 def test_create
152 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
152 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
153
153
154 user.login = "jsmith"
154 user.login = "jsmith"
155 user.password, user.password_confirmation = "password", "password"
155 user.password, user.password_confirmation = "password", "password"
156 # login uniqueness
156 # login uniqueness
157 assert !user.save
157 assert !user.save
158 assert_equal 1, user.errors.count
158 assert_equal 1, user.errors.count
159
159
160 user.login = "newuser"
160 user.login = "newuser"
161 user.password, user.password_confirmation = "password", "pass"
161 user.password, user.password_confirmation = "password", "pass"
162 # password confirmation
162 # password confirmation
163 assert !user.save
163 assert !user.save
164 assert_equal 1, user.errors.count
164 assert_equal 1, user.errors.count
165
165
166 user.password, user.password_confirmation = "password", "password"
166 user.password, user.password_confirmation = "password", "password"
167 assert user.save
167 assert user.save
168 end
168 end
169
169
170 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
170 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
171 @user1 = User.generate!
171 @user1 = User.generate!
172 assert_equal 'only_my_events', @user1.mail_notification
172 assert_equal 'only_my_events', @user1.mail_notification
173 with_settings :default_notification_option => 'all' do
173 with_settings :default_notification_option => 'all' do
174 @user2 = User.generate!
174 @user2 = User.generate!
175 assert_equal 'all', @user2.mail_notification
175 assert_equal 'all', @user2.mail_notification
176 end
176 end
177 end
177 end
178
178
179 def test_user_login_should_be_case_insensitive
179 def test_user_login_should_be_case_insensitive
180 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
180 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
181 u.login = 'newuser'
181 u.login = 'newuser'
182 u.password, u.password_confirmation = "password", "password"
182 u.password, u.password_confirmation = "password", "password"
183 assert u.save
183 assert u.save
184 u = User.new(:firstname => "Similar", :lastname => "User",
184 u = User.new(:firstname => "Similar", :lastname => "User",
185 :mail => "similaruser@somenet.foo")
185 :mail => "similaruser@somenet.foo")
186 u.login = 'NewUser'
186 u.login = 'NewUser'
187 u.password, u.password_confirmation = "password", "password"
187 u.password, u.password_confirmation = "password", "password"
188 assert !u.save
188 assert !u.save
189 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
189 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
190 end
190 end
191
191
192 def test_mail_uniqueness_should_not_be_case_sensitive
192 def test_mail_uniqueness_should_not_be_case_sensitive
193 set_language_if_valid 'en'
193 set_language_if_valid 'en'
194 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
194 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
195 u.login = 'newuser1'
195 u.login = 'newuser1'
196 u.password, u.password_confirmation = "password", "password"
196 u.password, u.password_confirmation = "password", "password"
197 assert u.save
197 assert u.save
198
198
199 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
199 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
200 u.login = 'newuser2'
200 u.login = 'newuser2'
201 u.password, u.password_confirmation = "password", "password"
201 u.password, u.password_confirmation = "password", "password"
202 assert !u.save
202 assert !u.save
203 assert_include "Email #{I18n.translate('activerecord.errors.messages.taken')}", u.errors.full_messages
203 assert_include "Email #{I18n.translate('activerecord.errors.messages.taken')}", u.errors.full_messages
204 end
204 end
205
205
206 def test_update
206 def test_update
207 assert_equal "admin", @admin.login
207 assert_equal "admin", @admin.login
208 @admin.login = "john"
208 @admin.login = "john"
209 assert @admin.save, @admin.errors.full_messages.join("; ")
209 assert @admin.save, @admin.errors.full_messages.join("; ")
210 @admin.reload
210 @admin.reload
211 assert_equal "john", @admin.login
211 assert_equal "john", @admin.login
212 end
212 end
213
213
214 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
214 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
215 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
215 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
216 u1.login = 'newuser1'
216 u1.login = 'newuser1'
217 assert u1.save
217 assert u1.save
218
218
219 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
219 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
220 u2.login = 'newuser1'
220 u2.login = 'newuser1'
221 assert u2.save(:validate => false)
221 assert u2.save(:validate => false)
222
222
223 user = User.find(u2.id)
223 user = User.find(u2.id)
224 user.firstname = "firstname"
224 user.firstname = "firstname"
225 assert user.save, "Save failed"
225 assert user.save, "Save failed"
226 end
226 end
227
227
228 def test_destroy_should_delete_members_and_roles
228 def test_destroy_should_delete_members_and_roles
229 members = Member.where(:user_id => 2)
229 members = Member.where(:user_id => 2)
230 ms = members.count
230 ms = members.count
231 rs = members.collect(&:roles).flatten.size
231 rs = members.collect(&:roles).flatten.size
232 assert ms > 0
232 assert ms > 0
233 assert rs > 0
233 assert rs > 0
234 assert_difference 'Member.count', - ms do
234 assert_difference 'Member.count', - ms do
235 assert_difference 'MemberRole.count', - rs do
235 assert_difference 'MemberRole.count', - rs do
236 User.find(2).destroy
236 User.find(2).destroy
237 end
237 end
238 end
238 end
239 assert_nil User.find_by_id(2)
239 assert_nil User.find_by_id(2)
240 assert_equal 0, Member.where(:user_id => 2).count
240 assert_equal 0, Member.where(:user_id => 2).count
241 end
241 end
242
242
243 def test_destroy_should_update_attachments
243 def test_destroy_should_update_attachments
244 attachment = Attachment.create!(:container => Project.find(1),
244 attachment = Attachment.create!(:container => Project.find(1),
245 :file => uploaded_test_file("testfile.txt", "text/plain"),
245 :file => uploaded_test_file("testfile.txt", "text/plain"),
246 :author_id => 2)
246 :author_id => 2)
247
247
248 User.find(2).destroy
248 User.find(2).destroy
249 assert_nil User.find_by_id(2)
249 assert_nil User.find_by_id(2)
250 assert_equal User.anonymous, attachment.reload.author
250 assert_equal User.anonymous, attachment.reload.author
251 end
251 end
252
252
253 def test_destroy_should_update_comments
253 def test_destroy_should_update_comments
254 comment = Comment.create!(
254 comment = Comment.create!(
255 :commented => News.create!(:project_id => 1,
255 :commented => News.create!(:project_id => 1,
256 :author_id => 1, :title => 'foo', :description => 'foo'),
256 :author_id => 1, :title => 'foo', :description => 'foo'),
257 :author => User.find(2),
257 :author => User.find(2),
258 :comments => 'foo'
258 :comments => 'foo'
259 )
259 )
260
260
261 User.find(2).destroy
261 User.find(2).destroy
262 assert_nil User.find_by_id(2)
262 assert_nil User.find_by_id(2)
263 assert_equal User.anonymous, comment.reload.author
263 assert_equal User.anonymous, comment.reload.author
264 end
264 end
265
265
266 def test_destroy_should_update_issues
266 def test_destroy_should_update_issues
267 issue = Issue.create!(:project_id => 1, :author_id => 2,
267 issue = Issue.create!(:project_id => 1, :author_id => 2,
268 :tracker_id => 1, :subject => 'foo')
268 :tracker_id => 1, :subject => 'foo')
269
269
270 User.find(2).destroy
270 User.find(2).destroy
271 assert_nil User.find_by_id(2)
271 assert_nil User.find_by_id(2)
272 assert_equal User.anonymous, issue.reload.author
272 assert_equal User.anonymous, issue.reload.author
273 end
273 end
274
274
275 def test_destroy_should_unassign_issues
275 def test_destroy_should_unassign_issues
276 issue = Issue.create!(:project_id => 1, :author_id => 1,
276 issue = Issue.create!(:project_id => 1, :author_id => 1,
277 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
277 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
278
278
279 User.find(2).destroy
279 User.find(2).destroy
280 assert_nil User.find_by_id(2)
280 assert_nil User.find_by_id(2)
281 assert_nil issue.reload.assigned_to
281 assert_nil issue.reload.assigned_to
282 end
282 end
283
283
284 def test_destroy_should_update_journals
284 def test_destroy_should_update_journals
285 issue = Issue.create!(:project_id => 1, :author_id => 2,
285 issue = Issue.create!(:project_id => 1, :author_id => 2,
286 :tracker_id => 1, :subject => 'foo')
286 :tracker_id => 1, :subject => 'foo')
287 issue.init_journal(User.find(2), "update")
287 issue.init_journal(User.find(2), "update")
288 issue.save!
288 issue.save!
289
289
290 User.find(2).destroy
290 User.find(2).destroy
291 assert_nil User.find_by_id(2)
291 assert_nil User.find_by_id(2)
292 assert_equal User.anonymous, issue.journals.first.reload.user
292 assert_equal User.anonymous, issue.journals.first.reload.user
293 end
293 end
294
294
295 def test_destroy_should_update_journal_details_old_value
295 def test_destroy_should_update_journal_details_old_value
296 issue = Issue.create!(:project_id => 1, :author_id => 1,
296 issue = Issue.create!(:project_id => 1, :author_id => 1,
297 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
297 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
298 issue.init_journal(User.find(1), "update")
298 issue.init_journal(User.find(1), "update")
299 issue.assigned_to_id = nil
299 issue.assigned_to_id = nil
300 assert_difference 'JournalDetail.count' do
300 assert_difference 'JournalDetail.count' do
301 issue.save!
301 issue.save!
302 end
302 end
303 journal_detail = JournalDetail.order('id DESC').first
303 journal_detail = JournalDetail.order('id DESC').first
304 assert_equal '2', journal_detail.old_value
304 assert_equal '2', journal_detail.old_value
305
305
306 User.find(2).destroy
306 User.find(2).destroy
307 assert_nil User.find_by_id(2)
307 assert_nil User.find_by_id(2)
308 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
308 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
309 end
309 end
310
310
311 def test_destroy_should_update_journal_details_value
311 def test_destroy_should_update_journal_details_value
312 issue = Issue.create!(:project_id => 1, :author_id => 1,
312 issue = Issue.create!(:project_id => 1, :author_id => 1,
313 :tracker_id => 1, :subject => 'foo')
313 :tracker_id => 1, :subject => 'foo')
314 issue.init_journal(User.find(1), "update")
314 issue.init_journal(User.find(1), "update")
315 issue.assigned_to_id = 2
315 issue.assigned_to_id = 2
316 assert_difference 'JournalDetail.count' do
316 assert_difference 'JournalDetail.count' do
317 issue.save!
317 issue.save!
318 end
318 end
319 journal_detail = JournalDetail.order('id DESC').first
319 journal_detail = JournalDetail.order('id DESC').first
320 assert_equal '2', journal_detail.value
320 assert_equal '2', journal_detail.value
321
321
322 User.find(2).destroy
322 User.find(2).destroy
323 assert_nil User.find_by_id(2)
323 assert_nil User.find_by_id(2)
324 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
324 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
325 end
325 end
326
326
327 def test_destroy_should_update_messages
327 def test_destroy_should_update_messages
328 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
328 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
329 message = Message.create!(:board_id => board.id, :author_id => 2,
329 message = Message.create!(:board_id => board.id, :author_id => 2,
330 :subject => 'foo', :content => 'foo')
330 :subject => 'foo', :content => 'foo')
331 User.find(2).destroy
331 User.find(2).destroy
332 assert_nil User.find_by_id(2)
332 assert_nil User.find_by_id(2)
333 assert_equal User.anonymous, message.reload.author
333 assert_equal User.anonymous, message.reload.author
334 end
334 end
335
335
336 def test_destroy_should_update_news
336 def test_destroy_should_update_news
337 news = News.create!(:project_id => 1, :author_id => 2,
337 news = News.create!(:project_id => 1, :author_id => 2,
338 :title => 'foo', :description => 'foo')
338 :title => 'foo', :description => 'foo')
339 User.find(2).destroy
339 User.find(2).destroy
340 assert_nil User.find_by_id(2)
340 assert_nil User.find_by_id(2)
341 assert_equal User.anonymous, news.reload.author
341 assert_equal User.anonymous, news.reload.author
342 end
342 end
343
343
344 def test_destroy_should_delete_private_queries
344 def test_destroy_should_delete_private_queries
345 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
345 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
346 query.project_id = 1
346 query.project_id = 1
347 query.user_id = 2
347 query.user_id = 2
348 query.save!
348 query.save!
349
349
350 User.find(2).destroy
350 User.find(2).destroy
351 assert_nil User.find_by_id(2)
351 assert_nil User.find_by_id(2)
352 assert_nil Query.find_by_id(query.id)
352 assert_nil Query.find_by_id(query.id)
353 end
353 end
354
354
355 def test_destroy_should_update_public_queries
355 def test_destroy_should_update_public_queries
356 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
356 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
357 query.project_id = 1
357 query.project_id = 1
358 query.user_id = 2
358 query.user_id = 2
359 query.save!
359 query.save!
360
360
361 User.find(2).destroy
361 User.find(2).destroy
362 assert_nil User.find_by_id(2)
362 assert_nil User.find_by_id(2)
363 assert_equal User.anonymous, query.reload.user
363 assert_equal User.anonymous, query.reload.user
364 end
364 end
365
365
366 def test_destroy_should_update_time_entries
366 def test_destroy_should_update_time_entries
367 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
367 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
368 :activity => TimeEntryActivity.create!(:name => 'foo'))
368 :activity => TimeEntryActivity.create!(:name => 'foo'))
369 entry.project_id = 1
369 entry.project_id = 1
370 entry.user_id = 2
370 entry.user_id = 2
371 entry.save!
371 entry.save!
372
372
373 User.find(2).destroy
373 User.find(2).destroy
374 assert_nil User.find_by_id(2)
374 assert_nil User.find_by_id(2)
375 assert_equal User.anonymous, entry.reload.user
375 assert_equal User.anonymous, entry.reload.user
376 end
376 end
377
377
378 def test_destroy_should_delete_tokens
378 def test_destroy_should_delete_tokens
379 token = Token.create!(:user_id => 2, :value => 'foo')
379 token = Token.create!(:user_id => 2, :value => 'foo')
380
380
381 User.find(2).destroy
381 User.find(2).destroy
382 assert_nil User.find_by_id(2)
382 assert_nil User.find_by_id(2)
383 assert_nil Token.find_by_id(token.id)
383 assert_nil Token.find_by_id(token.id)
384 end
384 end
385
385
386 def test_destroy_should_delete_watchers
386 def test_destroy_should_delete_watchers
387 issue = Issue.create!(:project_id => 1, :author_id => 1,
387 issue = Issue.create!(:project_id => 1, :author_id => 1,
388 :tracker_id => 1, :subject => 'foo')
388 :tracker_id => 1, :subject => 'foo')
389 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
389 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
390
390
391 User.find(2).destroy
391 User.find(2).destroy
392 assert_nil User.find_by_id(2)
392 assert_nil User.find_by_id(2)
393 assert_nil Watcher.find_by_id(watcher.id)
393 assert_nil Watcher.find_by_id(watcher.id)
394 end
394 end
395
395
396 def test_destroy_should_update_wiki_contents
396 def test_destroy_should_update_wiki_contents
397 wiki_content = WikiContent.create!(
397 wiki_content = WikiContent.create!(
398 :text => 'foo',
398 :text => 'foo',
399 :author_id => 2,
399 :author_id => 2,
400 :page => WikiPage.create!(:title => 'Foo',
400 :page => WikiPage.create!(:title => 'Foo',
401 :wiki => Wiki.create!(:project_id => 3,
401 :wiki => Wiki.create!(:project_id => 3,
402 :start_page => 'Start'))
402 :start_page => 'Start'))
403 )
403 )
404 wiki_content.text = 'bar'
404 wiki_content.text = 'bar'
405 assert_difference 'WikiContent::Version.count' do
405 assert_difference 'WikiContent::Version.count' do
406 wiki_content.save!
406 wiki_content.save!
407 end
407 end
408
408
409 User.find(2).destroy
409 User.find(2).destroy
410 assert_nil User.find_by_id(2)
410 assert_nil User.find_by_id(2)
411 assert_equal User.anonymous, wiki_content.reload.author
411 assert_equal User.anonymous, wiki_content.reload.author
412 wiki_content.versions.each do |version|
412 wiki_content.versions.each do |version|
413 assert_equal User.anonymous, version.reload.author
413 assert_equal User.anonymous, version.reload.author
414 end
414 end
415 end
415 end
416
416
417 def test_destroy_should_nullify_issue_categories
417 def test_destroy_should_nullify_issue_categories
418 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
418 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
419
419
420 User.find(2).destroy
420 User.find(2).destroy
421 assert_nil User.find_by_id(2)
421 assert_nil User.find_by_id(2)
422 assert_nil category.reload.assigned_to_id
422 assert_nil category.reload.assigned_to_id
423 end
423 end
424
424
425 def test_destroy_should_nullify_changesets
425 def test_destroy_should_nullify_changesets
426 changeset = Changeset.create!(
426 changeset = Changeset.create!(
427 :repository => Repository::Subversion.create!(
427 :repository => Repository::Subversion.create!(
428 :project_id => 1,
428 :project_id => 1,
429 :url => 'file:///tmp',
429 :url => 'file:///tmp',
430 :identifier => 'tmp'
430 :identifier => 'tmp'
431 ),
431 ),
432 :revision => '12',
432 :revision => '12',
433 :committed_on => Time.now,
433 :committed_on => Time.now,
434 :committer => 'jsmith'
434 :committer => 'jsmith'
435 )
435 )
436 assert_equal 2, changeset.user_id
436 assert_equal 2, changeset.user_id
437
437
438 User.find(2).destroy
438 User.find(2).destroy
439 assert_nil User.find_by_id(2)
439 assert_nil User.find_by_id(2)
440 assert_nil changeset.reload.user_id
440 assert_nil changeset.reload.user_id
441 end
441 end
442
442
443 def test_anonymous_user_should_not_be_destroyable
443 def test_anonymous_user_should_not_be_destroyable
444 assert_no_difference 'User.count' do
444 assert_no_difference 'User.count' do
445 assert_equal false, User.anonymous.destroy
445 assert_equal false, User.anonymous.destroy
446 end
446 end
447 end
447 end
448
448
449 def test_password_change_should_destroy_tokens
449 def test_password_change_should_destroy_tokens
450 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
450 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
451 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
451 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
452
452
453 user = User.find(2)
453 user = User.find(2)
454 user.password, user.password_confirmation = "a new password", "a new password"
454 user.password, user.password_confirmation = "a new password", "a new password"
455 assert user.save
455 assert user.save
456
456
457 assert_nil Token.find_by_id(recovery_token.id)
457 assert_nil Token.find_by_id(recovery_token.id)
458 assert_nil Token.find_by_id(autologin_token.id)
458 assert_nil Token.find_by_id(autologin_token.id)
459 end
459 end
460
460
461 def test_mail_change_should_destroy_tokens
461 def test_mail_change_should_destroy_tokens
462 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
462 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
463 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
463 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
464
464
465 user = User.find(2)
465 user = User.find(2)
466 user.mail = "user@somwehere.com"
466 user.mail = "user@somwehere.com"
467 assert user.save
467 assert user.save
468
468
469 assert_nil Token.find_by_id(recovery_token.id)
469 assert_nil Token.find_by_id(recovery_token.id)
470 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
470 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
471 end
471 end
472
472
473 def test_change_on_other_fields_should_not_destroy_tokens
473 def test_change_on_other_fields_should_not_destroy_tokens
474 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
474 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
475 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
475 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
476
476
477 user = User.find(2)
477 user = User.find(2)
478 user.firstname = "Bobby"
478 user.firstname = "Bobby"
479 assert user.save
479 assert user.save
480
480
481 assert_equal recovery_token, Token.find_by_id(recovery_token.id)
481 assert_equal recovery_token, Token.find_by_id(recovery_token.id)
482 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
482 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
483 end
483 end
484
484
485 def test_validate_login_presence
485 def test_validate_login_presence
486 @admin.login = ""
486 @admin.login = ""
487 assert !@admin.save
487 assert !@admin.save
488 assert_equal 1, @admin.errors.count
488 assert_equal 1, @admin.errors.count
489 end
489 end
490
490
491 def test_validate_mail_notification_inclusion
491 def test_validate_mail_notification_inclusion
492 u = User.new
492 u = User.new
493 u.mail_notification = 'foo'
493 u.mail_notification = 'foo'
494 u.save
494 u.save
495 assert_not_equal [], u.errors[:mail_notification]
495 assert_not_equal [], u.errors[:mail_notification]
496 end
496 end
497
497
498 def test_password
498 def test_password
499 user = User.try_to_login("admin", "admin")
499 user = User.try_to_login("admin", "admin")
500 assert_kind_of User, user
500 assert_kind_of User, user
501 assert_equal "admin", user.login
501 assert_equal "admin", user.login
502 user.password = "hello123"
502 user.password = "hello123"
503 assert user.save
503 assert user.save
504
504
505 user = User.try_to_login("admin", "hello123")
505 user = User.try_to_login("admin", "hello123")
506 assert_kind_of User, user
506 assert_kind_of User, user
507 assert_equal "admin", user.login
507 assert_equal "admin", user.login
508 end
508 end
509
509
510 def test_validate_password_length
510 def test_validate_password_length
511 with_settings :password_min_length => '100' do
511 with_settings :password_min_length => '100' do
512 user = User.new(:firstname => "new100",
512 user = User.new(:firstname => "new100",
513 :lastname => "user100", :mail => "newuser100@somenet.foo")
513 :lastname => "user100", :mail => "newuser100@somenet.foo")
514 user.login = "newuser100"
514 user.login = "newuser100"
515 user.password, user.password_confirmation = "password100", "password100"
515 user.password, user.password_confirmation = "password100", "password100"
516 assert !user.save
516 assert !user.save
517 assert_equal 1, user.errors.count
517 assert_equal 1, user.errors.count
518 end
518 end
519 end
519 end
520
520
521 def test_name_format
521 def test_name_format
522 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
522 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
523 assert_equal 'Smith, John', @jsmith.name(:lastname_comma_firstname)
523 assert_equal 'Smith, John', @jsmith.name(:lastname_comma_firstname)
524 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
524 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
525 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
525 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
526 end
526 end
527
527
528 def test_name_should_use_setting_as_default_format
528 def test_name_should_use_setting_as_default_format
529 with_settings :user_format => :firstname_lastname do
529 with_settings :user_format => :firstname_lastname do
530 assert_equal 'John Smith', @jsmith.reload.name
530 assert_equal 'John Smith', @jsmith.reload.name
531 end
531 end
532 with_settings :user_format => :username do
532 with_settings :user_format => :username do
533 assert_equal 'jsmith', @jsmith.reload.name
533 assert_equal 'jsmith', @jsmith.reload.name
534 end
534 end
535 with_settings :user_format => :lastname do
535 with_settings :user_format => :lastname do
536 assert_equal 'Smith', @jsmith.reload.name
536 assert_equal 'Smith', @jsmith.reload.name
537 end
537 end
538 end
538 end
539
539
540 def test_today_should_return_the_day_according_to_user_time_zone
540 def test_today_should_return_the_day_according_to_user_time_zone
541 preference = User.find(1).pref
541 preference = User.find(1).pref
542 date = Date.new(2012, 05, 15)
542 date = Date.new(2012, 05, 15)
543 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
543 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
544 Date.stubs(:today).returns(date)
544 Date.stubs(:today).returns(date)
545 Time.stubs(:now).returns(time)
545 Time.stubs(:now).returns(time)
546
546
547 preference.update_attribute :time_zone, 'Baku' # UTC+4
547 preference.update_attribute :time_zone, 'Baku' # UTC+4
548 assert_equal '2012-05-16', User.find(1).today.to_s
548 assert_equal '2012-05-16', User.find(1).today.to_s
549
549
550 preference.update_attribute :time_zone, 'La Paz' # UTC-4
550 preference.update_attribute :time_zone, 'La Paz' # UTC-4
551 assert_equal '2012-05-15', User.find(1).today.to_s
551 assert_equal '2012-05-15', User.find(1).today.to_s
552
552
553 preference.update_attribute :time_zone, ''
553 preference.update_attribute :time_zone, ''
554 assert_equal '2012-05-15', User.find(1).today.to_s
554 assert_equal '2012-05-15', User.find(1).today.to_s
555 end
555 end
556
556
557 def test_time_to_date_should_return_the_date_according_to_user_time_zone
557 def test_time_to_date_should_return_the_date_according_to_user_time_zone
558 preference = User.find(1).pref
558 preference = User.find(1).pref
559 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
559 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
560
560
561 preference.update_attribute :time_zone, 'Baku' # UTC+4
561 preference.update_attribute :time_zone, 'Baku' # UTC+4
562 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
562 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
563
563
564 preference.update_attribute :time_zone, 'La Paz' # UTC-4
564 preference.update_attribute :time_zone, 'La Paz' # UTC-4
565 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
565 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
566
566
567 preference.update_attribute :time_zone, ''
567 preference.update_attribute :time_zone, ''
568 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
568 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
569 end
569 end
570
570
571 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
571 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
572 with_settings :user_format => 'lastname_comma_firstname' do
572 with_settings :user_format => 'lastname_comma_firstname' do
573 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
573 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
574 User.fields_for_order_statement
574 User.fields_for_order_statement
575 end
575 end
576 end
576 end
577
577
578 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
578 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
579 with_settings :user_format => 'lastname_firstname' do
579 with_settings :user_format => 'lastname_firstname' do
580 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
580 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
581 User.fields_for_order_statement('authors')
581 User.fields_for_order_statement('authors')
582 end
582 end
583 end
583 end
584
584
585 def test_fields_for_order_statement_with_blank_format_should_return_default
585 def test_fields_for_order_statement_with_blank_format_should_return_default
586 with_settings :user_format => '' do
586 with_settings :user_format => '' do
587 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
587 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
588 User.fields_for_order_statement
588 User.fields_for_order_statement
589 end
589 end
590 end
590 end
591
591
592 def test_fields_for_order_statement_with_invalid_format_should_return_default
592 def test_fields_for_order_statement_with_invalid_format_should_return_default
593 with_settings :user_format => 'foo' do
593 with_settings :user_format => 'foo' do
594 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
594 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
595 User.fields_for_order_statement
595 User.fields_for_order_statement
596 end
596 end
597 end
597 end
598
598
599 test ".try_to_login with good credentials should return the user" do
599 test ".try_to_login with good credentials should return the user" do
600 user = User.try_to_login("admin", "admin")
600 user = User.try_to_login("admin", "admin")
601 assert_kind_of User, user
601 assert_kind_of User, user
602 assert_equal "admin", user.login
602 assert_equal "admin", user.login
603 end
603 end
604
604
605 test ".try_to_login with wrong credentials should return nil" do
605 test ".try_to_login with wrong credentials should return nil" do
606 assert_nil User.try_to_login("admin", "foo")
606 assert_nil User.try_to_login("admin", "foo")
607 end
607 end
608
608
609 def test_try_to_login_with_locked_user_should_return_nil
609 def test_try_to_login_with_locked_user_should_return_nil
610 @jsmith.status = User::STATUS_LOCKED
610 @jsmith.status = User::STATUS_LOCKED
611 @jsmith.save!
611 @jsmith.save!
612
612
613 user = User.try_to_login("jsmith", "jsmith")
613 user = User.try_to_login("jsmith", "jsmith")
614 assert_equal nil, user
614 assert_nil user
615 end
615 end
616
616
617 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
617 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
618 @jsmith.status = User::STATUS_LOCKED
618 @jsmith.status = User::STATUS_LOCKED
619 @jsmith.save!
619 @jsmith.save!
620
620
621 user = User.try_to_login("jsmith", "jsmith", false)
621 user = User.try_to_login("jsmith", "jsmith", false)
622 assert_equal @jsmith, user
622 assert_equal @jsmith, user
623 end
623 end
624
624
625 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
625 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
626 user = User.try_to_login("AdMin", "admin")
626 user = User.try_to_login("AdMin", "admin")
627 assert_kind_of User, user
627 assert_kind_of User, user
628 assert_equal "admin", user.login
628 assert_equal "admin", user.login
629 end
629 end
630
630
631 test ".try_to_login should select the exact matching user first" do
631 test ".try_to_login should select the exact matching user first" do
632 case_sensitive_user = User.generate! do |user|
632 case_sensitive_user = User.generate! do |user|
633 user.password = "admin123"
633 user.password = "admin123"
634 end
634 end
635 # bypass validations to make it appear like existing data
635 # bypass validations to make it appear like existing data
636 case_sensitive_user.update_attribute(:login, 'ADMIN')
636 case_sensitive_user.update_attribute(:login, 'ADMIN')
637
637
638 user = User.try_to_login("ADMIN", "admin123")
638 user = User.try_to_login("ADMIN", "admin123")
639 assert_kind_of User, user
639 assert_kind_of User, user
640 assert_equal "ADMIN", user.login
640 assert_equal "ADMIN", user.login
641 end
641 end
642
642
643 if ldap_configured?
643 if ldap_configured?
644 test "#try_to_login using LDAP with failed connection to the LDAP server" do
644 test "#try_to_login using LDAP with failed connection to the LDAP server" do
645 auth_source = AuthSourceLdap.find(1)
645 auth_source = AuthSourceLdap.find(1)
646 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
646 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
647
647
648 assert_equal nil, User.try_to_login('edavis', 'wrong')
648 assert_nil User.try_to_login('edavis', 'wrong')
649 end
649 end
650
650
651 test "#try_to_login using LDAP" do
651 test "#try_to_login using LDAP" do
652 assert_equal nil, User.try_to_login('edavis', 'wrong')
652 assert_nil User.try_to_login('edavis', 'wrong')
653 end
653 end
654
654
655 test "#try_to_login using LDAP binding with user's account" do
655 test "#try_to_login using LDAP binding with user's account" do
656 auth_source = AuthSourceLdap.find(1)
656 auth_source = AuthSourceLdap.find(1)
657 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
657 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
658 auth_source.account_password = ''
658 auth_source.account_password = ''
659 auth_source.save!
659 auth_source.save!
660
660
661 ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
661 ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
662 ldap_user.login = 'example1'
662 ldap_user.login = 'example1'
663 ldap_user.save!
663 ldap_user.save!
664
664
665 assert_equal ldap_user, User.try_to_login('example1', '123456')
665 assert_equal ldap_user, User.try_to_login('example1', '123456')
666 assert_nil User.try_to_login('example1', '11111')
666 assert_nil User.try_to_login('example1', '11111')
667 end
667 end
668
668
669 test "#try_to_login using LDAP on the fly registration" do
669 test "#try_to_login using LDAP on the fly registration" do
670 AuthSourceLdap.find(1).update_attribute :onthefly_register, true
670 AuthSourceLdap.find(1).update_attribute :onthefly_register, true
671
671
672 assert_difference('User.count') do
672 assert_difference('User.count') do
673 assert User.try_to_login('edavis', '123456')
673 assert User.try_to_login('edavis', '123456')
674 end
674 end
675
675
676 assert_no_difference('User.count') do
676 assert_no_difference('User.count') do
677 assert User.try_to_login('edavis', '123456')
677 assert User.try_to_login('edavis', '123456')
678 end
678 end
679
679
680 assert_nil User.try_to_login('example1', '11111')
680 assert_nil User.try_to_login('example1', '11111')
681 end
681 end
682
682
683 test "#try_to_login using LDAP on the fly registration and binding with user's account" do
683 test "#try_to_login using LDAP on the fly registration and binding with user's account" do
684 auth_source = AuthSourceLdap.find(1)
684 auth_source = AuthSourceLdap.find(1)
685 auth_source.update_attribute :onthefly_register, true
685 auth_source.update_attribute :onthefly_register, true
686 auth_source = AuthSourceLdap.find(1)
686 auth_source = AuthSourceLdap.find(1)
687 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
687 auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
688 auth_source.account_password = ''
688 auth_source.account_password = ''
689 auth_source.save!
689 auth_source.save!
690
690
691 assert_difference('User.count') do
691 assert_difference('User.count') do
692 assert User.try_to_login('example1', '123456')
692 assert User.try_to_login('example1', '123456')
693 end
693 end
694
694
695 assert_no_difference('User.count') do
695 assert_no_difference('User.count') do
696 assert User.try_to_login('example1', '123456')
696 assert User.try_to_login('example1', '123456')
697 end
697 end
698
698
699 assert_nil User.try_to_login('example1', '11111')
699 assert_nil User.try_to_login('example1', '11111')
700 end
700 end
701
701
702 else
702 else
703 puts "Skipping LDAP tests."
703 puts "Skipping LDAP tests."
704 end
704 end
705
705
706 def test_create_anonymous
706 def test_create_anonymous
707 AnonymousUser.delete_all
707 AnonymousUser.delete_all
708 anon = User.anonymous
708 anon = User.anonymous
709 assert !anon.new_record?
709 assert !anon.new_record?
710 assert_kind_of AnonymousUser, anon
710 assert_kind_of AnonymousUser, anon
711 end
711 end
712
712
713 def test_ensure_single_anonymous_user
713 def test_ensure_single_anonymous_user
714 AnonymousUser.delete_all
714 AnonymousUser.delete_all
715 anon1 = User.anonymous
715 anon1 = User.anonymous
716 assert !anon1.new_record?
716 assert !anon1.new_record?
717 assert_kind_of AnonymousUser, anon1
717 assert_kind_of AnonymousUser, anon1
718 anon2 = AnonymousUser.create(
718 anon2 = AnonymousUser.create(
719 :lastname => 'Anonymous', :firstname => '',
719 :lastname => 'Anonymous', :firstname => '',
720 :login => '', :status => 0)
720 :login => '', :status => 0)
721 assert_equal 1, anon2.errors.count
721 assert_equal 1, anon2.errors.count
722 end
722 end
723
723
724 def test_rss_key
724 def test_rss_key
725 assert_nil @jsmith.rss_token
725 assert_nil @jsmith.rss_token
726 key = @jsmith.rss_key
726 key = @jsmith.rss_key
727 assert_equal 40, key.length
727 assert_equal 40, key.length
728
728
729 @jsmith.reload
729 @jsmith.reload
730 assert_equal key, @jsmith.rss_key
730 assert_equal key, @jsmith.rss_key
731 end
731 end
732
732
733 def test_rss_key_should_not_be_generated_twice
733 def test_rss_key_should_not_be_generated_twice
734 assert_difference 'Token.count', 1 do
734 assert_difference 'Token.count', 1 do
735 key1 = @jsmith.rss_key
735 key1 = @jsmith.rss_key
736 key2 = @jsmith.rss_key
736 key2 = @jsmith.rss_key
737 assert_equal key1, key2
737 assert_equal key1, key2
738 end
738 end
739 end
739 end
740
740
741 def test_api_key_should_not_be_generated_twice
741 def test_api_key_should_not_be_generated_twice
742 assert_difference 'Token.count', 1 do
742 assert_difference 'Token.count', 1 do
743 key1 = @jsmith.api_key
743 key1 = @jsmith.api_key
744 key2 = @jsmith.api_key
744 key2 = @jsmith.api_key
745 assert_equal key1, key2
745 assert_equal key1, key2
746 end
746 end
747 end
747 end
748
748
749 test "#api_key should generate a new one if the user doesn't have one" do
749 test "#api_key should generate a new one if the user doesn't have one" do
750 user = User.generate!(:api_token => nil)
750 user = User.generate!(:api_token => nil)
751 assert_nil user.api_token
751 assert_nil user.api_token
752
752
753 key = user.api_key
753 key = user.api_key
754 assert_equal 40, key.length
754 assert_equal 40, key.length
755 user.reload
755 user.reload
756 assert_equal key, user.api_key
756 assert_equal key, user.api_key
757 end
757 end
758
758
759 test "#api_key should return the existing api token value" do
759 test "#api_key should return the existing api token value" do
760 user = User.generate!
760 user = User.generate!
761 token = Token.create!(:action => 'api')
761 token = Token.create!(:action => 'api')
762 user.api_token = token
762 user.api_token = token
763 assert user.save
763 assert user.save
764
764
765 assert_equal token.value, user.api_key
765 assert_equal token.value, user.api_key
766 end
766 end
767
767
768 test "#find_by_api_key should return nil if no matching key is found" do
768 test "#find_by_api_key should return nil if no matching key is found" do
769 assert_nil User.find_by_api_key('zzzzzzzzz')
769 assert_nil User.find_by_api_key('zzzzzzzzz')
770 end
770 end
771
771
772 test "#find_by_api_key should return nil if the key is found for an inactive user" do
772 test "#find_by_api_key should return nil if the key is found for an inactive user" do
773 user = User.generate!
773 user = User.generate!
774 user.status = User::STATUS_LOCKED
774 user.status = User::STATUS_LOCKED
775 token = Token.create!(:action => 'api')
775 token = Token.create!(:action => 'api')
776 user.api_token = token
776 user.api_token = token
777 user.save
777 user.save
778
778
779 assert_nil User.find_by_api_key(token.value)
779 assert_nil User.find_by_api_key(token.value)
780 end
780 end
781
781
782 test "#find_by_api_key should return the user if the key is found for an active user" do
782 test "#find_by_api_key should return the user if the key is found for an active user" do
783 user = User.generate!
783 user = User.generate!
784 token = Token.create!(:action => 'api')
784 token = Token.create!(:action => 'api')
785 user.api_token = token
785 user.api_token = token
786 user.save
786 user.save
787
787
788 assert_equal user, User.find_by_api_key(token.value)
788 assert_equal user, User.find_by_api_key(token.value)
789 end
789 end
790
790
791 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
791 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
792 user = User.find_by_login("admin")
792 user = User.find_by_login("admin")
793 user.password = "admin"
793 user.password = "admin"
794 assert user.save(:validate => false)
794 assert user.save(:validate => false)
795
795
796 assert_equal false, User.default_admin_account_changed?
796 assert_equal false, User.default_admin_account_changed?
797 end
797 end
798
798
799 def test_default_admin_account_changed_should_return_true_if_password_was_changed
799 def test_default_admin_account_changed_should_return_true_if_password_was_changed
800 user = User.find_by_login("admin")
800 user = User.find_by_login("admin")
801 user.password = "newpassword"
801 user.password = "newpassword"
802 user.save!
802 user.save!
803
803
804 assert_equal true, User.default_admin_account_changed?
804 assert_equal true, User.default_admin_account_changed?
805 end
805 end
806
806
807 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
807 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
808 user = User.find_by_login("admin")
808 user = User.find_by_login("admin")
809 user.password = "admin"
809 user.password = "admin"
810 user.status = User::STATUS_LOCKED
810 user.status = User::STATUS_LOCKED
811 assert user.save(:validate => false)
811 assert user.save(:validate => false)
812
812
813 assert_equal true, User.default_admin_account_changed?
813 assert_equal true, User.default_admin_account_changed?
814 end
814 end
815
815
816 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
816 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
817 user = User.find_by_login("admin")
817 user = User.find_by_login("admin")
818 user.destroy
818 user.destroy
819
819
820 assert_equal true, User.default_admin_account_changed?
820 assert_equal true, User.default_admin_account_changed?
821 end
821 end
822
822
823 def test_membership_with_project_should_return_membership
823 def test_membership_with_project_should_return_membership
824 project = Project.find(1)
824 project = Project.find(1)
825
825
826 membership = @jsmith.membership(project)
826 membership = @jsmith.membership(project)
827 assert_kind_of Member, membership
827 assert_kind_of Member, membership
828 assert_equal @jsmith, membership.user
828 assert_equal @jsmith, membership.user
829 assert_equal project, membership.project
829 assert_equal project, membership.project
830 end
830 end
831
831
832 def test_membership_with_project_id_should_return_membership
832 def test_membership_with_project_id_should_return_membership
833 project = Project.find(1)
833 project = Project.find(1)
834
834
835 membership = @jsmith.membership(1)
835 membership = @jsmith.membership(1)
836 assert_kind_of Member, membership
836 assert_kind_of Member, membership
837 assert_equal @jsmith, membership.user
837 assert_equal @jsmith, membership.user
838 assert_equal project, membership.project
838 assert_equal project, membership.project
839 end
839 end
840
840
841 def test_membership_for_non_member_should_return_nil
841 def test_membership_for_non_member_should_return_nil
842 project = Project.find(1)
842 project = Project.find(1)
843
843
844 user = User.generate!
844 user = User.generate!
845 membership = user.membership(1)
845 membership = user.membership(1)
846 assert_nil membership
846 assert_nil membership
847 end
847 end
848
848
849 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
849 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
850 roles = @jsmith.roles_for_project(Project.find(1))
850 roles = @jsmith.roles_for_project(Project.find(1))
851 assert_kind_of Role, roles.first
851 assert_kind_of Role, roles.first
852 assert_equal ["Manager"], roles.map(&:name)
852 assert_equal ["Manager"], roles.map(&:name)
853 end
853 end
854
854
855 def test_roles_for_project_with_member_on_private_project_should_return_roles
855 def test_roles_for_project_with_member_on_private_project_should_return_roles
856 Project.find(1).update_attribute :is_public, false
856 Project.find(1).update_attribute :is_public, false
857
857
858 roles = @jsmith.roles_for_project(Project.find(1))
858 roles = @jsmith.roles_for_project(Project.find(1))
859 assert_kind_of Role, roles.first
859 assert_kind_of Role, roles.first
860 assert_equal ["Manager"], roles.map(&:name)
860 assert_equal ["Manager"], roles.map(&:name)
861 end
861 end
862
862
863 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
863 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
864 set_language_if_valid 'en'
864 set_language_if_valid 'en'
865 roles = User.find(8).roles_for_project(Project.find(1))
865 roles = User.find(8).roles_for_project(Project.find(1))
866 assert_equal ["Non member"], roles.map(&:name)
866 assert_equal ["Non member"], roles.map(&:name)
867 end
867 end
868
868
869 def test_roles_for_project_with_non_member_with_public_project_and_override_should_return_override_roles
869 def test_roles_for_project_with_non_member_with_public_project_and_override_should_return_override_roles
870 project = Project.find(1)
870 project = Project.find(1)
871 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
871 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
872 roles = User.find(8).roles_for_project(project)
872 roles = User.find(8).roles_for_project(project)
873 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
873 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
874 end
874 end
875
875
876 def test_roles_for_project_with_non_member_with_private_project_should_return_no_roles
876 def test_roles_for_project_with_non_member_with_private_project_should_return_no_roles
877 Project.find(1).update_attribute :is_public, false
877 Project.find(1).update_attribute :is_public, false
878
878
879 roles = User.find(8).roles_for_project(Project.find(1))
879 roles = User.find(8).roles_for_project(Project.find(1))
880 assert_equal [], roles.map(&:name)
880 assert_equal [], roles.map(&:name)
881 end
881 end
882
882
883 def test_roles_for_project_with_non_member_with_private_project_and_override_should_return_no_roles
883 def test_roles_for_project_with_non_member_with_private_project_and_override_should_return_no_roles
884 project = Project.find(1)
884 project = Project.find(1)
885 project.update_attribute :is_public, false
885 project.update_attribute :is_public, false
886 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
886 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
887 roles = User.find(8).roles_for_project(project)
887 roles = User.find(8).roles_for_project(project)
888 assert_equal [], roles.map(&:name).sort
888 assert_equal [], roles.map(&:name).sort
889 end
889 end
890
890
891 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
891 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
892 set_language_if_valid 'en'
892 set_language_if_valid 'en'
893 roles = User.anonymous.roles_for_project(Project.find(1))
893 roles = User.anonymous.roles_for_project(Project.find(1))
894 assert_equal ["Anonymous"], roles.map(&:name)
894 assert_equal ["Anonymous"], roles.map(&:name)
895 end
895 end
896
896
897 def test_roles_for_project_with_anonymous_with_public_project_and_override_should_return_override_roles
897 def test_roles_for_project_with_anonymous_with_public_project_and_override_should_return_override_roles
898 project = Project.find(1)
898 project = Project.find(1)
899 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
899 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
900 roles = User.anonymous.roles_for_project(project)
900 roles = User.anonymous.roles_for_project(project)
901 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
901 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
902 end
902 end
903
903
904 def test_roles_for_project_with_anonymous_with_private_project_should_return_no_roles
904 def test_roles_for_project_with_anonymous_with_private_project_should_return_no_roles
905 Project.find(1).update_attribute :is_public, false
905 Project.find(1).update_attribute :is_public, false
906
906
907 roles = User.anonymous.roles_for_project(Project.find(1))
907 roles = User.anonymous.roles_for_project(Project.find(1))
908 assert_equal [], roles.map(&:name)
908 assert_equal [], roles.map(&:name)
909 end
909 end
910
910
911 def test_roles_for_project_with_anonymous_with_private_project_and_override_should_return_no_roles
911 def test_roles_for_project_with_anonymous_with_private_project_and_override_should_return_no_roles
912 project = Project.find(1)
912 project = Project.find(1)
913 project.update_attribute :is_public, false
913 project.update_attribute :is_public, false
914 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
914 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
915 roles = User.anonymous.roles_for_project(project)
915 roles = User.anonymous.roles_for_project(project)
916 assert_equal [], roles.map(&:name).sort
916 assert_equal [], roles.map(&:name).sort
917 end
917 end
918
918
919 def test_roles_for_project_should_be_unique
919 def test_roles_for_project_should_be_unique
920 m = Member.new(:user_id => 1, :project_id => 1)
920 m = Member.new(:user_id => 1, :project_id => 1)
921 m.member_roles.build(:role_id => 1)
921 m.member_roles.build(:role_id => 1)
922 m.member_roles.build(:role_id => 1)
922 m.member_roles.build(:role_id => 1)
923 m.save!
923 m.save!
924
924
925 user = User.find(1)
925 user = User.find(1)
926 project = Project.find(1)
926 project = Project.find(1)
927 assert_equal 1, user.roles_for_project(project).size
927 assert_equal 1, user.roles_for_project(project).size
928 assert_equal [1], user.roles_for_project(project).map(&:id)
928 assert_equal [1], user.roles_for_project(project).map(&:id)
929 end
929 end
930
930
931 def test_projects_by_role_for_user_with_role
931 def test_projects_by_role_for_user_with_role
932 user = User.find(2)
932 user = User.find(2)
933 assert_kind_of Hash, user.projects_by_role
933 assert_kind_of Hash, user.projects_by_role
934 assert_equal 2, user.projects_by_role.size
934 assert_equal 2, user.projects_by_role.size
935 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
935 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
936 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
936 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
937 end
937 end
938
938
939 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
939 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
940 user = User.find(2)
940 user = User.find(2)
941 assert_equal [], user.projects_by_role[Role.find(3)]
941 assert_equal [], user.projects_by_role[Role.find(3)]
942 # should not update the hash
942 # should not update the hash
943 assert_nil user.projects_by_role.values.detect(&:blank?)
943 assert_nil user.projects_by_role.values.detect(&:blank?)
944 end
944 end
945
945
946 def test_projects_by_role_for_user_with_no_role
946 def test_projects_by_role_for_user_with_no_role
947 user = User.generate!
947 user = User.generate!
948 assert_equal({}, user.projects_by_role)
948 assert_equal({}, user.projects_by_role)
949 end
949 end
950
950
951 def test_projects_by_role_for_anonymous
951 def test_projects_by_role_for_anonymous
952 assert_equal({}, User.anonymous.projects_by_role)
952 assert_equal({}, User.anonymous.projects_by_role)
953 end
953 end
954
954
955 def test_valid_notification_options
955 def test_valid_notification_options
956 # without memberships
956 # without memberships
957 assert_equal 5, User.find(7).valid_notification_options.size
957 assert_equal 5, User.find(7).valid_notification_options.size
958 # with memberships
958 # with memberships
959 assert_equal 6, User.find(2).valid_notification_options.size
959 assert_equal 6, User.find(2).valid_notification_options.size
960 end
960 end
961
961
962 def test_valid_notification_options_class_method
962 def test_valid_notification_options_class_method
963 assert_equal 5, User.valid_notification_options.size
963 assert_equal 5, User.valid_notification_options.size
964 assert_equal 5, User.valid_notification_options(User.find(7)).size
964 assert_equal 5, User.valid_notification_options(User.find(7)).size
965 assert_equal 6, User.valid_notification_options(User.find(2)).size
965 assert_equal 6, User.valid_notification_options(User.find(2)).size
966 end
966 end
967
967
968 def test_notified_project_ids_setter_should_coerce_to_unique_integer_array
968 def test_notified_project_ids_setter_should_coerce_to_unique_integer_array
969 @jsmith.notified_project_ids = ["1", "123", "2u", "wrong", "12", 6, 12, -35, ""]
969 @jsmith.notified_project_ids = ["1", "123", "2u", "wrong", "12", 6, 12, -35, ""]
970 assert_equal [1, 123, 2, 12, 6], @jsmith.notified_projects_ids
970 assert_equal [1, 123, 2, 12, 6], @jsmith.notified_projects_ids
971 end
971 end
972
972
973 def test_mail_notification_all
973 def test_mail_notification_all
974 @jsmith.mail_notification = 'all'
974 @jsmith.mail_notification = 'all'
975 @jsmith.notified_project_ids = []
975 @jsmith.notified_project_ids = []
976 @jsmith.save
976 @jsmith.save
977 @jsmith.reload
977 @jsmith.reload
978 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
978 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
979 end
979 end
980
980
981 def test_mail_notification_selected
981 def test_mail_notification_selected
982 @jsmith.mail_notification = 'selected'
982 @jsmith.mail_notification = 'selected'
983 @jsmith.notified_project_ids = [1]
983 @jsmith.notified_project_ids = [1]
984 @jsmith.save
984 @jsmith.save
985 @jsmith.reload
985 @jsmith.reload
986 assert Project.find(1).recipients.include?(@jsmith.mail)
986 assert Project.find(1).recipients.include?(@jsmith.mail)
987 end
987 end
988
988
989 def test_mail_notification_only_my_events
989 def test_mail_notification_only_my_events
990 @jsmith.mail_notification = 'only_my_events'
990 @jsmith.mail_notification = 'only_my_events'
991 @jsmith.notified_project_ids = []
991 @jsmith.notified_project_ids = []
992 @jsmith.save
992 @jsmith.save
993 @jsmith.reload
993 @jsmith.reload
994 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
994 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
995 end
995 end
996
996
997 def test_comments_sorting_preference
997 def test_comments_sorting_preference
998 assert !@jsmith.wants_comments_in_reverse_order?
998 assert !@jsmith.wants_comments_in_reverse_order?
999 @jsmith.pref.comments_sorting = 'asc'
999 @jsmith.pref.comments_sorting = 'asc'
1000 assert !@jsmith.wants_comments_in_reverse_order?
1000 assert !@jsmith.wants_comments_in_reverse_order?
1001 @jsmith.pref.comments_sorting = 'desc'
1001 @jsmith.pref.comments_sorting = 'desc'
1002 assert @jsmith.wants_comments_in_reverse_order?
1002 assert @jsmith.wants_comments_in_reverse_order?
1003 end
1003 end
1004
1004
1005 def test_find_by_mail_should_be_case_insensitive
1005 def test_find_by_mail_should_be_case_insensitive
1006 u = User.find_by_mail('JSmith@somenet.foo')
1006 u = User.find_by_mail('JSmith@somenet.foo')
1007 assert_not_nil u
1007 assert_not_nil u
1008 assert_equal 'jsmith@somenet.foo', u.mail
1008 assert_equal 'jsmith@somenet.foo', u.mail
1009 end
1009 end
1010
1010
1011 def test_random_password
1011 def test_random_password
1012 u = User.new
1012 u = User.new
1013 u.random_password
1013 u.random_password
1014 assert !u.password.blank?
1014 assert !u.password.blank?
1015 assert !u.password_confirmation.blank?
1015 assert !u.password_confirmation.blank?
1016 end
1016 end
1017
1017
1018 test "#change_password_allowed? should be allowed if no auth source is set" do
1018 test "#change_password_allowed? should be allowed if no auth source is set" do
1019 user = User.generate!
1019 user = User.generate!
1020 assert user.change_password_allowed?
1020 assert user.change_password_allowed?
1021 end
1021 end
1022
1022
1023 test "#change_password_allowed? should delegate to the auth source" do
1023 test "#change_password_allowed? should delegate to the auth source" do
1024 user = User.generate!
1024 user = User.generate!
1025
1025
1026 allowed_auth_source = AuthSource.generate!
1026 allowed_auth_source = AuthSource.generate!
1027 def allowed_auth_source.allow_password_changes?; true; end
1027 def allowed_auth_source.allow_password_changes?; true; end
1028
1028
1029 denied_auth_source = AuthSource.generate!
1029 denied_auth_source = AuthSource.generate!
1030 def denied_auth_source.allow_password_changes?; false; end
1030 def denied_auth_source.allow_password_changes?; false; end
1031
1031
1032 assert user.change_password_allowed?
1032 assert user.change_password_allowed?
1033
1033
1034 user.auth_source = allowed_auth_source
1034 user.auth_source = allowed_auth_source
1035 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
1035 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
1036
1036
1037 user.auth_source = denied_auth_source
1037 user.auth_source = denied_auth_source
1038 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
1038 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
1039 end
1039 end
1040
1040
1041 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
1041 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
1042 with_settings :unsubscribe => '1' do
1042 with_settings :unsubscribe => '1' do
1043 assert_equal true, User.find(2).own_account_deletable?
1043 assert_equal true, User.find(2).own_account_deletable?
1044 end
1044 end
1045 end
1045 end
1046
1046
1047 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
1047 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
1048 with_settings :unsubscribe => '0' do
1048 with_settings :unsubscribe => '0' do
1049 assert_equal false, User.find(2).own_account_deletable?
1049 assert_equal false, User.find(2).own_account_deletable?
1050 end
1050 end
1051 end
1051 end
1052
1052
1053 def test_own_account_deletable_should_be_false_for_a_single_admin
1053 def test_own_account_deletable_should_be_false_for_a_single_admin
1054 User.where(["admin = ? AND id <> ?", true, 1]).delete_all
1054 User.where(["admin = ? AND id <> ?", true, 1]).delete_all
1055
1055
1056 with_settings :unsubscribe => '1' do
1056 with_settings :unsubscribe => '1' do
1057 assert_equal false, User.find(1).own_account_deletable?
1057 assert_equal false, User.find(1).own_account_deletable?
1058 end
1058 end
1059 end
1059 end
1060
1060
1061 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
1061 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
1062 User.generate! do |user|
1062 User.generate! do |user|
1063 user.admin = true
1063 user.admin = true
1064 end
1064 end
1065
1065
1066 with_settings :unsubscribe => '1' do
1066 with_settings :unsubscribe => '1' do
1067 assert_equal true, User.find(1).own_account_deletable?
1067 assert_equal true, User.find(1).own_account_deletable?
1068 end
1068 end
1069 end
1069 end
1070
1070
1071 test "#allowed_to? for archived project should return false" do
1071 test "#allowed_to? for archived project should return false" do
1072 project = Project.find(1)
1072 project = Project.find(1)
1073 project.archive
1073 project.archive
1074 project.reload
1074 project.reload
1075 assert_equal false, @admin.allowed_to?(:view_issues, project)
1075 assert_equal false, @admin.allowed_to?(:view_issues, project)
1076 end
1076 end
1077
1077
1078 test "#allowed_to? for closed project should return true for read actions" do
1078 test "#allowed_to? for closed project should return true for read actions" do
1079 project = Project.find(1)
1079 project = Project.find(1)
1080 project.close
1080 project.close
1081 project.reload
1081 project.reload
1082 assert_equal false, @admin.allowed_to?(:edit_project, project)
1082 assert_equal false, @admin.allowed_to?(:edit_project, project)
1083 assert_equal true, @admin.allowed_to?(:view_project, project)
1083 assert_equal true, @admin.allowed_to?(:view_project, project)
1084 end
1084 end
1085
1085
1086 test "#allowed_to? for project with module disabled should return false" do
1086 test "#allowed_to? for project with module disabled should return false" do
1087 project = Project.find(1)
1087 project = Project.find(1)
1088 project.enabled_module_names = ["issue_tracking"]
1088 project.enabled_module_names = ["issue_tracking"]
1089 assert_equal true, @admin.allowed_to?(:add_issues, project)
1089 assert_equal true, @admin.allowed_to?(:add_issues, project)
1090 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
1090 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
1091 end
1091 end
1092
1092
1093 test "#allowed_to? for admin users should return true" do
1093 test "#allowed_to? for admin users should return true" do
1094 project = Project.find(1)
1094 project = Project.find(1)
1095 assert ! @admin.member_of?(project)
1095 assert ! @admin.member_of?(project)
1096 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
1096 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
1097 assert_equal true, @admin.allowed_to?(p.to_sym, project)
1097 assert_equal true, @admin.allowed_to?(p.to_sym, project)
1098 end
1098 end
1099 end
1099 end
1100
1100
1101 test "#allowed_to? for normal users" do
1101 test "#allowed_to? for normal users" do
1102 project = Project.find(1)
1102 project = Project.find(1)
1103 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
1103 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
1104 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
1104 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
1105 end
1105 end
1106
1106
1107 test "#allowed_to? with empty array should return false" do
1107 test "#allowed_to? with empty array should return false" do
1108 assert_equal false, @admin.allowed_to?(:view_project, [])
1108 assert_equal false, @admin.allowed_to?(:view_project, [])
1109 end
1109 end
1110
1110
1111 test "#allowed_to? with multiple projects" do
1111 test "#allowed_to? with multiple projects" do
1112 assert_equal true, @admin.allowed_to?(:view_project, Project.all.to_a)
1112 assert_equal true, @admin.allowed_to?(:view_project, Project.all.to_a)
1113 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all.to_a) #cannot see Project(2)
1113 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all.to_a) #cannot see Project(2)
1114 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects.to_a) #Manager or Developer everywhere
1114 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects.to_a) #Manager or Developer everywhere
1115 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects.to_a) #Dev cannot delete_issue_watchers
1115 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects.to_a) #Dev cannot delete_issue_watchers
1116 end
1116 end
1117
1117
1118 test "#allowed_to? with with options[:global] should return true if user has one role with the permission" do
1118 test "#allowed_to? with with options[:global] should return true if user has one role with the permission" do
1119 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1119 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1120 @anonymous = User.find(6)
1120 @anonymous = User.find(6)
1121 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1121 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1122 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1122 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1123 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1123 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1124 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1124 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1125 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1125 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1126 end
1126 end
1127
1127
1128 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1128 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1129 test "#allowed_to_globally?" do
1129 test "#allowed_to_globally?" do
1130 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1130 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1131 @anonymous = User.find(6)
1131 @anonymous = User.find(6)
1132 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1132 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1133 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1133 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1134 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1134 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1135 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1135 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1136 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1136 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1137 end
1137 end
1138
1138
1139 def test_notify_about_issue
1139 def test_notify_about_issue
1140 project = Project.find(1)
1140 project = Project.find(1)
1141 author = User.generate!
1141 author = User.generate!
1142 assignee = User.generate!
1142 assignee = User.generate!
1143 Member.create!(:user => assignee, :project => project, :role_ids => [1])
1143 Member.create!(:user => assignee, :project => project, :role_ids => [1])
1144 member = User.generate!
1144 member = User.generate!
1145 Member.create!(:user => member, :project => project, :role_ids => [1])
1145 Member.create!(:user => member, :project => project, :role_ids => [1])
1146 issue = Issue.generate!(:project => project, :assigned_to => assignee, :author => author)
1146 issue = Issue.generate!(:project => project, :assigned_to => assignee, :author => author)
1147
1147
1148 tests = {
1148 tests = {
1149 author => %w(all only_my_events only_owner selected),
1149 author => %w(all only_my_events only_owner selected),
1150 assignee => %w(all only_my_events only_assigned selected),
1150 assignee => %w(all only_my_events only_assigned selected),
1151 member => %w(all)
1151 member => %w(all)
1152 }
1152 }
1153
1153
1154 tests.each do |user, expected|
1154 tests.each do |user, expected|
1155 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1155 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1156 user.mail_notification = option
1156 user.mail_notification = option
1157 assert_equal expected.include?(option), user.notify_about?(issue)
1157 assert_equal expected.include?(option), user.notify_about?(issue)
1158 end
1158 end
1159 end
1159 end
1160 end
1160 end
1161
1161
1162 def test_notify_about_issue_for_previous_assignee
1162 def test_notify_about_issue_for_previous_assignee
1163 assignee = User.generate!(:mail_notification => 'only_assigned')
1163 assignee = User.generate!(:mail_notification => 'only_assigned')
1164 Member.create!(:user => assignee, :project_id => 1, :role_ids => [1])
1164 Member.create!(:user => assignee, :project_id => 1, :role_ids => [1])
1165 new_assignee = User.generate!(:mail_notification => 'only_assigned')
1165 new_assignee = User.generate!(:mail_notification => 'only_assigned')
1166 Member.create!(:user => new_assignee, :project_id => 1, :role_ids => [1])
1166 Member.create!(:user => new_assignee, :project_id => 1, :role_ids => [1])
1167 issue = Issue.generate!(:assigned_to => assignee)
1167 issue = Issue.generate!(:assigned_to => assignee)
1168
1168
1169 assert assignee.notify_about?(issue)
1169 assert assignee.notify_about?(issue)
1170 assert !new_assignee.notify_about?(issue)
1170 assert !new_assignee.notify_about?(issue)
1171
1171
1172 issue.assigned_to = new_assignee
1172 issue.assigned_to = new_assignee
1173 assert assignee.notify_about?(issue)
1173 assert assignee.notify_about?(issue)
1174 assert new_assignee.notify_about?(issue)
1174 assert new_assignee.notify_about?(issue)
1175
1175
1176 issue.save!
1176 issue.save!
1177 assert !assignee.notify_about?(issue)
1177 assert !assignee.notify_about?(issue)
1178 assert new_assignee.notify_about?(issue)
1178 assert new_assignee.notify_about?(issue)
1179 end
1179 end
1180
1180
1181 def test_notify_about_news
1181 def test_notify_about_news
1182 user = User.generate!
1182 user = User.generate!
1183 news = News.new
1183 news = News.new
1184
1184
1185 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1185 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1186 user.mail_notification = option
1186 user.mail_notification = option
1187 assert_equal (option != 'none'), user.notify_about?(news)
1187 assert_equal (option != 'none'), user.notify_about?(news)
1188 end
1188 end
1189 end
1189 end
1190
1190
1191 def test_salt_unsalted_passwords
1191 def test_salt_unsalted_passwords
1192 # Restore a user with an unsalted password
1192 # Restore a user with an unsalted password
1193 user = User.find(1)
1193 user = User.find(1)
1194 user.salt = nil
1194 user.salt = nil
1195 user.hashed_password = User.hash_password("unsalted")
1195 user.hashed_password = User.hash_password("unsalted")
1196 user.save!
1196 user.save!
1197
1197
1198 User.salt_unsalted_passwords!
1198 User.salt_unsalted_passwords!
1199
1199
1200 user.reload
1200 user.reload
1201 # Salt added
1201 # Salt added
1202 assert !user.salt.blank?
1202 assert !user.salt.blank?
1203 # Password still valid
1203 # Password still valid
1204 assert user.check_password?("unsalted")
1204 assert user.check_password?("unsalted")
1205 assert_equal user, User.try_to_login(user.login, "unsalted")
1205 assert_equal user, User.try_to_login(user.login, "unsalted")
1206 end
1206 end
1207
1207
1208 if Object.const_defined?(:OpenID)
1208 if Object.const_defined?(:OpenID)
1209 def test_setting_identity_url
1209 def test_setting_identity_url
1210 normalized_open_id_url = 'http://example.com/'
1210 normalized_open_id_url = 'http://example.com/'
1211 u = User.new( :identity_url => 'http://example.com/' )
1211 u = User.new( :identity_url => 'http://example.com/' )
1212 assert_equal normalized_open_id_url, u.identity_url
1212 assert_equal normalized_open_id_url, u.identity_url
1213 end
1213 end
1214
1214
1215 def test_setting_identity_url_without_trailing_slash
1215 def test_setting_identity_url_without_trailing_slash
1216 normalized_open_id_url = 'http://example.com/'
1216 normalized_open_id_url = 'http://example.com/'
1217 u = User.new( :identity_url => 'http://example.com' )
1217 u = User.new( :identity_url => 'http://example.com' )
1218 assert_equal normalized_open_id_url, u.identity_url
1218 assert_equal normalized_open_id_url, u.identity_url
1219 end
1219 end
1220
1220
1221 def test_setting_identity_url_without_protocol
1221 def test_setting_identity_url_without_protocol
1222 normalized_open_id_url = 'http://example.com/'
1222 normalized_open_id_url = 'http://example.com/'
1223 u = User.new( :identity_url => 'example.com' )
1223 u = User.new( :identity_url => 'example.com' )
1224 assert_equal normalized_open_id_url, u.identity_url
1224 assert_equal normalized_open_id_url, u.identity_url
1225 end
1225 end
1226
1226
1227 def test_setting_blank_identity_url
1227 def test_setting_blank_identity_url
1228 u = User.new( :identity_url => 'example.com' )
1228 u = User.new( :identity_url => 'example.com' )
1229 u.identity_url = ''
1229 u.identity_url = ''
1230 assert u.identity_url.blank?
1230 assert u.identity_url.blank?
1231 end
1231 end
1232
1232
1233 def test_setting_invalid_identity_url
1233 def test_setting_invalid_identity_url
1234 u = User.new( :identity_url => 'this is not an openid url' )
1234 u = User.new( :identity_url => 'this is not an openid url' )
1235 assert u.identity_url.blank?
1235 assert u.identity_url.blank?
1236 end
1236 end
1237 else
1237 else
1238 puts "Skipping openid tests."
1238 puts "Skipping openid tests."
1239 end
1239 end
1240 end
1240 end
@@ -1,293 +1,293
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class VersionTest < ActiveSupport::TestCase
20 class VersionTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :issues, :issue_statuses, :trackers,
21 fixtures :projects, :users, :issues, :issue_statuses, :trackers,
22 :enumerations, :versions, :projects_trackers
22 :enumerations, :versions, :projects_trackers
23
23
24 def test_create
24 def test_create
25 v = Version.new(:project => Project.find(1), :name => '1.1',
25 v = Version.new(:project => Project.find(1), :name => '1.1',
26 :effective_date => '2011-03-25')
26 :effective_date => '2011-03-25')
27 assert v.save
27 assert v.save
28 assert_equal 'open', v.status
28 assert_equal 'open', v.status
29 assert_equal 'none', v.sharing
29 assert_equal 'none', v.sharing
30 end
30 end
31
31
32 def test_create_as_default_project_version
32 def test_create_as_default_project_version
33 project = Project.find(1)
33 project = Project.find(1)
34 v = Version.new(:project => project, :name => '1.1',
34 v = Version.new(:project => project, :name => '1.1',
35 :default_project_version => '1')
35 :default_project_version => '1')
36 assert v.save
36 assert v.save
37 assert_equal v, project.reload.default_version
37 assert_equal v, project.reload.default_version
38 end
38 end
39
39
40 def test_create_not_as_default_project_version
40 def test_create_not_as_default_project_version
41 project = Project.find(1)
41 project = Project.find(1)
42 v = Version.new(:project => project, :name => '1.1',
42 v = Version.new(:project => project, :name => '1.1',
43 :default_project_version => '0')
43 :default_project_version => '0')
44 assert v.save
44 assert v.save
45 assert_nil project.reload.default_version
45 assert_nil project.reload.default_version
46 end
46 end
47
47
48 def test_invalid_effective_date_validation
48 def test_invalid_effective_date_validation
49 v = Version.new(:project => Project.find(1), :name => '1.1',
49 v = Version.new(:project => Project.find(1), :name => '1.1',
50 :effective_date => '99999-01-01')
50 :effective_date => '99999-01-01')
51 assert !v.valid?
51 assert !v.valid?
52 v.effective_date = '2012-11-33'
52 v.effective_date = '2012-11-33'
53 assert !v.valid?
53 assert !v.valid?
54 v.effective_date = '2012-31-11'
54 v.effective_date = '2012-31-11'
55 assert !v.valid?
55 assert !v.valid?
56 v.effective_date = '-2012-31-11'
56 v.effective_date = '-2012-31-11'
57 assert !v.valid?
57 assert !v.valid?
58 v.effective_date = 'ABC'
58 v.effective_date = 'ABC'
59 assert !v.valid?
59 assert !v.valid?
60 assert_include I18n.translate('activerecord.errors.messages.not_a_date'),
60 assert_include I18n.translate('activerecord.errors.messages.not_a_date'),
61 v.errors[:effective_date]
61 v.errors[:effective_date]
62 end
62 end
63
63
64 def test_progress_should_be_0_with_no_assigned_issues
64 def test_progress_should_be_0_with_no_assigned_issues
65 project = Project.find(1)
65 project = Project.find(1)
66 v = Version.create!(:project => project, :name => 'Progress')
66 v = Version.create!(:project => project, :name => 'Progress')
67 assert_equal 0, v.completed_percent
67 assert_equal 0, v.completed_percent
68 assert_equal 0, v.closed_percent
68 assert_equal 0, v.closed_percent
69 end
69 end
70
70
71 def test_progress_should_be_0_with_unbegun_assigned_issues
71 def test_progress_should_be_0_with_unbegun_assigned_issues
72 project = Project.find(1)
72 project = Project.find(1)
73 v = Version.create!(:project => project, :name => 'Progress')
73 v = Version.create!(:project => project, :name => 'Progress')
74 add_issue(v)
74 add_issue(v)
75 add_issue(v, :done_ratio => 0)
75 add_issue(v, :done_ratio => 0)
76 assert_progress_equal 0, v.completed_percent
76 assert_progress_equal 0, v.completed_percent
77 assert_progress_equal 0, v.closed_percent
77 assert_progress_equal 0, v.closed_percent
78 end
78 end
79
79
80 def test_progress_should_be_100_with_closed_assigned_issues
80 def test_progress_should_be_100_with_closed_assigned_issues
81 project = Project.find(1)
81 project = Project.find(1)
82 status = IssueStatus.where(:is_closed => true).first
82 status = IssueStatus.where(:is_closed => true).first
83 v = Version.create!(:project => project, :name => 'Progress')
83 v = Version.create!(:project => project, :name => 'Progress')
84 add_issue(v, :status => status)
84 add_issue(v, :status => status)
85 add_issue(v, :status => status, :done_ratio => 20)
85 add_issue(v, :status => status, :done_ratio => 20)
86 add_issue(v, :status => status, :done_ratio => 70, :estimated_hours => 25)
86 add_issue(v, :status => status, :done_ratio => 70, :estimated_hours => 25)
87 add_issue(v, :status => status, :estimated_hours => 15)
87 add_issue(v, :status => status, :estimated_hours => 15)
88 assert_progress_equal 100.0, v.completed_percent
88 assert_progress_equal 100.0, v.completed_percent
89 assert_progress_equal 100.0, v.closed_percent
89 assert_progress_equal 100.0, v.closed_percent
90 end
90 end
91
91
92 def test_progress_should_consider_done_ratio_of_open_assigned_issues
92 def test_progress_should_consider_done_ratio_of_open_assigned_issues
93 project = Project.find(1)
93 project = Project.find(1)
94 v = Version.create!(:project => project, :name => 'Progress')
94 v = Version.create!(:project => project, :name => 'Progress')
95 add_issue(v)
95 add_issue(v)
96 add_issue(v, :done_ratio => 20)
96 add_issue(v, :done_ratio => 20)
97 add_issue(v, :done_ratio => 70)
97 add_issue(v, :done_ratio => 70)
98 assert_progress_equal (0.0 + 20.0 + 70.0)/3, v.completed_percent
98 assert_progress_equal (0.0 + 20.0 + 70.0)/3, v.completed_percent
99 assert_progress_equal 0, v.closed_percent
99 assert_progress_equal 0, v.closed_percent
100 end
100 end
101
101
102 def test_progress_should_consider_closed_issues_as_completed
102 def test_progress_should_consider_closed_issues_as_completed
103 project = Project.find(1)
103 project = Project.find(1)
104 v = Version.create!(:project => project, :name => 'Progress')
104 v = Version.create!(:project => project, :name => 'Progress')
105 add_issue(v)
105 add_issue(v)
106 add_issue(v, :done_ratio => 20)
106 add_issue(v, :done_ratio => 20)
107 add_issue(v, :status => IssueStatus.where(:is_closed => true).first)
107 add_issue(v, :status => IssueStatus.where(:is_closed => true).first)
108 assert_progress_equal (0.0 + 20.0 + 100.0)/3, v.completed_percent
108 assert_progress_equal (0.0 + 20.0 + 100.0)/3, v.completed_percent
109 assert_progress_equal (100.0)/3, v.closed_percent
109 assert_progress_equal (100.0)/3, v.closed_percent
110 end
110 end
111
111
112 def test_progress_should_consider_estimated_hours_to_weight_issues
112 def test_progress_should_consider_estimated_hours_to_weight_issues
113 project = Project.find(1)
113 project = Project.find(1)
114 v = Version.create!(:project => project, :name => 'Progress')
114 v = Version.create!(:project => project, :name => 'Progress')
115 add_issue(v, :estimated_hours => 10)
115 add_issue(v, :estimated_hours => 10)
116 add_issue(v, :estimated_hours => 20, :done_ratio => 30)
116 add_issue(v, :estimated_hours => 20, :done_ratio => 30)
117 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
117 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
118 add_issue(v, :estimated_hours => 25, :status => IssueStatus.where(:is_closed => true).first)
118 add_issue(v, :estimated_hours => 25, :status => IssueStatus.where(:is_closed => true).first)
119 assert_progress_equal (10.0*0 + 20.0*0.3 + 40*0.1 + 25.0*1)/95.0*100, v.completed_percent
119 assert_progress_equal (10.0*0 + 20.0*0.3 + 40*0.1 + 25.0*1)/95.0*100, v.completed_percent
120 assert_progress_equal 25.0/95.0*100, v.closed_percent
120 assert_progress_equal 25.0/95.0*100, v.closed_percent
121 end
121 end
122
122
123 def test_progress_should_consider_average_estimated_hours_to_weight_unestimated_issues
123 def test_progress_should_consider_average_estimated_hours_to_weight_unestimated_issues
124 project = Project.find(1)
124 project = Project.find(1)
125 v = Version.create!(:project => project, :name => 'Progress')
125 v = Version.create!(:project => project, :name => 'Progress')
126 add_issue(v, :done_ratio => 20)
126 add_issue(v, :done_ratio => 20)
127 add_issue(v, :status => IssueStatus.where(:is_closed => true).first)
127 add_issue(v, :status => IssueStatus.where(:is_closed => true).first)
128 add_issue(v, :estimated_hours => 10, :done_ratio => 30)
128 add_issue(v, :estimated_hours => 10, :done_ratio => 30)
129 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
129 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
130 assert_progress_equal (25.0*0.2 + 25.0*1 + 10.0*0.3 + 40.0*0.1)/100.0*100, v.completed_percent
130 assert_progress_equal (25.0*0.2 + 25.0*1 + 10.0*0.3 + 40.0*0.1)/100.0*100, v.completed_percent
131 assert_progress_equal 25.0/100.0*100, v.closed_percent
131 assert_progress_equal 25.0/100.0*100, v.closed_percent
132 end
132 end
133
133
134 def test_should_sort_scheduled_then_unscheduled_versions
134 def test_should_sort_scheduled_then_unscheduled_versions
135 Version.delete_all
135 Version.delete_all
136 v4 = Version.create!(:project_id => 1, :name => 'v4')
136 v4 = Version.create!(:project_id => 1, :name => 'v4')
137 v3 = Version.create!(:project_id => 1, :name => 'v2', :effective_date => '2012-07-14')
137 v3 = Version.create!(:project_id => 1, :name => 'v2', :effective_date => '2012-07-14')
138 v2 = Version.create!(:project_id => 1, :name => 'v1')
138 v2 = Version.create!(:project_id => 1, :name => 'v1')
139 v1 = Version.create!(:project_id => 1, :name => 'v3', :effective_date => '2012-08-02')
139 v1 = Version.create!(:project_id => 1, :name => 'v3', :effective_date => '2012-08-02')
140 v5 = Version.create!(:project_id => 1, :name => 'v5', :effective_date => '2012-07-02')
140 v5 = Version.create!(:project_id => 1, :name => 'v5', :effective_date => '2012-07-02')
141
141
142 assert_equal [v5, v3, v1, v2, v4], [v1, v2, v3, v4, v5].sort
142 assert_equal [v5, v3, v1, v2, v4], [v1, v2, v3, v4, v5].sort
143 assert_equal [v5, v3, v1, v2, v4], Version.sorted.to_a
143 assert_equal [v5, v3, v1, v2, v4], Version.sorted.to_a
144 end
144 end
145
145
146 def test_should_sort_versions_with_same_date_by_name
146 def test_should_sort_versions_with_same_date_by_name
147 v1 = Version.new(:effective_date => '2014-12-03', :name => 'v2')
147 v1 = Version.new(:effective_date => '2014-12-03', :name => 'v2')
148 v2 = Version.new(:effective_date => '2014-12-03', :name => 'v1')
148 v2 = Version.new(:effective_date => '2014-12-03', :name => 'v1')
149 assert_equal [v2, v1], [v1, v2].sort
149 assert_equal [v2, v1], [v1, v2].sort
150 end
150 end
151
151
152 def test_completed_should_be_false_when_due_today
152 def test_completed_should_be_false_when_due_today
153 version = Version.create!(:project_id => 1, :effective_date => Date.today, :name => 'Due today')
153 version = Version.create!(:project_id => 1, :effective_date => Date.today, :name => 'Due today')
154 assert_equal false, version.completed?
154 assert_equal false, version.completed?
155 end
155 end
156
156
157 def test_completed_should_be_true_when_closed
157 def test_completed_should_be_true_when_closed
158 version = Version.create!(:project_id => 1, :status => 'closed', :name => 'Closed')
158 version = Version.create!(:project_id => 1, :status => 'closed', :name => 'Closed')
159 assert_equal true, version.completed?
159 assert_equal true, version.completed?
160 end
160 end
161
161
162 test "#behind_schedule? should be false if there are no issues assigned" do
162 test "#behind_schedule? should be false if there are no issues assigned" do
163 version = Version.generate!(:effective_date => Date.yesterday)
163 version = Version.generate!(:effective_date => Date.yesterday)
164 assert_equal false, version.behind_schedule?
164 assert_equal false, version.behind_schedule?
165 end
165 end
166
166
167 test "#behind_schedule? should be false if there is no effective_date" do
167 test "#behind_schedule? should be false if there is no effective_date" do
168 version = Version.generate!(:effective_date => nil)
168 version = Version.generate!(:effective_date => nil)
169 assert_equal false, version.behind_schedule?
169 assert_equal false, version.behind_schedule?
170 end
170 end
171
171
172 test "#behind_schedule? should be false if all of the issues are ahead of schedule" do
172 test "#behind_schedule? should be false if all of the issues are ahead of schedule" do
173 version = Version.create!(:project_id => 1, :name => 'test', :effective_date => 7.days.from_now.to_date)
173 version = Version.create!(:project_id => 1, :name => 'test', :effective_date => 7.days.from_now.to_date)
174 add_issue(version, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
174 add_issue(version, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
175 add_issue(version, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
175 add_issue(version, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
176 assert_equal 60, version.completed_percent
176 assert_equal 60, version.completed_percent
177 assert_equal false, version.behind_schedule?
177 assert_equal false, version.behind_schedule?
178 end
178 end
179
179
180 test "#behind_schedule? should be true if any of the issues are behind schedule" do
180 test "#behind_schedule? should be true if any of the issues are behind schedule" do
181 version = Version.create!(:project_id => 1, :name => 'test', :effective_date => 7.days.from_now.to_date)
181 version = Version.create!(:project_id => 1, :name => 'test', :effective_date => 7.days.from_now.to_date)
182 add_issue(version, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
182 add_issue(version, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
183 add_issue(version, :start_date => 7.days.ago, :done_ratio => 20) # 14 day span, 20% done, 50% time left
183 add_issue(version, :start_date => 7.days.ago, :done_ratio => 20) # 14 day span, 20% done, 50% time left
184 assert_equal 40, version.completed_percent
184 assert_equal 40, version.completed_percent
185 assert_equal true, version.behind_schedule?
185 assert_equal true, version.behind_schedule?
186 end
186 end
187
187
188 test "#behind_schedule? should be false if all of the issues are complete" do
188 test "#behind_schedule? should be false if all of the issues are complete" do
189 version = Version.create!(:project_id => 1, :name => 'test', :effective_date => 7.days.from_now.to_date)
189 version = Version.create!(:project_id => 1, :name => 'test', :effective_date => 7.days.from_now.to_date)
190 add_issue(version, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)) # 7 day span
190 add_issue(version, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)) # 7 day span
191 add_issue(version, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)) # 7 day span
191 add_issue(version, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)) # 7 day span
192 assert_equal 100, version.completed_percent
192 assert_equal 100, version.completed_percent
193 assert_equal false, version.behind_schedule?
193 assert_equal false, version.behind_schedule?
194 end
194 end
195
195
196 test "#estimated_hours should return 0 with no assigned issues" do
196 test "#estimated_hours should return 0 with no assigned issues" do
197 version = Version.generate!
197 version = Version.generate!
198 assert_equal 0, version.estimated_hours
198 assert_equal 0, version.estimated_hours
199 end
199 end
200
200
201 test "#estimated_hours should return 0 with no estimated hours" do
201 test "#estimated_hours should return 0 with no estimated hours" do
202 version = Version.create!(:project_id => 1, :name => 'test')
202 version = Version.create!(:project_id => 1, :name => 'test')
203 add_issue(version)
203 add_issue(version)
204 assert_equal 0, version.estimated_hours
204 assert_equal 0, version.estimated_hours
205 end
205 end
206
206
207 test "#estimated_hours should return return the sum of estimated hours" do
207 test "#estimated_hours should return return the sum of estimated hours" do
208 version = Version.create!(:project_id => 1, :name => 'test')
208 version = Version.create!(:project_id => 1, :name => 'test')
209 add_issue(version, :estimated_hours => 2.5)
209 add_issue(version, :estimated_hours => 2.5)
210 add_issue(version, :estimated_hours => 5)
210 add_issue(version, :estimated_hours => 5)
211 assert_equal 7.5, version.estimated_hours
211 assert_equal 7.5, version.estimated_hours
212 end
212 end
213
213
214 test "#estimated_hours should return the sum of leaves estimated hours" do
214 test "#estimated_hours should return the sum of leaves estimated hours" do
215 version = Version.create!(:project_id => 1, :name => 'test')
215 version = Version.create!(:project_id => 1, :name => 'test')
216 parent = add_issue(version)
216 parent = add_issue(version)
217 add_issue(version, :estimated_hours => 2.5, :parent_issue_id => parent.id)
217 add_issue(version, :estimated_hours => 2.5, :parent_issue_id => parent.id)
218 add_issue(version, :estimated_hours => 5, :parent_issue_id => parent.id)
218 add_issue(version, :estimated_hours => 5, :parent_issue_id => parent.id)
219 assert_equal 7.5, version.estimated_hours
219 assert_equal 7.5, version.estimated_hours
220 end
220 end
221
221
222 test "should update all issue's fixed_version associations in case the hierarchy changed XXX" do
222 test "should update all issue's fixed_version associations in case the hierarchy changed XXX" do
223 User.current = User.find(1) # Need the admin's permissions
223 User.current = User.find(1) # Need the admin's permissions
224
224
225 @version = Version.find(7)
225 @version = Version.find(7)
226 # Separate hierarchy
226 # Separate hierarchy
227 project_1_issue = Issue.find(1)
227 project_1_issue = Issue.find(1)
228 project_1_issue.fixed_version = @version
228 project_1_issue.fixed_version = @version
229 assert project_1_issue.save, project_1_issue.errors.full_messages.to_s
229 assert project_1_issue.save, project_1_issue.errors.full_messages.to_s
230
230
231 project_5_issue = Issue.find(6)
231 project_5_issue = Issue.find(6)
232 project_5_issue.fixed_version = @version
232 project_5_issue.fixed_version = @version
233 assert project_5_issue.save
233 assert project_5_issue.save
234
234
235 # Project
235 # Project
236 project_2_issue = Issue.find(4)
236 project_2_issue = Issue.find(4)
237 project_2_issue.fixed_version = @version
237 project_2_issue.fixed_version = @version
238 assert project_2_issue.save
238 assert project_2_issue.save
239
239
240 # Update the sharing
240 # Update the sharing
241 @version.sharing = 'none'
241 @version.sharing = 'none'
242 assert @version.save
242 assert @version.save
243
243
244 # Project 1 now out of the shared scope
244 # Project 1 now out of the shared scope
245 project_1_issue.reload
245 project_1_issue.reload
246 assert_equal nil, project_1_issue.fixed_version,
246 assert_nil project_1_issue.fixed_version,
247 "Fixed version is still set after changing the Version's sharing"
247 "Fixed version is still set after changing the Version's sharing"
248
248
249 # Project 5 now out of the shared scope
249 # Project 5 now out of the shared scope
250 project_5_issue.reload
250 project_5_issue.reload
251 assert_equal nil, project_5_issue.fixed_version,
251 assert_nil project_5_issue.fixed_version,
252 "Fixed version is still set after changing the Version's sharing"
252 "Fixed version is still set after changing the Version's sharing"
253
253
254 # Project 2 issue remains
254 # Project 2 issue remains
255 project_2_issue.reload
255 project_2_issue.reload
256 assert_equal @version, project_2_issue.fixed_version
256 assert_equal @version, project_2_issue.fixed_version
257 end
257 end
258
258
259 def test_deletable_should_return_true_when_not_referenced
259 def test_deletable_should_return_true_when_not_referenced
260 version = Version.generate!
260 version = Version.generate!
261
261
262 assert_equal true, version.deletable?
262 assert_equal true, version.deletable?
263 end
263 end
264
264
265 def test_deletable_should_return_false_when_referenced_by_an_issue
265 def test_deletable_should_return_false_when_referenced_by_an_issue
266 version = Version.generate!
266 version = Version.generate!
267 Issue.generate!(:fixed_version => version)
267 Issue.generate!(:fixed_version => version)
268
268
269 assert_equal false, version.deletable?
269 assert_equal false, version.deletable?
270 end
270 end
271
271
272 def test_deletable_should_return_false_when_referenced_by_a_custom_field
272 def test_deletable_should_return_false_when_referenced_by_a_custom_field
273 version = Version.generate!
273 version = Version.generate!
274 field = IssueCustomField.generate!(:field_format => 'version')
274 field = IssueCustomField.generate!(:field_format => 'version')
275 value = CustomValue.create!(:custom_field => field, :customized => Issue.first, :value => version.id)
275 value = CustomValue.create!(:custom_field => field, :customized => Issue.first, :value => version.id)
276
276
277 assert_equal false, version.deletable?
277 assert_equal false, version.deletable?
278 end
278 end
279
279
280 private
280 private
281
281
282 def add_issue(version, attributes={})
282 def add_issue(version, attributes={})
283 Issue.create!({:project => version.project,
283 Issue.create!({:project => version.project,
284 :fixed_version => version,
284 :fixed_version => version,
285 :subject => 'Test',
285 :subject => 'Test',
286 :author => User.first,
286 :author => User.first,
287 :tracker => version.project.trackers.first}.merge(attributes))
287 :tracker => version.project.trackers.first}.merge(attributes))
288 end
288 end
289
289
290 def assert_progress_equal(expected_float, actual_float, message="")
290 def assert_progress_equal(expected_float, actual_float, message="")
291 assert_in_delta(expected_float, actual_float, 0.000001, message="")
291 assert_in_delta(expected_float, actual_float, 0.000001, message="")
292 end
292 end
293 end
293 end
@@ -1,201 +1,201
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class WikiPageTest < ActiveSupport::TestCase
20 class WikiPageTest < ActiveSupport::TestCase
21 fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
21 fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
22
22
23 def setup
23 def setup
24 @wiki = Wiki.find(1)
24 @wiki = Wiki.find(1)
25 @page = @wiki.pages.first
25 @page = @wiki.pages.first
26 end
26 end
27
27
28 def test_create
28 def test_create
29 page = WikiPage.new(:wiki => @wiki)
29 page = WikiPage.new(:wiki => @wiki)
30 assert !page.save
30 assert !page.save
31 assert_equal 1, page.errors.count
31 assert_equal 1, page.errors.count
32
32
33 page.title = "Page"
33 page.title = "Page"
34 assert page.save
34 assert page.save
35 page.reload
35 page.reload
36 assert !page.protected?
36 assert !page.protected?
37
37
38 @wiki.reload
38 @wiki.reload
39 assert @wiki.pages.include?(page)
39 assert @wiki.pages.include?(page)
40 end
40 end
41
41
42 def test_sidebar_should_be_protected_by_default
42 def test_sidebar_should_be_protected_by_default
43 page = @wiki.find_or_new_page('sidebar')
43 page = @wiki.find_or_new_page('sidebar')
44 assert page.new_record?
44 assert page.new_record?
45 assert page.protected?
45 assert page.protected?
46 end
46 end
47
47
48 def test_find_or_new_page
48 def test_find_or_new_page
49 page = @wiki.find_or_new_page("CookBook documentation")
49 page = @wiki.find_or_new_page("CookBook documentation")
50 assert_kind_of WikiPage, page
50 assert_kind_of WikiPage, page
51 assert !page.new_record?
51 assert !page.new_record?
52
52
53 page = @wiki.find_or_new_page("Non existing page")
53 page = @wiki.find_or_new_page("Non existing page")
54 assert_kind_of WikiPage, page
54 assert_kind_of WikiPage, page
55 assert page.new_record?
55 assert page.new_record?
56 end
56 end
57
57
58 def test_parent_title
58 def test_parent_title
59 page = WikiPage.find_by_title('Another_page')
59 page = WikiPage.find_by_title('Another_page')
60 assert_nil page.parent_title
60 assert_nil page.parent_title
61
61
62 page = WikiPage.find_by_title('Page_with_an_inline_image')
62 page = WikiPage.find_by_title('Page_with_an_inline_image')
63 assert_equal 'CookBook documentation', page.parent_title
63 assert_equal 'CookBook documentation', page.parent_title
64 end
64 end
65
65
66 def test_assign_parent
66 def test_assign_parent
67 page = WikiPage.find_by_title('Another_page')
67 page = WikiPage.find_by_title('Another_page')
68 page.parent_title = 'CookBook documentation'
68 page.parent_title = 'CookBook documentation'
69 assert page.save
69 assert page.save
70 page.reload
70 page.reload
71 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
71 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
72 end
72 end
73
73
74 def test_unassign_parent
74 def test_unassign_parent
75 page = WikiPage.find_by_title('Page_with_an_inline_image')
75 page = WikiPage.find_by_title('Page_with_an_inline_image')
76 page.parent_title = ''
76 page.parent_title = ''
77 assert page.save
77 assert page.save
78 page.reload
78 page.reload
79 assert_nil page.parent
79 assert_nil page.parent
80 end
80 end
81
81
82 def test_parent_validation
82 def test_parent_validation
83 page = WikiPage.find_by_title('CookBook_documentation')
83 page = WikiPage.find_by_title('CookBook_documentation')
84
84
85 # A page that doesn't exist
85 # A page that doesn't exist
86 page.parent_title = 'Unknown title'
86 page.parent_title = 'Unknown title'
87 assert !page.save
87 assert !page.save
88 assert_include I18n.translate('activerecord.errors.messages.invalid'),
88 assert_include I18n.translate('activerecord.errors.messages.invalid'),
89 page.errors[:parent_title]
89 page.errors[:parent_title]
90 # A child page
90 # A child page
91 page.parent_title = 'Page_with_an_inline_image'
91 page.parent_title = 'Page_with_an_inline_image'
92 assert !page.save
92 assert !page.save
93 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
93 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
94 page.errors[:parent_title]
94 page.errors[:parent_title]
95 # The page itself
95 # The page itself
96 page.parent_title = 'CookBook_documentation'
96 page.parent_title = 'CookBook_documentation'
97 assert !page.save
97 assert !page.save
98 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
98 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
99 page.errors[:parent_title]
99 page.errors[:parent_title]
100 page.parent_title = 'Another_page'
100 page.parent_title = 'Another_page'
101 assert page.save
101 assert page.save
102 end
102 end
103
103
104 def test_move_child_should_clear_parent
104 def test_move_child_should_clear_parent
105 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
105 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
106 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
106 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
107
107
108 child.wiki_id = 2
108 child.wiki_id = 2
109 child.save!
109 child.save!
110 assert_equal nil, child.reload.parent_id
110 assert_nil child.reload.parent_id
111 end
111 end
112
112
113 def test_move_parent_should_move_child_page
113 def test_move_parent_should_move_child_page
114 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
114 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
115 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
115 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
116 parent.reload
116 parent.reload
117
117
118 parent.wiki_id = 2
118 parent.wiki_id = 2
119 parent.save!
119 parent.save!
120 assert_equal 2, child.reload.wiki_id
120 assert_equal 2, child.reload.wiki_id
121 assert_equal parent, child.parent
121 assert_equal parent, child.parent
122 end
122 end
123
123
124 def test_move_parent_with_child_with_duplicate_name_should_not_move_child
124 def test_move_parent_with_child_with_duplicate_name_should_not_move_child
125 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
125 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
126 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent_id => parent.id)
126 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent_id => parent.id)
127 parent.reload
127 parent.reload
128 # page with the same name as the child in the target wiki
128 # page with the same name as the child in the target wiki
129 WikiPage.create!(:wiki_id => 2, :title => 'Child')
129 WikiPage.create!(:wiki_id => 2, :title => 'Child')
130
130
131 parent.wiki_id = 2
131 parent.wiki_id = 2
132 parent.save!
132 parent.save!
133
133
134 parent.reload
134 parent.reload
135 assert_equal 2, parent.wiki_id
135 assert_equal 2, parent.wiki_id
136
136
137 child.reload
137 child.reload
138 assert_equal 1, child.wiki_id
138 assert_equal 1, child.wiki_id
139 assert_nil child.parent_id
139 assert_nil child.parent_id
140 end
140 end
141
141
142 def test_destroy
142 def test_destroy
143 page = WikiPage.find(1)
143 page = WikiPage.find(1)
144 page.destroy
144 page.destroy
145 assert_nil WikiPage.find_by_id(1)
145 assert_nil WikiPage.find_by_id(1)
146 # make sure that page content and its history are deleted
146 # make sure that page content and its history are deleted
147 assert_equal 0, WikiContent.where(:page_id => 1).count
147 assert_equal 0, WikiContent.where(:page_id => 1).count
148 assert_equal 0, WikiContent.versioned_class.where(:page_id => 1).count
148 assert_equal 0, WikiContent.versioned_class.where(:page_id => 1).count
149 end
149 end
150
150
151 def test_destroy_should_not_nullify_children
151 def test_destroy_should_not_nullify_children
152 page = WikiPage.find(2)
152 page = WikiPage.find(2)
153 child_ids = page.child_ids
153 child_ids = page.child_ids
154 assert child_ids.any?
154 assert child_ids.any?
155 page.destroy
155 page.destroy
156 assert_nil WikiPage.find_by_id(2)
156 assert_nil WikiPage.find_by_id(2)
157
157
158 children = WikiPage.where(:id => child_ids)
158 children = WikiPage.where(:id => child_ids)
159 assert_equal child_ids.size, children.count
159 assert_equal child_ids.size, children.count
160 children.each do |child|
160 children.each do |child|
161 assert_nil child.parent_id
161 assert_nil child.parent_id
162 end
162 end
163 end
163 end
164
164
165 def test_with_updated_on_scope_should_preload_updated_on_and_version
165 def test_with_updated_on_scope_should_preload_updated_on_and_version
166 page = WikiPage.with_updated_on.where(:id => 1).first
166 page = WikiPage.with_updated_on.where(:id => 1).first
167 # make the assertions fail if attributes are not preloaded
167 # make the assertions fail if attributes are not preloaded
168 WikiContent.update_all(:updated_on => '2001-01-01 10:00:00', :version => 1)
168 WikiContent.update_all(:updated_on => '2001-01-01 10:00:00', :version => 1)
169
169
170 assert_equal Time.gm(2007, 3, 6, 23, 10, 51), page.updated_on
170 assert_equal Time.gm(2007, 3, 6, 23, 10, 51), page.updated_on
171 assert_equal 3, page.version
171 assert_equal 3, page.version
172 end
172 end
173
173
174 def test_descendants
174 def test_descendants
175 page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
175 page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
176 child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
176 child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
177 child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
177 child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
178 child111 = WikiPage.create!(:wiki => @wiki, :title => 'Child111', :parent => child11)
178 child111 = WikiPage.create!(:wiki => @wiki, :title => 'Child111', :parent => child11)
179 child2 = WikiPage.create!(:wiki => @wiki, :title => 'Child2', :parent => page)
179 child2 = WikiPage.create!(:wiki => @wiki, :title => 'Child2', :parent => page)
180
180
181 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants.map(&:title).sort
181 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants.map(&:title).sort
182 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants(nil).map(&:title).sort
182 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants(nil).map(&:title).sort
183 assert_equal %w(Child1 Child11 Child2), page.descendants(2).map(&:title).sort
183 assert_equal %w(Child1 Child11 Child2), page.descendants(2).map(&:title).sort
184 assert_equal %w(Child1 Child2), page.descendants(1).map(&:title).sort
184 assert_equal %w(Child1 Child2), page.descendants(1).map(&:title).sort
185
185
186 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants.map(&:title).sort
186 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants.map(&:title).sort
187 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants(nil).map(&:title).sort
187 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants(nil).map(&:title).sort
188 assert_equal %w(Child1 Child11 Child2 Parent), page.self_and_descendants(2).map(&:title).sort
188 assert_equal %w(Child1 Child11 Child2 Parent), page.self_and_descendants(2).map(&:title).sort
189 assert_equal %w(Child1 Child2 Parent), page.self_and_descendants(1).map(&:title).sort
189 assert_equal %w(Child1 Child2 Parent), page.self_and_descendants(1).map(&:title).sort
190 end
190 end
191
191
192 def test_diff_for_page_with_deleted_version_should_pick_the_previous_available_version
192 def test_diff_for_page_with_deleted_version_should_pick_the_previous_available_version
193 WikiContent::Version.find_by_page_id_and_version(1, 2).destroy
193 WikiContent::Version.find_by_page_id_and_version(1, 2).destroy
194
194
195 page = WikiPage.find(1)
195 page = WikiPage.find(1)
196 diff = page.diff(3)
196 diff = page.diff(3)
197 assert_not_nil diff
197 assert_not_nil diff
198 assert_equal 3, diff.content_to.version
198 assert_equal 3, diff.content_to.version
199 assert_equal 1, diff.content_from.version
199 assert_equal 1, diff.content_from.version
200 end
200 end
201 end
201 end
General Comments 0
You need to be logged in to leave comments. Login now