##// END OF EJS Templates
Removes calls to #assert_template and #assigns in integration tests....
Jean-Philippe Lang -
r15345:e1da6de36f0d
parent child
Show More
@@ -1,338 +1,331
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class AccountTest < Redmine::IntegrationTest
21 21 fixtures :users, :email_addresses, :roles
22 22
23 23 def test_login
24 24 get "/my/page"
25 25 assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fmy%2Fpage"
26 26 log_user('jsmith', 'jsmith')
27 27
28 28 get "/my/account"
29 29 assert_response :success
30 assert_template "my/account"
31 30 end
32 31
33 32 def test_login_should_set_session_token
34 33 assert_difference 'Token.count' do
35 34 log_user('jsmith', 'jsmith')
36 35
37 36 assert_equal 2, session[:user_id]
38 37 assert_not_nil session[:tk]
39 38 end
40 39 end
41 40
42 41 def test_autologin
43 42 user = User.find(1)
44 43 Token.delete_all
45 44
46 45 with_settings :autologin => '7' do
47 46 assert_difference 'Token.count', 2 do
48 47 # User logs in with 'autologin' checked
49 48 post '/login', :username => user.login, :password => 'admin', :autologin => 1
50 49 assert_redirected_to '/my/page'
51 50 end
52 51 token = Token.where(:action => 'autologin').order(:id => :desc).first
53 52 assert_not_nil token
54 53 assert_equal user, token.user
55 54 assert_equal 'autologin', token.action
56 55 assert_equal user.id, session[:user_id]
57 56 assert_equal token.value, cookies['autologin']
58 57
59 58 # Session is cleared
60 59 reset!
61 60 User.current = nil
62 61 # Clears user's last login timestamp
63 62 user.update_attribute :last_login_on, nil
64 63 assert_nil user.reload.last_login_on
65 64
66 65 # User comes back with user's autologin cookie
67 66 cookies[:autologin] = token.value
68 67 get '/my/page'
69 68 assert_response :success
70 assert_template 'my/page'
71 69 assert_equal user.id, session[:user_id]
72 70 assert_not_nil user.reload.last_login_on
73 71 end
74 72 end
75 73
76 74 def test_autologin_should_use_autologin_cookie_name
77 75 Token.delete_all
78 76 Redmine::Configuration.stubs(:[]).with('autologin_cookie_name').returns('custom_autologin')
79 77 Redmine::Configuration.stubs(:[]).with('autologin_cookie_path').returns('/')
80 78 Redmine::Configuration.stubs(:[]).with('autologin_cookie_secure').returns(false)
81 79 Redmine::Configuration.stubs(:[]).with('sudo_mode_timeout').returns(15)
82 80
83 81 with_settings :autologin => '7' do
84 82 assert_difference 'Token.count', 2 do
85 83 post '/login', :username => 'admin', :password => 'admin', :autologin => 1
86 84 assert_response 302
87 85 end
88 86 assert cookies['custom_autologin'].present?
89 87 token = cookies['custom_autologin']
90 88
91 89 # Session is cleared
92 90 reset!
93 91 cookies['custom_autologin'] = token
94 92 get '/my/page'
95 93 assert_response :success
96 94
97 95 assert_difference 'Token.count', -2 do
98 96 post '/logout'
99 97 end
100 98 assert cookies['custom_autologin'].blank?
101 99 end
102 100 end
103 101
104 102 def test_lost_password
105 103 Token.delete_all
106 104
107 105 get "/account/lost_password"
108 106 assert_response :success
109 assert_template "account/lost_password"
110 107 assert_select 'input[name=mail]'
111 108
112 109 post "/account/lost_password", :mail => 'jSmith@somenet.foo'
113 110 assert_redirected_to "/login"
114 111
115 112 token = Token.first
116 113 assert_equal 'recovery', token.action
117 114 assert_equal 'jsmith@somenet.foo', token.user.mail
118 115 assert !token.expired?
119 116
120 117 get "/account/lost_password", :token => token.value
121 118 assert_response :success
122 assert_template "account/password_recovery"
123 119 assert_select 'input[type=hidden][name=token][value=?]', token.value
124 120 assert_select 'input[name=new_password]'
125 121 assert_select 'input[name=new_password_confirmation]'
126 122
127 123 post "/account/lost_password",
128 124 :token => token.value, :new_password => 'newpass123',
129 125 :new_password_confirmation => 'newpass123'
130 126 assert_redirected_to "/login"
131 127 assert_equal 'Password was successfully updated.', flash[:notice]
132 128
133 129 log_user('jsmith', 'newpass123')
134 130 assert_equal false, Token.exists?(token.id), "Password recovery token was not deleted"
135 131 end
136 132
137 133 def test_user_with_must_change_passwd_should_be_forced_to_change_its_password
138 134 User.find_by_login('jsmith').update_attribute :must_change_passwd, true
139 135
140 136 post '/login', :username => 'jsmith', :password => 'jsmith'
141 137 assert_redirected_to '/my/page'
142 138 follow_redirect!
143 139 assert_redirected_to '/my/password'
144 140
145 141 get '/issues'
146 142 assert_redirected_to '/my/password'
147 143 end
148 144
149 145 def test_user_with_must_change_passwd_should_be_able_to_change_its_password
150 146 User.find_by_login('jsmith').update_attribute :must_change_passwd, true
151 147
152 148 post '/login', :username => 'jsmith', :password => 'jsmith'
153 149 assert_redirected_to '/my/page'
154 150 follow_redirect!
155 151 assert_redirected_to '/my/password'
156 152 follow_redirect!
157 153 assert_response :success
158 154 post '/my/password', :password => 'jsmith', :new_password => 'newpassword', :new_password_confirmation => 'newpassword'
159 155 assert_redirected_to '/my/account'
160 156 follow_redirect!
161 157 assert_response :success
162 158
163 159 assert_equal false, User.find_by_login('jsmith').must_change_passwd?
164 160 end
165 161
166 162 def test_user_with_expired_password_should_be_forced_to_change_its_password
167 163 User.find_by_login('jsmith').update_attribute :passwd_changed_on, 14.days.ago
168 164
169 165 with_settings :password_max_age => 7 do
170 166 post '/login', :username => 'jsmith', :password => 'jsmith'
171 167 assert_redirected_to '/my/page'
172 168 follow_redirect!
173 169 assert_redirected_to '/my/password'
174 170
175 171 get '/issues'
176 172 assert_redirected_to '/my/password'
177 173 end
178 174 end
179 175
180 176 def test_user_with_expired_password_should_be_able_to_change_its_password
181 177 User.find_by_login('jsmith').update_attribute :passwd_changed_on, 14.days.ago
182 178
183 179 with_settings :password_max_age => 7 do
184 180 post '/login', :username => 'jsmith', :password => 'jsmith'
185 181 assert_redirected_to '/my/page'
186 182 follow_redirect!
187 183 assert_redirected_to '/my/password'
188 184 follow_redirect!
189 185 assert_response :success
190 186 post '/my/password', :password => 'jsmith', :new_password => 'newpassword', :new_password_confirmation => 'newpassword'
191 187 assert_redirected_to '/my/account'
192 188 follow_redirect!
193 189 assert_response :success
194 190
195 191 assert_equal false, User.find_by_login('jsmith').must_change_passwd?
196 192 end
197 193
198 194 end
199 195
200 196 def test_register_with_automatic_activation
201 197 Setting.self_registration = '3'
202 198
203 199 get '/account/register'
204 200 assert_response :success
205 assert_template 'account/register'
206 201
207 202 post '/account/register',
208 203 :user => {:login => "newuser", :language => "en",
209 204 :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar",
210 205 :password => "newpass123", :password_confirmation => "newpass123"}
211 206 assert_redirected_to '/my/account'
212 207 follow_redirect!
213 208 assert_response :success
214 assert_template 'my/account'
215 209
216 210 user = User.find_by_login('newuser')
217 211 assert_not_nil user
218 212 assert user.active?
219 213 assert_not_nil user.last_login_on
220 214 end
221 215
222 216 def test_register_with_manual_activation
223 217 Setting.self_registration = '2'
224 218
225 219 post '/account/register',
226 220 :user => {:login => "newuser", :language => "en",
227 221 :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar",
228 222 :password => "newpass123", :password_confirmation => "newpass123"}
229 223 assert_redirected_to '/login'
230 224 assert !User.find_by_login('newuser').active?
231 225 end
232 226
233 227 def test_register_with_email_activation
234 228 Setting.self_registration = '1'
235 229 Token.delete_all
236 230
237 231 post '/account/register',
238 232 :user => {:login => "newuser", :language => "en",
239 233 :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar",
240 234 :password => "newpass123", :password_confirmation => "newpass123"}
241 235 assert_redirected_to '/login'
242 236 assert !User.find_by_login('newuser').active?
243 237
244 238 token = Token.first
245 239 assert_equal 'register', token.action
246 240 assert_equal 'newuser@foo.bar', token.user.mail
247 241 assert !token.expired?
248 242
249 243 get '/account/activate', :token => token.value
250 244 assert_redirected_to '/login'
251 245 log_user('newuser', 'newpass123')
252 246 end
253 247
254 248 def test_onthefly_registration
255 249 # disable registration
256 250 Setting.self_registration = '0'
257 251 AuthSource.expects(:authenticate).returns(
258 252 {:login => 'foo', :firstname => 'Foo', :lastname => 'Smith',
259 253 :mail => 'foo@bar.com', :auth_source_id => 66})
260 254
261 255 post '/login', :username => 'foo', :password => 'bar'
262 256 assert_redirected_to '/my/page'
263 257
264 258 user = User.find_by_login('foo')
265 259 assert user.is_a?(User)
266 260 assert_equal 66, user.auth_source_id
267 261 assert user.hashed_password.blank?
268 262 end
269 263
270 264 def test_onthefly_registration_with_invalid_attributes
271 265 # disable registration
272 266 Setting.self_registration = '0'
273 267 AuthSource.expects(:authenticate).returns(
274 268 {:login => 'foo', :lastname => 'Smith', :auth_source_id => 66})
275 269
276 270 post '/login', :username => 'foo', :password => 'bar'
277 271 assert_response :success
278 assert_template 'account/register'
279 272 assert_select 'input[name=?][value=""]', 'user[firstname]'
280 273 assert_select 'input[name=?][value=Smith]', 'user[lastname]'
281 274 assert_select 'input[name=?]', 'user[login]', 0
282 275 assert_select 'input[name=?]', 'user[password]', 0
283 276
284 277 post '/account/register',
285 278 :user => {:firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com'}
286 279 assert_redirected_to '/my/account'
287 280
288 281 user = User.find_by_login('foo')
289 282 assert user.is_a?(User)
290 283 assert_equal 66, user.auth_source_id
291 284 assert user.hashed_password.blank?
292 285 end
293 286
294 287 def test_registered_user_should_be_able_to_get_a_new_activation_email
295 288 Token.delete_all
296 289
297 290 with_settings :self_registration => '1', :default_language => 'en' do
298 291 # register a new account
299 292 assert_difference 'User.count' do
300 293 assert_difference 'Token.count' do
301 294 post '/account/register',
302 295 :user => {:login => "newuser", :language => "en",
303 296 :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar",
304 297 :password => "newpass123", :password_confirmation => "newpass123"}
305 298 end
306 299 end
307 300 user = User.order('id desc').first
308 301 assert_equal User::STATUS_REGISTERED, user.status
309 302 reset!
310 303
311 304 # try to use "lost password"
312 305 assert_no_difference 'ActionMailer::Base.deliveries.size' do
313 306 post '/account/lost_password', :mail => 'newuser@foo.bar'
314 307 end
315 308 assert_redirected_to '/account/lost_password'
316 309 follow_redirect!
317 310 assert_response :success
318 311 assert_select 'div.flash', :text => /new activation email/
319 312 assert_select 'div.flash a[href="/account/activation_email"]'
320 313
321 314 # request a new action activation email
322 315 assert_difference 'ActionMailer::Base.deliveries.size' do
323 316 get '/account/activation_email'
324 317 end
325 318 assert_redirected_to '/login'
326 319 token = Token.order('id desc').first
327 320 activation_path = "/account/activate?token=#{token.value}"
328 321 assert_include activation_path, mail_body(ActionMailer::Base.deliveries.last)
329 322
330 323 # activate the account
331 324 get activation_path
332 325 assert_redirected_to '/login'
333 326
334 327 post '/login', :username => 'newuser', :password => 'newpass123'
335 328 assert_redirected_to '/my/page'
336 329 end
337 330 end
338 331 end
@@ -1,61 +1,61
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class AdminTest < Redmine::IntegrationTest
21 21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 22 :enumerations, :users, :issue_categories,
23 23 :projects_trackers,
24 24 :roles,
25 25 :member_roles,
26 26 :members,
27 27 :enabled_modules
28 28
29 29 def test_add_user
30 30 log_user("admin", "admin")
31 31 get "/users/new"
32 32 assert_response :success
33 assert_template "users/new"
33
34 34 post "/users",
35 35 :user => { :login => "psmith", :firstname => "Paul",
36 36 :lastname => "Smith", :mail => "psmith@somenet.foo",
37 37 :language => "en", :password => "psmith09",
38 38 :password_confirmation => "psmith09" }
39 39
40 40 user = User.find_by_login("psmith")
41 41 assert_kind_of User, user
42 42 assert_redirected_to "/users/#{ user.id }/edit"
43 43
44 44 logged_user = User.try_to_login("psmith", "psmith09")
45 45 assert_kind_of User, logged_user
46 46 assert_equal "Paul", logged_user.firstname
47 47
48 48 put "/users/#{user.id}", :id => user.id, :user => { :status => User::STATUS_LOCKED }
49 49 assert_redirected_to "/users/#{ user.id }/edit"
50 50 locked_user = User.try_to_login("psmith", "psmith09")
51 51 assert_equal nil, locked_user
52 52 end
53 53
54 54 test "Add a user as an anonymous user should fail" do
55 55 post '/users',
56 56 :user => { :login => 'psmith', :firstname => 'Paul'},
57 57 :password => "psmith09", :password_confirmation => "psmith09"
58 58 assert_response :redirect
59 59 assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fusers"
60 60 end
61 61 end
@@ -1,158 +1,158
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
21 21 fixtures :users
22 22
23 23 def test_api_should_deny_without_credentials
24 24 get '/users/current.xml', {}
25 25 assert_response 401
26 26 assert_equal User.anonymous, User.current
27 27 assert response.headers.has_key?('WWW-Authenticate')
28 28 end
29 29
30 30 def test_api_should_accept_http_basic_auth_using_username_and_password
31 31 user = User.generate! do |user|
32 32 user.password = 'my_password'
33 33 end
34 34 get '/users/current.xml', {}, credentials(user.login, 'my_password')
35 35 assert_response 200
36 36 assert_equal user, User.current
37 37 end
38 38
39 39 def test_api_should_deny_http_basic_auth_using_username_and_wrong_password
40 40 user = User.generate! do |user|
41 41 user.password = 'my_password'
42 42 end
43 43 get '/users/current.xml', {}, credentials(user.login, 'wrong_password')
44 44 assert_response 401
45 45 assert_equal User.anonymous, User.current
46 46 end
47 47
48 48 def test_api_should_accept_http_basic_auth_using_api_key
49 49 user = User.generate!
50 50 token = Token.create!(:user => user, :action => 'api')
51 51 get '/users/current.xml', {}, credentials(token.value, 'X')
52 52 assert_response 200
53 53 assert_equal user, User.current
54 54 end
55 55
56 56 def test_api_should_deny_http_basic_auth_using_wrong_api_key
57 57 user = User.generate!
58 58 token = Token.create!(:user => user, :action => 'feeds') # not the API key
59 59 get '/users/current.xml', {}, credentials(token.value, 'X')
60 60 assert_response 401
61 61 assert_equal User.anonymous, User.current
62 62 end
63 63
64 64 def test_api_should_accept_auth_using_api_key_as_parameter
65 65 user = User.generate!
66 66 token = Token.create!(:user => user, :action => 'api')
67 67 get "/users/current.xml?key=#{token.value}", {}
68 68 assert_response 200
69 69 assert_equal user, User.current
70 70 end
71 71
72 72 def test_api_should_deny_auth_using_wrong_api_key_as_parameter
73 73 user = User.generate!
74 74 token = Token.create!(:user => user, :action => 'feeds') # not the API key
75 75 get "/users/current.xml?key=#{token.value}", {}
76 76 assert_response 401
77 77 assert_equal User.anonymous, User.current
78 78 end
79 79
80 80 def test_api_should_accept_auth_using_api_key_as_request_header
81 81 user = User.generate!
82 82 token = Token.create!(:user => user, :action => 'api')
83 83 get "/users/current.xml", {}, {'X-Redmine-API-Key' => token.value.to_s}
84 84 assert_response 200
85 85 assert_equal user, User.current
86 86 end
87 87
88 88 def test_api_should_deny_auth_using_wrong_api_key_as_request_header
89 89 user = User.generate!
90 90 token = Token.create!(:user => user, :action => 'feeds') # not the API key
91 91 get "/users/current.xml", {}, {'X-Redmine-API-Key' => token.value.to_s}
92 92 assert_response 401
93 93 assert_equal User.anonymous, User.current
94 94 end
95 95
96 96 def test_api_should_trigger_basic_http_auth_with_basic_authorization_header
97 97 ApplicationController.any_instance.expects(:authenticate_with_http_basic).once
98 98 get '/users/current.xml', {}, credentials('jsmith')
99 99 assert_response 401
100 100 end
101 101
102 102 def test_api_should_not_trigger_basic_http_auth_with_non_basic_authorization_header
103 103 ApplicationController.any_instance.expects(:authenticate_with_http_basic).never
104 104 get '/users/current.xml', {}, 'HTTP_AUTHORIZATION' => 'Digest foo bar'
105 105 assert_response 401
106 106 end
107 107
108 108 def test_invalid_utf8_credentials_should_not_trigger_an_error
109 109 invalid_utf8 = "\x82".force_encoding('UTF-8')
110 110 assert !invalid_utf8.valid_encoding?
111 111 assert_nothing_raised do
112 112 get '/users/current.xml', {}, credentials(invalid_utf8, "foo")
113 113 end
114 114 end
115 115
116 116 def test_api_request_should_not_use_user_session
117 117 log_user('jsmith', 'jsmith')
118 118
119 119 get '/users/current'
120 120 assert_response :success
121 121
122 122 get '/users/current.json'
123 123 assert_response 401
124 124 end
125 125
126 126 def test_api_should_accept_switch_user_header_for_admin_user
127 127 user = User.find(1)
128 128 su = User.find(4)
129 129
130 130 get '/users/current', {}, {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login}
131 131 assert_response :success
132 assert_equal su, assigns(:user)
132 assert_select 'h2', :text => su.name
133 133 assert_equal su, User.current
134 134 end
135 135
136 136 def test_api_should_respond_with_412_when_trying_to_switch_to_a_invalid_user
137 137 get '/users/current', {}, {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => 'foobar'}
138 138 assert_response 412
139 139 end
140 140
141 141 def test_api_should_respond_with_412_when_trying_to_switch_to_a_locked_user
142 142 user = User.find(5)
143 143 assert user.locked?
144 144
145 145 get '/users/current', {}, {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => user.login}
146 146 assert_response 412
147 147 end
148 148
149 149 def test_api_should_not_accept_switch_user_header_for_non_admin_user
150 150 user = User.find(2)
151 151 su = User.find(4)
152 152
153 153 get '/users/current', {}, {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login}
154 154 assert_response :success
155 assert_equal user, assigns(:user)
155 assert_select 'h2', :text => user.name
156 156 assert_equal user, User.current
157 157 end
158 158 end
@@ -1,867 +1,864
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
21 21 fixtures :projects,
22 22 :users,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :issues,
27 27 :issue_statuses,
28 28 :issue_relations,
29 29 :versions,
30 30 :trackers,
31 31 :projects_trackers,
32 32 :issue_categories,
33 33 :enabled_modules,
34 34 :enumerations,
35 35 :attachments,
36 36 :workflows,
37 37 :custom_fields,
38 38 :custom_values,
39 39 :custom_fields_projects,
40 40 :custom_fields_trackers,
41 41 :time_entries,
42 42 :journals,
43 43 :journal_details,
44 44 :queries,
45 45 :attachments
46 46
47 47 test "GET /issues.xml should contain metadata" do
48 48 get '/issues.xml'
49 assert_select 'issues[type=array][total_count=?][limit="25"][offset="0"]',
50 assigns(:issue_count).to_s
49 assert_select 'issues[type=array][total_count][limit="25"][offset="0"]'
51 50 end
52 51
53 52 test "GET /issues.xml with nometa param should not contain metadata" do
54 53 get '/issues.xml?nometa=1'
55 54 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
56 55 end
57 56
58 57 test "GET /issues.xml with nometa header should not contain metadata" do
59 58 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
60 59 assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
61 60 end
62 61
63 62 test "GET /issues.xml with offset and limit" do
64 63 get '/issues.xml?offset=2&limit=3'
65
66 assert_equal 3, assigns(:limit)
67 assert_equal 2, assigns(:offset)
64 assert_select 'issues[type=array][total_count][limit="3"][offset="2"]'
68 65 assert_select 'issues issue', 3
69 66 end
70 67
71 68 test "GET /issues.xml with relations" do
72 69 get '/issues.xml?include=relations'
73 70
74 71 assert_response :success
75 72 assert_equal 'application/xml', @response.content_type
76 73
77 74 assert_select 'issue id', :text => '3' do
78 75 assert_select '~ relations relation', 1
79 76 assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
80 77 end
81 78
82 79 assert_select 'issue id', :text => '1' do
83 80 assert_select '~ relations'
84 81 assert_select '~ relations relation', 0
85 82 end
86 83 end
87 84
88 85 test "GET /issues.xml with invalid query params" do
89 86 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
90 87
91 88 assert_response :unprocessable_entity
92 89 assert_equal 'application/xml', @response.content_type
93 90 assert_select 'errors error', :text => "Start date cannot be blank"
94 91 end
95 92
96 93 test "GET /issues.xml with custom field filter" do
97 94 get '/issues.xml',
98 95 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
99 96
100 97 expected_ids = Issue.visible.
101 98 joins(:custom_values).
102 99 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
103 100 assert expected_ids.any?
104 101
105 102 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
106 103 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
107 104 end
108 105 end
109 106
110 107 test "GET /issues.xml with custom field filter (shorthand method)" do
111 108 get '/issues.xml', {:cf_1 => 'MySQL'}
112 109
113 110 expected_ids = Issue.visible.
114 111 joins(:custom_values).
115 112 where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
116 113 assert expected_ids.any?
117 114
118 115 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
119 116 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
120 117 end
121 118 end
122 119
123 120 def test_index_should_include_issue_attributes
124 121 get '/issues.xml'
125 122 assert_select 'issues>issue>is_private', :text => 'false'
126 123 end
127 124
128 125 def test_index_should_allow_timestamp_filtering
129 126 Issue.delete_all
130 127 Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
131 128 Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
132 129
133 130 get '/issues.xml',
134 131 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
135 132 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
136 133 assert_select 'issues>issue', :count => 1
137 134 assert_select 'issues>issue>subject', :text => '1'
138 135
139 136 get '/issues.xml',
140 137 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
141 138 :v => {:updated_on => ['2014-01-02T12:00:00Z']}}
142 139 assert_select 'issues>issue', :count => 1
143 140 assert_select 'issues>issue>subject', :text => '2'
144 141
145 142 get '/issues.xml',
146 143 {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
147 144 :v => {:updated_on => ['2014-01-02T08:00:00Z']}}
148 145 assert_select 'issues>issue', :count => 2
149 146 end
150 147
151 148 test "GET /issues.xml with filter" do
152 149 get '/issues.xml?status_id=5'
153 150
154 151 expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
155 152 assert expected_ids.any?
156 153
157 154 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
158 155 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
159 156 end
160 157 end
161 158
162 159 test "GET /issues.json with filter" do
163 160 get '/issues.json?status_id=5'
164 161
165 162 json = ActiveSupport::JSON.decode(response.body)
166 163 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
167 164 assert_equal 3, status_ids_used.length
168 165 assert status_ids_used.all? {|id| id == 5 }
169 166 end
170 167
171 168 test "GET /issues/:id.xml with journals" do
172 169 Journal.find(2).update_attribute(:private_notes, true)
173 170
174 171 get '/issues/1.xml?include=journals', {}, credentials('jsmith')
175 172
176 173 assert_select 'issue journals[type=array]' do
177 174 assert_select 'journal[id="1"]' do
178 175 assert_select 'private_notes', :text => 'false'
179 176 assert_select 'details[type=array]' do
180 177 assert_select 'detail[name=status_id]' do
181 178 assert_select 'old_value', :text => '1'
182 179 assert_select 'new_value', :text => '2'
183 180 end
184 181 end
185 182 end
186 183 assert_select 'journal[id="2"]' do
187 184 assert_select 'private_notes', :text => 'true'
188 185 assert_select 'details[type=array]'
189 186 end
190 187 end
191 188 end
192 189
193 190 test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
194 191 get '/issues/1.xml?include=journals'
195 192
196 193 iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
197 194 assert_select 'issue>created_on', :text => iso_date
198 195 assert_select 'issue>updated_on', :text => iso_date
199 196 assert_select 'issue journal>created_on', :text => iso_date
200 197 end
201 198
202 199 test "GET /issues/:id.xml with custom fields" do
203 200 get '/issues/3.xml'
204 201
205 202 assert_select 'issue custom_fields[type=array]' do
206 203 assert_select 'custom_field[id="1"]' do
207 204 assert_select 'value', :text => 'MySQL'
208 205 end
209 206 end
210 207 assert_nothing_raised do
211 208 Hash.from_xml(response.body).to_xml
212 209 end
213 210 end
214 211
215 212 test "GET /issues/:id.xml with multi custom fields" do
216 213 field = CustomField.find(1)
217 214 field.update_attribute :multiple, true
218 215 issue = Issue.find(3)
219 216 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
220 217 issue.save!
221 218
222 219 get '/issues/3.xml'
223 220 assert_response :success
224 221
225 222 assert_select 'issue custom_fields[type=array]' do
226 223 assert_select 'custom_field[id="1"]' do
227 224 assert_select 'value[type=array] value', 2
228 225 end
229 226 end
230 227 xml = Hash.from_xml(response.body)
231 228 custom_fields = xml['issue']['custom_fields']
232 229 assert_kind_of Array, custom_fields
233 230 field = custom_fields.detect {|f| f['id'] == '1'}
234 231 assert_kind_of Hash, field
235 232 assert_equal ['MySQL', 'Oracle'], field['value'].sort
236 233 end
237 234
238 235 test "GET /issues/:id.json with multi custom fields" do
239 236 field = CustomField.find(1)
240 237 field.update_attribute :multiple, true
241 238 issue = Issue.find(3)
242 239 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
243 240 issue.save!
244 241
245 242 get '/issues/3.json'
246 243 assert_response :success
247 244
248 245 json = ActiveSupport::JSON.decode(response.body)
249 246 custom_fields = json['issue']['custom_fields']
250 247 assert_kind_of Array, custom_fields
251 248 field = custom_fields.detect {|f| f['id'] == 1}
252 249 assert_kind_of Hash, field
253 250 assert_equal ['MySQL', 'Oracle'], field['value'].sort
254 251 end
255 252
256 253 test "GET /issues/:id.xml with empty value for multi custom field" do
257 254 field = CustomField.find(1)
258 255 field.update_attribute :multiple, true
259 256 issue = Issue.find(3)
260 257 issue.custom_field_values = {1 => ['']}
261 258 issue.save!
262 259
263 260 get '/issues/3.xml'
264 261
265 262 assert_select 'issue custom_fields[type=array]' do
266 263 assert_select 'custom_field[id="1"]' do
267 264 assert_select 'value[type=array]:empty'
268 265 end
269 266 end
270 267 xml = Hash.from_xml(response.body)
271 268 custom_fields = xml['issue']['custom_fields']
272 269 assert_kind_of Array, custom_fields
273 270 field = custom_fields.detect {|f| f['id'] == '1'}
274 271 assert_kind_of Hash, field
275 272 assert_equal [], field['value']
276 273 end
277 274
278 275 test "GET /issues/:id.json with empty value for multi custom field" do
279 276 field = CustomField.find(1)
280 277 field.update_attribute :multiple, true
281 278 issue = Issue.find(3)
282 279 issue.custom_field_values = {1 => ['']}
283 280 issue.save!
284 281
285 282 get '/issues/3.json'
286 283 assert_response :success
287 284 json = ActiveSupport::JSON.decode(response.body)
288 285 custom_fields = json['issue']['custom_fields']
289 286 assert_kind_of Array, custom_fields
290 287 field = custom_fields.detect {|f| f['id'] == 1}
291 288 assert_kind_of Hash, field
292 289 assert_equal [], field['value'].sort
293 290 end
294 291
295 292 test "GET /issues/:id.xml with attachments" do
296 293 get '/issues/3.xml?include=attachments'
297 294
298 295 assert_select 'issue attachments[type=array]' do
299 296 assert_select 'attachment', 4
300 297 assert_select 'attachment id', :text => '1' do
301 298 assert_select '~ filename', :text => 'error281.txt'
302 299 assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/1/error281.txt'
303 300 end
304 301 end
305 302 end
306 303
307 304 test "GET /issues/:id.xml with subtasks" do
308 305 issue = Issue.generate_with_descendants!(:project_id => 1)
309 306 get "/issues/#{issue.id}.xml?include=children"
310 307
311 308 assert_select 'issue id', :text => issue.id.to_s do
312 309 assert_select '~ children[type=array] > issue', 2
313 310 assert_select '~ children[type=array] > issue > children', 1
314 311 end
315 312 end
316 313
317 314 test "GET /issues/:id.json with subtasks" do
318 315 issue = Issue.generate_with_descendants!(:project_id => 1)
319 316 get "/issues/#{issue.id}.json?include=children"
320 317
321 318 json = ActiveSupport::JSON.decode(response.body)
322 319 assert_equal 2, json['issue']['children'].size
323 320 assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
324 321 end
325 322
326 323 def test_show_should_include_issue_attributes
327 324 get '/issues/1.xml'
328 325 assert_select 'issue>is_private', :text => 'false'
329 326 end
330 327
331 328 test "GET /issues/:id.xml?include=watchers should include watchers" do
332 329 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
333 330
334 331 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
335 332
336 333 assert_response :ok
337 334 assert_equal 'application/xml', response.content_type
338 335 assert_select 'issue' do
339 336 assert_select 'watchers', Issue.find(1).watchers.count
340 337 assert_select 'watchers' do
341 338 assert_select 'user[id="3"]'
342 339 end
343 340 end
344 341 end
345 342
346 343 test "GET /issues/:id.xml should not disclose associated changesets from projects the user has no access to" do
347 344 project = Project.generate!(:is_public => false)
348 345 repository = Repository::Subversion.create!(:project => project, :url => "svn://localhost")
349 346 Issue.find(1).changesets << Changeset.generate!(:repository => repository)
350 347 assert Issue.find(1).changesets.any?
351 348
352 349 get '/issues/1.xml?include=changesets', {}, credentials('jsmith')
353 350
354 351 # the user jsmith has no permission to view the associated changeset
355 352 assert_select 'issue changesets[type=array]' do
356 353 assert_select 'changeset', 0
357 354 end
358 355 end
359 356
360 357 test "GET /issues/:id.xml should contains total_estimated_hours and total_spent_hours" do
361 358 parent = Issue.find(3)
362 359 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
363 360 TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
364 361 :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
365 362 get '/issues/3.xml'
366 363
367 364 assert_equal 'application/xml', response.content_type
368 365 assert_select 'issue' do
369 366 assert_select 'estimated_hours', parent.estimated_hours.to_s
370 367 assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
371 368 assert_select 'spent_hours', parent.spent_hours.to_s
372 369 assert_select 'total_spent_hours', (parent.spent_hours.to_f + 2.5).to_s
373 370 end
374 371 end
375 372
376 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
377 374 parent = Issue.find(3)
378 375 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
379 376 # remove permission!
380 377 Role.anonymous.remove_permission! :view_time_entries
381 378 #Role.all.each { |role| role.remove_permission! :view_time_entries }
382 379 get '/issues/3.xml'
383 380
384 381 assert_equal 'application/xml', response.content_type
385 382 assert_select 'issue' do
386 383 assert_select 'estimated_hours', parent.estimated_hours.to_s
387 384 assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
388 385 assert_select 'spent_hours', false
389 386 assert_select 'total_spent_hours', false
390 387 end
391 388 end
392 389
393 390 test "GET /issues/:id.json should contains total_estimated_hours and total_spent_hours" do
394 391 parent = Issue.find(3)
395 392 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
396 393 TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
397 394 :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
398 395 get '/issues/3.json'
399 396
400 397 assert_equal 'application/json', response.content_type
401 398 json = ActiveSupport::JSON.decode(response.body)
402 399 assert_equal parent.estimated_hours, json['issue']['estimated_hours']
403 400 assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
404 401 assert_equal parent.spent_hours, json['issue']['spent_hours']
405 402 assert_equal (parent.spent_hours.to_f + 2.5), json['issue']['total_spent_hours']
406 403 end
407 404
408 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
409 406 parent = Issue.find(3)
410 407 child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
411 408 # remove permission!
412 409 Role.anonymous.remove_permission! :view_time_entries
413 410 #Role.all.each { |role| role.remove_permission! :view_time_entries }
414 411 get '/issues/3.json'
415 412
416 413 assert_equal 'application/json', response.content_type
417 414 json = ActiveSupport::JSON.decode(response.body)
418 415 assert_equal parent.estimated_hours, json['issue']['estimated_hours']
419 416 assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
420 417 assert_equal nil, json['issue']['spent_hours']
421 418 assert_equal nil, json['issue']['total_spent_hours']
422 419 end
423 420
424 421 test "POST /issues.xml should create an issue with the attributes" do
425 422
426 423 payload = <<-XML
427 424 <?xml version="1.0" encoding="UTF-8" ?>
428 425 <issue>
429 426 <project_id>1</project_id>
430 427 <tracker_id>2</tracker_id>
431 428 <status_id>3</status_id>
432 429 <subject>API test</subject>
433 430 </issue>
434 431 XML
435 432
436 433 assert_difference('Issue.count') do
437 434 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
438 435 end
439 436 issue = Issue.order('id DESC').first
440 437 assert_equal 1, issue.project_id
441 438 assert_equal 2, issue.tracker_id
442 439 assert_equal 3, issue.status_id
443 440 assert_equal 'API test', issue.subject
444 441
445 442 assert_response :created
446 443 assert_equal 'application/xml', @response.content_type
447 444 assert_select 'issue > id', :text => issue.id.to_s
448 445 end
449 446
450 447 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
451 448 assert_difference('Issue.count') do
452 449 post '/issues.xml',
453 450 {:issue => {:project_id => 1, :subject => 'Watchers',
454 451 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
455 452 assert_response :created
456 453 end
457 454 issue = Issue.order('id desc').first
458 455 assert_equal 2, issue.watchers.size
459 456 assert_equal [1, 3], issue.watcher_user_ids.sort
460 457 end
461 458
462 459 test "POST /issues.xml with failure should return errors" do
463 460 assert_no_difference('Issue.count') do
464 461 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
465 462 end
466 463
467 464 assert_select 'errors error', :text => "Subject cannot be blank"
468 465 end
469 466
470 467 test "POST /issues.json should create an issue with the attributes" do
471 468
472 469 payload = <<-JSON
473 470 {
474 471 "issue": {
475 472 "project_id": "1",
476 473 "tracker_id": "2",
477 474 "status_id": "3",
478 475 "subject": "API test"
479 476 }
480 477 }
481 478 JSON
482 479
483 480 assert_difference('Issue.count') do
484 481 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
485 482 end
486 483
487 484 issue = Issue.order('id DESC').first
488 485 assert_equal 1, issue.project_id
489 486 assert_equal 2, issue.tracker_id
490 487 assert_equal 3, issue.status_id
491 488 assert_equal 'API test', issue.subject
492 489 end
493 490
494 491 test "POST /issues.json without tracker_id should accept custom fields" do
495 492 field = IssueCustomField.generate!(
496 493 :field_format => 'list',
497 494 :multiple => true,
498 495 :possible_values => ["V1", "V2", "V3"],
499 496 :default_value => "V2",
500 497 :is_for_all => true,
501 498 :trackers => Tracker.all.to_a
502 499 )
503 500
504 501 payload = <<-JSON
505 502 {
506 503 "issue": {
507 504 "project_id": "1",
508 505 "subject": "Multivalued custom field",
509 506 "custom_field_values":{"#{field.id}":["V1","V3"]}
510 507 }
511 508 }
512 509 JSON
513 510
514 511 assert_difference('Issue.count') do
515 512 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
516 513 end
517 514
518 515 assert_response :created
519 516 issue = Issue.order('id DESC').first
520 517 assert_equal ["V1", "V3"], issue.custom_field_value(field).sort
521 518 end
522 519
523 520 test "POST /issues.json with omitted custom field should set default value" do
524 521 field = IssueCustomField.generate!(:default_value => "Default")
525 522
526 523 issue = new_record(Issue) do
527 524 post '/issues.json',
528 525 {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {}}},
529 526 credentials('jsmith')
530 527 end
531 528 assert_equal "Default", issue.custom_field_value(field)
532 529 end
533 530
534 531 test "POST /issues.json with custom field set to blank should not set default value" do
535 532 field = IssueCustomField.generate!(:default_value => "Default")
536 533
537 534 issue = new_record(Issue) do
538 535 post '/issues.json',
539 536 {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {field.id.to_s => ""}}},
540 537 credentials('jsmith')
541 538 end
542 539 assert_equal "", issue.custom_field_value(field)
543 540 end
544 541
545 542 test "POST /issues.json with failure should return errors" do
546 543 assert_no_difference('Issue.count') do
547 544 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
548 545 end
549 546
550 547 json = ActiveSupport::JSON.decode(response.body)
551 548 assert json['errors'].include?("Subject cannot be blank")
552 549 end
553 550
554 551 test "POST /issues.json with invalid project_id should respond with 422" do
555 552 post '/issues.json', {:issue => {:project_id => 999, :subject => "API"}}, credentials('jsmith')
556 553 assert_response 422
557 554 end
558 555
559 556 test "PUT /issues/:id.xml" do
560 557 assert_difference('Journal.count') do
561 558 put '/issues/6.xml',
562 559 {:issue => {:subject => 'API update', :notes => 'A new note'}},
563 560 credentials('jsmith')
564 561 end
565 562
566 563 issue = Issue.find(6)
567 564 assert_equal "API update", issue.subject
568 565 journal = Journal.last
569 566 assert_equal "A new note", journal.notes
570 567 end
571 568
572 569 test "PUT /issues/:id.xml with custom fields" do
573 570 put '/issues/3.xml',
574 571 {:issue => {:custom_fields => [
575 572 {'id' => '1', 'value' => 'PostgreSQL' },
576 573 {'id' => '2', 'value' => '150'}
577 574 ]}},
578 575 credentials('jsmith')
579 576
580 577 issue = Issue.find(3)
581 578 assert_equal '150', issue.custom_value_for(2).value
582 579 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
583 580 end
584 581
585 582 test "PUT /issues/:id.xml with multi custom fields" do
586 583 field = CustomField.find(1)
587 584 field.update_attribute :multiple, true
588 585
589 586 put '/issues/3.xml',
590 587 {:issue => {:custom_fields => [
591 588 {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
592 589 {'id' => '2', 'value' => '150'}
593 590 ]}},
594 591 credentials('jsmith')
595 592
596 593 issue = Issue.find(3)
597 594 assert_equal '150', issue.custom_value_for(2).value
598 595 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
599 596 end
600 597
601 598 test "PUT /issues/:id.xml with project change" do
602 599 put '/issues/3.xml',
603 600 {:issue => {:project_id => 2, :subject => 'Project changed'}},
604 601 credentials('jsmith')
605 602
606 603 issue = Issue.find(3)
607 604 assert_equal 2, issue.project_id
608 605 assert_equal 'Project changed', issue.subject
609 606 end
610 607
611 608 test "PUT /issues/:id.xml with notes only" do
612 609 assert_difference('Journal.count') do
613 610 put '/issues/6.xml',
614 611 {:issue => {:notes => 'Notes only'}},
615 612 credentials('jsmith')
616 613 end
617 614
618 615 journal = Journal.last
619 616 assert_equal "Notes only", journal.notes
620 617 end
621 618
622 619 test "PUT /issues/:id.json with omitted custom field should not change blank value to default value" do
623 620 field = IssueCustomField.generate!(:default_value => "Default")
624 621 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
625 622 assert_equal "", issue.reload.custom_field_value(field)
626 623
627 624 assert_difference('Journal.count') do
628 625 put "/issues/#{issue.id}.json",
629 626 {:issue => {:custom_field_values => {}, :notes => 'API'}},
630 627 credentials('jsmith')
631 628 end
632 629
633 630 assert_equal "", issue.reload.custom_field_value(field)
634 631 end
635 632
636 633 test "PUT /issues/:id.json with custom field set to blank should not change blank value to default value" do
637 634 field = IssueCustomField.generate!(:default_value => "Default")
638 635 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
639 636 assert_equal "", issue.reload.custom_field_value(field)
640 637
641 638 assert_difference('Journal.count') do
642 639 put "/issues/#{issue.id}.json",
643 640 {:issue => {:custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
644 641 credentials('jsmith')
645 642 end
646 643
647 644 assert_equal "", issue.reload.custom_field_value(field)
648 645 end
649 646
650 647 test "PUT /issues/:id.json with tracker change and omitted custom field specific to that tracker should set default value" do
651 648 field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
652 649 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
653 650
654 651 assert_difference('Journal.count') do
655 652 put "/issues/#{issue.id}.json",
656 653 {:issue => {:tracker_id => 2, :custom_field_values => {}, :notes => 'API'}},
657 654 credentials('jsmith')
658 655 end
659 656
660 657 assert_equal 2, issue.reload.tracker_id
661 658 assert_equal "Default", issue.reload.custom_field_value(field)
662 659 end
663 660
664 661 test "PUT /issues/:id.json with tracker change and custom field specific to that tracker set to blank should not set default value" do
665 662 field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
666 663 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
667 664
668 665 assert_difference('Journal.count') do
669 666 put "/issues/#{issue.id}.json",
670 667 {:issue => {:tracker_id => 2, :custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
671 668 credentials('jsmith')
672 669 end
673 670
674 671 assert_equal 2, issue.reload.tracker_id
675 672 assert_equal "", issue.reload.custom_field_value(field)
676 673 end
677 674
678 675 test "PUT /issues/:id.xml with failed update" do
679 676 put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith')
680 677
681 678 assert_response :unprocessable_entity
682 679 assert_select 'errors error', :text => "Subject cannot be blank"
683 680 end
684 681
685 682 test "PUT /issues/:id.json" do
686 683 assert_difference('Journal.count') do
687 684 put '/issues/6.json',
688 685 {:issue => {:subject => 'API update', :notes => 'A new note'}},
689 686 credentials('jsmith')
690 687
691 688 assert_response :ok
692 689 assert_equal '', response.body
693 690 end
694 691
695 692 issue = Issue.find(6)
696 693 assert_equal "API update", issue.subject
697 694 journal = Journal.last
698 695 assert_equal "A new note", journal.notes
699 696 end
700 697
701 698 test "PUT /issues/:id.json with failed update" do
702 699 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
703 700
704 701 assert_response :unprocessable_entity
705 702 json = ActiveSupport::JSON.decode(response.body)
706 703 assert json['errors'].include?("Subject cannot be blank")
707 704 end
708 705
709 706 test "DELETE /issues/:id.xml" do
710 707 assert_difference('Issue.count', -1) do
711 708 delete '/issues/6.xml', {}, credentials('jsmith')
712 709
713 710 assert_response :ok
714 711 assert_equal '', response.body
715 712 end
716 713 assert_nil Issue.find_by_id(6)
717 714 end
718 715
719 716 test "DELETE /issues/:id.json" do
720 717 assert_difference('Issue.count', -1) do
721 718 delete '/issues/6.json', {}, credentials('jsmith')
722 719
723 720 assert_response :ok
724 721 assert_equal '', response.body
725 722 end
726 723 assert_nil Issue.find_by_id(6)
727 724 end
728 725
729 726 test "POST /issues/:id/watchers.xml should add watcher" do
730 727 assert_difference 'Watcher.count' do
731 728 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
732 729
733 730 assert_response :ok
734 731 assert_equal '', response.body
735 732 end
736 733 watcher = Watcher.order('id desc').first
737 734 assert_equal Issue.find(1), watcher.watchable
738 735 assert_equal User.find(3), watcher.user
739 736 end
740 737
741 738 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
742 739 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
743 740
744 741 assert_difference 'Watcher.count', -1 do
745 742 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
746 743
747 744 assert_response :ok
748 745 assert_equal '', response.body
749 746 end
750 747 assert_equal false, Issue.find(1).watched_by?(User.find(3))
751 748 end
752 749
753 750 def test_create_issue_with_uploaded_file
754 751 token = xml_upload('test_create_with_upload', credentials('jsmith'))
755 752 attachment = Attachment.find_by_token(token)
756 753
757 754 # create the issue with the upload's token
758 755 assert_difference 'Issue.count' do
759 756 post '/issues.xml',
760 757 {:issue => {:project_id => 1, :subject => 'Uploaded file',
761 758 :uploads => [{:token => token, :filename => 'test.txt',
762 759 :content_type => 'text/plain'}]}},
763 760 credentials('jsmith')
764 761 assert_response :created
765 762 end
766 763 issue = Issue.order('id DESC').first
767 764 assert_equal 1, issue.attachments.count
768 765 assert_equal attachment, issue.attachments.first
769 766
770 767 attachment.reload
771 768 assert_equal 'test.txt', attachment.filename
772 769 assert_equal 'text/plain', attachment.content_type
773 770 assert_equal 'test_create_with_upload'.size, attachment.filesize
774 771 assert_equal 2, attachment.author_id
775 772
776 773 # get the issue with its attachments
777 774 get "/issues/#{issue.id}.xml", :include => 'attachments'
778 775 assert_response :success
779 776 xml = Hash.from_xml(response.body)
780 777 attachments = xml['issue']['attachments']
781 778 assert_kind_of Array, attachments
782 779 assert_equal 1, attachments.size
783 780 url = attachments.first['content_url']
784 781 assert_not_nil url
785 782
786 783 # download the attachment
787 784 get url
788 785 assert_response :success
789 786 assert_equal 'test_create_with_upload', response.body
790 787 end
791 788
792 789 def test_create_issue_with_multiple_uploaded_files_as_xml
793 790 token1 = xml_upload('File content 1', credentials('jsmith'))
794 791 token2 = xml_upload('File content 2', credentials('jsmith'))
795 792
796 793 payload = <<-XML
797 794 <?xml version="1.0" encoding="UTF-8" ?>
798 795 <issue>
799 796 <project_id>1</project_id>
800 797 <tracker_id>1</tracker_id>
801 798 <subject>Issue with multiple attachments</subject>
802 799 <uploads type="array">
803 800 <upload>
804 801 <token>#{token1}</token>
805 802 <filename>test1.txt</filename>
806 803 </upload>
807 804 <upload>
808 805 <token>#{token2}</token>
809 806 <filename>test1.txt</filename>
810 807 </upload>
811 808 </uploads>
812 809 </issue>
813 810 XML
814 811
815 812 assert_difference 'Issue.count' do
816 813 post '/issues.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
817 814 assert_response :created
818 815 end
819 816 issue = Issue.order('id DESC').first
820 817 assert_equal 2, issue.attachments.count
821 818 end
822 819
823 820 def test_create_issue_with_multiple_uploaded_files_as_json
824 821 token1 = json_upload('File content 1', credentials('jsmith'))
825 822 token2 = json_upload('File content 2', credentials('jsmith'))
826 823
827 824 payload = <<-JSON
828 825 {
829 826 "issue": {
830 827 "project_id": "1",
831 828 "tracker_id": "1",
832 829 "subject": "Issue with multiple attachments",
833 830 "uploads": [
834 831 {"token": "#{token1}", "filename": "test1.txt"},
835 832 {"token": "#{token2}", "filename": "test2.txt"}
836 833 ]
837 834 }
838 835 }
839 836 JSON
840 837
841 838 assert_difference 'Issue.count' do
842 839 post '/issues.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
843 840 assert_response :created
844 841 end
845 842 issue = Issue.order('id DESC').first
846 843 assert_equal 2, issue.attachments.count
847 844 end
848 845
849 846 def test_update_issue_with_uploaded_file
850 847 token = xml_upload('test_upload_with_upload', credentials('jsmith'))
851 848 attachment = Attachment.find_by_token(token)
852 849
853 850 # update the issue with the upload's token
854 851 assert_difference 'Journal.count' do
855 852 put '/issues/1.xml',
856 853 {:issue => {:notes => 'Attachment added',
857 854 :uploads => [{:token => token, :filename => 'test.txt',
858 855 :content_type => 'text/plain'}]}},
859 856 credentials('jsmith')
860 857 assert_response :ok
861 858 assert_equal '', @response.body
862 859 end
863 860
864 861 issue = Issue.find(1)
865 862 assert_include attachment, issue.attachments
866 863 end
867 864 end
@@ -1,62 +1,62
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::RolesTest < Redmine::ApiTest::Base
21 21 fixtures :roles
22 22
23 23 test "GET /roles.xml should return the roles" do
24 24 get '/roles.xml'
25 25
26 26 assert_response :success
27 27 assert_equal 'application/xml', @response.content_type
28 assert_equal 3, assigns(:roles).size
29 28
29 assert_select 'roles role', 3
30 30 assert_select 'roles[type=array] role id', :text => '2' do
31 31 assert_select '~ name', :text => 'Developer'
32 32 end
33 33 end
34 34
35 35 test "GET /roles.json should return the roles" do
36 36 get '/roles.json'
37 37
38 38 assert_response :success
39 39 assert_equal 'application/json', @response.content_type
40 assert_equal 3, assigns(:roles).size
41 40
42 41 json = ActiveSupport::JSON.decode(response.body)
43 42 assert_kind_of Hash, json
44 43 assert_kind_of Array, json['roles']
44 assert_equal 3, json['roles'].size
45 45 assert_include({'id' => 2, 'name' => 'Developer'}, json['roles'])
46 46 end
47 47
48 48 test "GET /roles/:id.xml should return the role" do
49 49 get '/roles/1.xml'
50 50
51 51 assert_response :success
52 52 assert_equal 'application/xml', @response.content_type
53 53
54 54 assert_select 'role' do
55 55 assert_select 'name', :text => 'Manager'
56 56 assert_select 'role permissions[type=array]' do
57 57 assert_select 'permission', Role.find(1).permissions.size
58 58 assert_select 'permission', :text => 'view_issues'
59 59 end
60 60 end
61 61 end
62 62 end
@@ -1,92 +1,92
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::SearchTest < Redmine::ApiTest::Base
21 21 fixtures :projects, :projects_trackers,
22 22 :enabled_modules, :roles, :users, :members, :member_roles,
23 23 :issues, :trackers, :issue_statuses, :enumerations,
24 24 :workflows,
25 25 :custom_fields, :custom_values,
26 26 :custom_fields_projects, :custom_fields_trackers,
27 27 :repositories, :changesets
28 28
29 29 test "GET /search.xml should return xml content" do
30 30 get '/search.xml'
31 31
32 32 assert_response :success
33 33 assert_equal 'application/xml', @response.content_type
34 34 end
35 35
36 36 test "GET /search.json should return json content" do
37 37 get '/search.json'
38 38
39 39 assert_response :success
40 40 assert_equal 'application/json', @response.content_type
41 41
42 42 json = ActiveSupport::JSON.decode(response.body)
43 43 assert_kind_of Hash, json
44 44 assert_kind_of Array, json['results']
45 45 end
46 46
47 47 test "GET /search.xml without query strings should return empty results" do
48 48 get '/search.xml', :q => '', :all_words => ''
49 49
50 50 assert_response :success
51 assert_equal 0, assigns(:results).size
51 assert_select 'result', 0
52 52 end
53 53
54 54 test "GET /search.xml with query strings should return results" do
55 get '/search.xml', :q => 'recipe subproject commit', :all_words => ''
55 issue = Issue.generate!(:subject => 'searchapi')
56
57 get '/search.xml', :q => 'searchapi', :all_words => ''
56 58
57 59 assert_response :success
58 assert_not_empty(assigns(:results))
59 60
60 61 assert_select 'results[type=array]' do
61 assert_select 'result', :count => assigns(:results).count
62 assigns(:results).size.times.each do |i|
63 assert_select 'result' do
64 assert_select 'id', :text => assigns(:results)[i].id.to_s
65 assert_select 'title', :text => assigns(:results)[i].event_title
66 assert_select 'type', :text => assigns(:results)[i].event_type
67 assert_select 'url', :text => url_for(assigns(:results)[i].event_url(:only_path => false))
68 assert_select 'description', :text => assigns(:results)[i].event_description
69 assert_select 'datetime'
70 end
62 assert_select 'result', 1
63
64 assert_select 'result' do
65 assert_select 'id', :text => issue.id.to_s
66 assert_select 'title', :text => "Bug ##{issue.id} (New): searchapi"
67 assert_select 'type', :text => 'issue'
68 assert_select 'url', :text => "http://www.example.com/issues/#{issue.id}"
69 assert_select 'description', :text => ''
70 assert_select 'datetime'
71 71 end
72 72 end
73 73 end
74 74
75 75 test "GET /search.xml should paginate" do
76 76 issue = (0..10).map {Issue.generate! :subject => 'search_with_limited_results'}.reverse.map(&:id)
77 77
78 78 get '/search.json', :q => 'search_with_limited_results', :limit => 4
79 79 json = ActiveSupport::JSON.decode(response.body)
80 80 assert_equal 11, json['total_count']
81 81 assert_equal 0, json['offset']
82 82 assert_equal 4, json['limit']
83 83 assert_equal issue[0..3], json['results'].map {|r| r['id']}
84 84
85 85 get '/search.json', :q => 'search_with_limited_results', :offset => 8, :limit => 4
86 86 json = ActiveSupport::JSON.decode(response.body)
87 87 assert_equal 11, json['total_count']
88 88 assert_equal 8, json['offset']
89 89 assert_equal 4, json['limit']
90 90 assert_equal issue[8..10], json['results'].map {|r| r['id']}
91 91 end
92 92 end
@@ -1,294 +1,294
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class Redmine::ApiTest::UsersTest < Redmine::ApiTest::Base
21 21 fixtures :users, :email_addresses, :members, :member_roles, :roles, :projects
22 22
23 23 test "GET /users.xml should return users" do
24 24 get '/users.xml', {}, credentials('admin')
25 25
26 26 assert_response :success
27 27 assert_equal 'application/xml', response.content_type
28 28 assert_select 'users' do
29 assert_select 'user', assigns(:users).size
29 assert_select 'user', User.active.count
30 30 end
31 31 end
32 32
33 33 test "GET /users.json should return users" do
34 34 get '/users.json', {}, credentials('admin')
35 35
36 36 assert_response :success
37 37 assert_equal 'application/json', response.content_type
38 38 json = ActiveSupport::JSON.decode(response.body)
39 39 assert json.key?('users')
40 assert_equal assigns(:users).size, json['users'].size
40 assert_equal User.active.count, json['users'].size
41 41 end
42 42
43 43 test "GET /users/:id.xml should return the user" do
44 44 get '/users/2.xml'
45 45
46 46 assert_response :success
47 47 assert_select 'user id', :text => '2'
48 48 end
49 49
50 50 test "GET /users/:id.json should return the user" do
51 51 get '/users/2.json'
52 52
53 53 assert_response :success
54 54 json = ActiveSupport::JSON.decode(response.body)
55 55 assert_kind_of Hash, json
56 56 assert_kind_of Hash, json['user']
57 57 assert_equal 2, json['user']['id']
58 58 end
59 59
60 60 test "GET /users/:id.xml with include=memberships should include memberships" do
61 61 get '/users/2.xml?include=memberships'
62 62
63 63 assert_response :success
64 64 assert_select 'user memberships', 1
65 65 end
66 66
67 67 test "GET /users/:id.json with include=memberships should include memberships" do
68 68 get '/users/2.json?include=memberships'
69 69
70 70 assert_response :success
71 71 json = ActiveSupport::JSON.decode(response.body)
72 72 assert_kind_of Array, json['user']['memberships']
73 73 assert_equal [{
74 74 "id"=>1,
75 75 "project"=>{"name"=>"eCookbook", "id"=>1},
76 76 "roles"=>[{"name"=>"Manager", "id"=>1}]
77 77 }], json['user']['memberships']
78 78 end
79 79
80 80 test "GET /users/current.xml should require authentication" do
81 81 get '/users/current.xml'
82 82
83 83 assert_response 401
84 84 end
85 85
86 86 test "GET /users/current.xml should return current user" do
87 87 get '/users/current.xml', {}, credentials('jsmith')
88 88
89 89 assert_select 'user id', :text => '2'
90 90 end
91 91
92 92 test "GET /users/:id should not return login for other user" do
93 93 get '/users/3.xml', {}, credentials('jsmith')
94 94 assert_response :success
95 95 assert_select 'user login', 0
96 96 end
97 97
98 98 test "GET /users/:id should return login for current user" do
99 99 get '/users/2.xml', {}, credentials('jsmith')
100 100 assert_response :success
101 101 assert_select 'user login', :text => 'jsmith'
102 102 end
103 103
104 104 test "GET /users/:id should not return api_key for other user" do
105 105 get '/users/3.xml', {}, credentials('jsmith')
106 106 assert_response :success
107 107 assert_select 'user api_key', 0
108 108 end
109 109
110 110 test "GET /users/:id should return api_key for current user" do
111 111 get '/users/2.xml', {}, credentials('jsmith')
112 112 assert_response :success
113 113 assert_select 'user api_key', :text => User.find(2).api_key
114 114 end
115 115
116 116 test "GET /users/:id should not return status for standard user" do
117 117 get '/users/3.xml', {}, credentials('jsmith')
118 118 assert_response :success
119 119 assert_select 'user status', 0
120 120 end
121 121
122 122 test "GET /users/:id should return status for administrators" do
123 123 get '/users/2.xml', {}, credentials('admin')
124 124 assert_response :success
125 125 assert_select 'user status', :text => User.find(1).status.to_s
126 126 end
127 127
128 128 test "POST /users.xml with valid parameters should create the user" do
129 129 assert_difference('User.count') do
130 130 post '/users.xml', {
131 131 :user => {
132 132 :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
133 133 :mail => 'foo@example.net', :password => 'secret123',
134 134 :mail_notification => 'only_assigned'}
135 135 },
136 136 credentials('admin')
137 137 end
138 138
139 139 user = User.order('id DESC').first
140 140 assert_equal 'foo', user.login
141 141 assert_equal 'Firstname', user.firstname
142 142 assert_equal 'Lastname', user.lastname
143 143 assert_equal 'foo@example.net', user.mail
144 144 assert_equal 'only_assigned', user.mail_notification
145 145 assert !user.admin?
146 146 assert user.check_password?('secret123')
147 147
148 148 assert_response :created
149 149 assert_equal 'application/xml', @response.content_type
150 150 assert_select 'user id', :text => user.id.to_s
151 151 end
152 152
153 153 test "POST /users.json with valid parameters should create the user" do
154 154 assert_difference('User.count') do
155 155 post '/users.json', {
156 156 :user => {
157 157 :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
158 158 :mail => 'foo@example.net', :password => 'secret123',
159 159 :mail_notification => 'only_assigned'}
160 160 },
161 161 credentials('admin')
162 162 end
163 163
164 164 user = User.order('id DESC').first
165 165 assert_equal 'foo', user.login
166 166 assert_equal 'Firstname', user.firstname
167 167 assert_equal 'Lastname', user.lastname
168 168 assert_equal 'foo@example.net', user.mail
169 169 assert !user.admin?
170 170
171 171 assert_response :created
172 172 assert_equal 'application/json', @response.content_type
173 173 json = ActiveSupport::JSON.decode(response.body)
174 174 assert_kind_of Hash, json
175 175 assert_kind_of Hash, json['user']
176 176 assert_equal user.id, json['user']['id']
177 177 end
178 178
179 179 test "POST /users.xml with with invalid parameters should return errors" do
180 180 assert_no_difference('User.count') do
181 181 post '/users.xml', {:user => {:login => 'foo', :lastname => 'Lastname', :mail => 'foo'}}, credentials('admin')
182 182 end
183 183
184 184 assert_response :unprocessable_entity
185 185 assert_equal 'application/xml', @response.content_type
186 186 assert_select 'errors error', :text => "First name cannot be blank"
187 187 end
188 188
189 189 test "POST /users.json with with invalid parameters should return errors" do
190 190 assert_no_difference('User.count') do
191 191 post '/users.json', {:user => {:login => 'foo', :lastname => 'Lastname', :mail => 'foo'}}, credentials('admin')
192 192 end
193 193
194 194 assert_response :unprocessable_entity
195 195 assert_equal 'application/json', @response.content_type
196 196 json = ActiveSupport::JSON.decode(response.body)
197 197 assert_kind_of Hash, json
198 198 assert json.has_key?('errors')
199 199 assert_kind_of Array, json['errors']
200 200 end
201 201
202 202 test "PUT /users/:id.xml with valid parameters should update the user" do
203 203 assert_no_difference('User.count') do
204 204 put '/users/2.xml', {
205 205 :user => {
206 206 :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed',
207 207 :mail => 'jsmith@somenet.foo'}
208 208 },
209 209 credentials('admin')
210 210 end
211 211
212 212 user = User.find(2)
213 213 assert_equal 'jsmith', user.login
214 214 assert_equal 'John', user.firstname
215 215 assert_equal 'Renamed', user.lastname
216 216 assert_equal 'jsmith@somenet.foo', user.mail
217 217 assert !user.admin?
218 218
219 219 assert_response :ok
220 220 assert_equal '', @response.body
221 221 end
222 222
223 223 test "PUT /users/:id.json with valid parameters should update the user" do
224 224 assert_no_difference('User.count') do
225 225 put '/users/2.json', {
226 226 :user => {
227 227 :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed',
228 228 :mail => 'jsmith@somenet.foo'}
229 229 },
230 230 credentials('admin')
231 231 end
232 232
233 233 user = User.find(2)
234 234 assert_equal 'jsmith', user.login
235 235 assert_equal 'John', user.firstname
236 236 assert_equal 'Renamed', user.lastname
237 237 assert_equal 'jsmith@somenet.foo', user.mail
238 238 assert !user.admin?
239 239
240 240 assert_response :ok
241 241 assert_equal '', @response.body
242 242 end
243 243
244 244 test "PUT /users/:id.xml with invalid parameters" do
245 245 assert_no_difference('User.count') do
246 246 put '/users/2.xml', {
247 247 :user => {
248 248 :login => 'jsmith', :firstname => '', :lastname => 'Lastname',
249 249 :mail => 'foo'}
250 250 },
251 251 credentials('admin')
252 252 end
253 253
254 254 assert_response :unprocessable_entity
255 255 assert_equal 'application/xml', @response.content_type
256 256 assert_select 'errors error', :text => "First name cannot be blank"
257 257 end
258 258
259 259 test "PUT /users/:id.json with invalid parameters" do
260 260 assert_no_difference('User.count') do
261 261 put '/users/2.json', {
262 262 :user => {
263 263 :login => 'jsmith', :firstname => '', :lastname => 'Lastname',
264 264 :mail => 'foo'}
265 265 },
266 266 credentials('admin')
267 267 end
268 268
269 269 assert_response :unprocessable_entity
270 270 assert_equal 'application/json', @response.content_type
271 271 json = ActiveSupport::JSON.decode(response.body)
272 272 assert_kind_of Hash, json
273 273 assert json.has_key?('errors')
274 274 assert_kind_of Array, json['errors']
275 275 end
276 276
277 277 test "DELETE /users/:id.xml should delete the user" do
278 278 assert_difference('User.count', -1) do
279 279 delete '/users/2.xml', {}, credentials('admin')
280 280 end
281 281
282 282 assert_response :ok
283 283 assert_equal '', @response.body
284 284 end
285 285
286 286 test "DELETE /users/:id.json should delete the user" do
287 287 assert_difference('User.count', -1) do
288 288 delete '/users/2.json', {}, credentials('admin')
289 289 end
290 290
291 291 assert_response :ok
292 292 assert_equal '', @response.body
293 293 end
294 294 end
@@ -1,244 +1,242
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class IssuesTest < Redmine::IntegrationTest
21 21 fixtures :projects,
22 22 :users, :email_addresses,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :trackers,
27 27 :projects_trackers,
28 28 :enabled_modules,
29 29 :issue_statuses,
30 30 :issues,
31 31 :enumerations,
32 32 :custom_fields,
33 33 :custom_values,
34 34 :custom_fields_trackers,
35 35 :attachments
36 36
37 37 # create an issue
38 38 def test_add_issue
39 39 log_user('jsmith', 'jsmith')
40 40
41 41 get '/projects/ecookbook/issues/new'
42 42 assert_response :success
43 assert_template 'issues/new'
44 43
45 44 issue = new_record(Issue) do
46 45 post '/projects/ecookbook/issues',
47 46 :issue => { :tracker_id => "1",
48 47 :start_date => "2006-12-26",
49 48 :priority_id => "4",
50 49 :subject => "new test issue",
51 50 :category_id => "",
52 51 :description => "new issue",
53 52 :done_ratio => "0",
54 53 :due_date => "",
55 54 :assigned_to_id => "" },
56 55 :custom_fields => {'2' => 'Value for field 2'}
57 56 end
58 57 # check redirection
59 58 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
60 59 follow_redirect!
61 assert_equal issue, assigns(:issue)
62 60
63 61 # check issue attributes
64 62 assert_equal 'jsmith', issue.author.login
65 63 assert_equal 1, issue.project.id
66 64 assert_equal 1, issue.status.id
67 65 end
68 66
69 67 def test_create_issue_by_anonymous_without_permission_should_fail
70 68 Role.anonymous.remove_permission! :add_issues
71 69
72 70 assert_no_difference 'Issue.count' do
73 71 post '/projects/1/issues', :tracker_id => "1", :issue => {:subject => "new test issue"}
74 72 end
75 73 assert_response 302
76 74 end
77 75
78 76 def test_create_issue_by_anonymous_with_custom_permission_should_succeed
79 77 Role.anonymous.remove_permission! :add_issues
80 78 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [3])
81 79
82 80 issue = new_record(Issue) do
83 81 post '/projects/1/issues', :tracker_id => "1", :issue => {:subject => "new test issue"}
84 82 assert_response 302
85 83 end
86 84 assert_equal User.anonymous, issue.author
87 85 end
88 86
89 87 # add then remove 2 attachments to an issue
90 88 def test_issue_attachments
91 89 log_user('jsmith', 'jsmith')
92 90 set_tmp_attachments_directory
93 91
94 92 attachment = new_record(Attachment) do
95 93 put '/issues/1',
96 94 :notes => 'Some notes',
97 95 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'This is an attachment'}}
98 96 assert_redirected_to "/issues/1"
99 97 end
100 98
101 99 assert_equal Issue.find(1), attachment.container
102 100 assert_equal 'testfile.txt', attachment.filename
103 101 assert_equal 'This is an attachment', attachment.description
104 102 # verify the size of the attachment stored in db
105 103 #assert_equal file_data_1.length, attachment.filesize
106 104 # verify that the attachment was written to disk
107 105 assert File.exist?(attachment.diskfile)
108 106
109 107 # remove the attachments
110 108 Issue.find(1).attachments.each(&:destroy)
111 109 assert_equal 0, Issue.find(1).attachments.length
112 110 end
113 111
114 112 def test_other_formats_links_on_index
115 113 get '/projects/ecookbook/issues'
116 114
117 115 %w(Atom PDF CSV).each do |format|
118 116 assert_select 'a[rel=nofollow][href=?]', "/projects/ecookbook/issues.#{format.downcase}", :text => format
119 117 end
120 118 end
121 119
122 120 def test_other_formats_links_on_index_without_project_id_in_url
123 121 get '/issues', :project_id => 'ecookbook'
124 122
125 123 %w(Atom PDF CSV).each do |format|
126 124 assert_select 'a[rel=nofollow][href=?]', "/issues.#{format.downcase}?project_id=ecookbook", :text => format
127 125 end
128 126 end
129 127
130 128 def test_pagination_links_on_index
131 129 with_settings :per_page_options => '2' do
132 130 get '/projects/ecookbook/issues'
133 131
134 132 assert_select 'a[href=?]', '/projects/ecookbook/issues?page=2', :text => '2'
135 133 end
136 134 end
137 135
138 136 def test_pagination_links_should_preserve_query_parameters
139 137 with_settings :per_page_options => '2' do
140 138 get '/projects/ecookbook/issues?foo=bar'
141 139
142 140 assert_select 'a[href=?]', '/projects/ecookbook/issues?foo=bar&page=2', :text => '2'
143 141 end
144 142 end
145 143
146 144 def test_pagination_links_should_not_use_params_as_url_options
147 145 with_settings :per_page_options => '2' do
148 146 get '/projects/ecookbook/issues?host=foo'
149 147
150 148 assert_select 'a[href=?]', '/projects/ecookbook/issues?host=foo&page=2', :text => '2'
151 149 end
152 150 end
153 151
154 152 def test_sort_links_on_index
155 153 get '/projects/ecookbook/issues'
156 154
157 155 assert_select 'a[href=?]', '/projects/ecookbook/issues?sort=subject%2Cid%3Adesc', :text => 'Subject'
158 156 end
159 157
160 158 def test_sort_links_should_preserve_query_parameters
161 159 get '/projects/ecookbook/issues?foo=bar'
162 160
163 161 assert_select 'a[href=?]', '/projects/ecookbook/issues?foo=bar&sort=subject%2Cid%3Adesc', :text => 'Subject'
164 162 end
165 163
166 164 def test_sort_links_should_not_use_params_as_url_options
167 165 get '/projects/ecookbook/issues?host=foo'
168 166
169 167 assert_select 'a[href=?]', '/projects/ecookbook/issues?host=foo&sort=subject%2Cid%3Adesc', :text => 'Subject'
170 168 end
171 169
172 170 def test_issue_with_user_custom_field
173 171 @field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :trackers => Tracker.all)
174 172 Role.anonymous.add_permission! :add_issues, :edit_issues
175 173 users = Project.find(1).users.sort
176 174 tester = users.first
177 175
178 176 # Issue form
179 177 get '/projects/ecookbook/issues/new'
180 178 assert_response :success
181 179 assert_select 'select[name=?]', "issue[custom_field_values][#{@field.id}]" do
182 180 assert_select 'option', users.size + 1 # +1 for blank value
183 181 assert_select 'option[value=?]', tester.id.to_s, :text => tester.name
184 182 end
185 183
186 184 # Create issue
187 185 issue = new_record(Issue) do
188 186 post '/projects/ecookbook/issues',
189 187 :issue => {
190 188 :tracker_id => '1',
191 189 :priority_id => '4',
192 190 :subject => 'Issue with user custom field',
193 191 :custom_field_values => {@field.id.to_s => users.first.id.to_s}
194 192 }
195 193 assert_response 302
196 194 end
197 195
198 196 # Issue view
199 197 follow_redirect!
200 198 assert_select ".cf_#{@field.id}" do
201 199 assert_select '.label', :text => 'Tester:'
202 200 assert_select '.value', :text => tester.name
203 201 end
204 202 assert_select 'select[name=?]', "issue[custom_field_values][#{@field.id}]" do
205 203 assert_select 'option', users.size + 1 # +1 for blank value
206 204 assert_select 'option[value=?][selected=selected]', tester.id.to_s, :text => tester.name
207 205 end
208 206
209 207 new_tester = users[1]
210 208 with_settings :default_language => 'en' do
211 209 # Update issue
212 210 assert_difference 'Journal.count' do
213 211 put "/issues/#{issue.id}",
214 212 :notes => 'Updating custom field',
215 213 :issue => {
216 214 :custom_field_values => {@field.id.to_s => new_tester.id.to_s}
217 215 }
218 216 assert_redirected_to "/issues/#{issue.id}"
219 217 end
220 218 # Issue view
221 219 follow_redirect!
222 220 assert_select 'ul.details li', :text => "Tester changed from #{tester} to #{new_tester}"
223 221 end
224 222 end
225 223
226 224 def test_update_using_invalid_http_verbs
227 225 subject = 'Updated by an invalid http verb'
228 226
229 227 get '/issues/update/1', {:issue => {:subject => subject}}, credentials('jsmith')
230 228 assert_response 404
231 229 assert_not_equal subject, Issue.find(1).subject
232 230
233 231 post '/issues/1', {:issue => {:subject => subject}}, credentials('jsmith')
234 232 assert_response 404
235 233 assert_not_equal subject, Issue.find(1).subject
236 234 end
237 235
238 236 def test_get_watch_should_be_invalid
239 237 assert_no_difference 'Watcher.count' do
240 238 get '/watchers/watch?object_type=issue&object_id=1', {}, credentials('jsmith')
241 239 assert_response 404
242 240 end
243 241 end
244 242 end
@@ -1,51 +1,51
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class ProjectsTest < Redmine::IntegrationTest
21 21 fixtures :projects, :users, :members, :enabled_modules
22 22
23 23 def test_archive_project
24 24 subproject = Project.find(1).children.first
25 25 log_user("admin", "admin")
26 26 get "/admin/projects"
27 27 assert_response :success
28 assert_template "admin/projects"
28
29 29 post "/projects/1/archive"
30 30 assert_redirected_to "/admin/projects"
31 31 assert !Project.find(1).active?
32 32
33 33 get '/projects/1'
34 34 assert_response 403
35 35 get "/projects/#{subproject.id}"
36 36 assert_response 403
37 37
38 38 post "/projects/1/unarchive"
39 39 assert_redirected_to "/admin/projects"
40 40 assert Project.find(1).active?
41 41 get "/projects/1"
42 42 assert_response :success
43 43 end
44 44
45 45 def test_modules_should_not_allow_get
46 46 assert_no_difference 'EnabledModule.count' do
47 47 get '/projects/1/modules', {:enabled_module_names => ['']}, credentials('jsmith')
48 48 assert_response 404
49 49 end
50 50 end
51 51 end
@@ -1,191 +1,191
1 1 require File.expand_path('../../test_helper', __FILE__)
2 2
3 3 class SudoModeTest < Redmine::IntegrationTest
4 4 fixtures :projects, :members, :member_roles, :roles, :users, :email_addresses
5 5
6 6 def setup
7 7 Redmine::SudoMode.stubs(:enabled?).returns(true)
8 8 end
9 9
10 10 def test_sudo_mode_should_be_active_after_login
11 11 log_user("admin", "admin")
12 12 get "/users/new"
13 13 assert_response :success
14 14 post "/users",
15 15 :user => { :login => "psmith", :firstname => "Paul",
16 16 :lastname => "Smith", :mail => "psmith@somenet.foo",
17 17 :language => "en", :password => "psmith09",
18 18 :password_confirmation => "psmith09" }
19 19 assert_response 302
20 20
21 21 user = User.find_by_login("psmith")
22 22 assert_kind_of User, user
23 23 end
24 24
25 25 def test_add_user
26 26 log_user("admin", "admin")
27 27 expire_sudo_mode!
28 28 get "/users/new"
29 29 assert_response :success
30 30 post "/users",
31 31 :user => { :login => "psmith", :firstname => "Paul",
32 32 :lastname => "Smith", :mail => "psmith@somenet.foo",
33 33 :language => "en", :password => "psmith09",
34 34 :password_confirmation => "psmith09" }
35 35 assert_response :success
36 36 assert_nil User.find_by_login("psmith")
37 37
38 38 assert_select 'input[name=?][value=?]', 'user[login]', 'psmith'
39 39 assert_select 'input[name=?][value=?]', 'user[firstname]', 'Paul'
40 40
41 41 post "/users",
42 42 :user => { :login => "psmith", :firstname => "Paul",
43 43 :lastname => "Smith", :mail => "psmith@somenet.foo",
44 44 :language => "en", :password => "psmith09",
45 45 :password_confirmation => "psmith09" },
46 46 :sudo_password => 'admin'
47 47 assert_response 302
48 48
49 49 user = User.find_by_login("psmith")
50 50 assert_kind_of User, user
51 51 end
52 52
53 53 def test_create_member_xhr
54 54 log_user 'admin', 'admin'
55 55 expire_sudo_mode!
56 56 get '/projects/ecookbook/settings/members'
57 57 assert_response :success
58 58
59 59 assert_no_difference 'Member.count' do
60 60 xhr :post, '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}
61 61 end
62 62
63 63 assert_no_difference 'Member.count' do
64 64 xhr :post, '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}, sudo_password: ''
65 65 end
66 66
67 67 assert_no_difference 'Member.count' do
68 68 xhr :post, '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}, sudo_password: 'wrong'
69 69 end
70 70
71 71 assert_difference 'Member.count' do
72 72 xhr :post, '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}, sudo_password: 'admin'
73 73 end
74 74 assert User.find(7).member_of?(Project.find(1))
75 75 end
76 76
77 77 def test_create_member
78 78 log_user 'admin', 'admin'
79 79 expire_sudo_mode!
80 80 get '/projects/ecookbook/settings/members'
81 81 assert_response :success
82 82
83 83 assert_no_difference 'Member.count' do
84 84 post '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}
85 85 end
86 86
87 87 assert_no_difference 'Member.count' do
88 88 post '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}, sudo_password: ''
89 89 end
90 90
91 91 assert_no_difference 'Member.count' do
92 92 post '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}, sudo_password: 'wrong'
93 93 end
94 94
95 95 assert_difference 'Member.count' do
96 96 post '/projects/ecookbook/memberships', membership: {role_ids: [1], user_id: 7}, sudo_password: 'admin'
97 97 end
98 98
99 99 assert_redirected_to '/projects/ecookbook/settings/members'
100 100 assert User.find(7).member_of?(Project.find(1))
101 101 end
102 102
103 103 def test_create_role
104 104 log_user 'admin', 'admin'
105 105 expire_sudo_mode!
106 106 get '/roles'
107 107 assert_response :success
108 108
109 109 get '/roles/new'
110 110 assert_response :success
111 111
112 112 post '/roles', role: { }
113 113 assert_response :success
114 114 assert_select 'h2', 'Confirm your password to continue'
115 115 assert_select 'form[action="/roles"]'
116 assert assigns(:sudo_form).errors.blank?
116 assert_select '#flash_error', 0
117 117
118 118 post '/roles', role: { name: 'new role', issues_visibility: 'all' }
119 119 assert_response :success
120 120 assert_select 'h2', 'Confirm your password to continue'
121 121 assert_select 'form[action="/roles"]'
122 assert_match /"new role"/, response.body
123 assert assigns(:sudo_form).errors.blank?
122 assert_select 'input[type=hidden][name=?][value=?]', 'role[name]', 'new role'
123 assert_select '#flash_error', 0
124 124
125 125 post '/roles', role: { name: 'new role', issues_visibility: 'all' }, sudo_password: 'wrong'
126 126 assert_response :success
127 127 assert_select 'h2', 'Confirm your password to continue'
128 128 assert_select 'form[action="/roles"]'
129 assert_match /"new role"/, response.body
130 assert assigns(:sudo_form).errors[:password].present?
129 assert_select 'input[type=hidden][name=?][value=?]', 'role[name]', 'new role'
130 assert_select '#flash_error'
131 131
132 132 assert_difference 'Role.count' do
133 133 post '/roles', role: { name: 'new role', issues_visibility: 'all', assignable: '1', permissions: %w(view_calendar) }, sudo_password: 'admin'
134 134 end
135 135 assert_redirected_to '/roles'
136 136 end
137 137
138 138 def test_update_email_address
139 139 log_user 'jsmith', 'jsmith'
140 140 expire_sudo_mode!
141 141 get '/my/account'
142 142 assert_response :success
143 143 post '/my/account', user: { mail: 'newmail@test.com' }
144 144 assert_response :success
145 145 assert_select 'h2', 'Confirm your password to continue'
146 146 assert_select 'form[action="/my/account"]'
147 assert_match /"newmail@test\.com"/, response.body
148 assert assigns(:sudo_form).errors.blank?
147 assert_select 'input[type=hidden][name=?][value=?]', 'user[mail]', 'newmail@test.com'
148 assert_select '#flash_error', 0
149 149
150 150 # wrong password
151 151 post '/my/account', user: { mail: 'newmail@test.com' }, sudo_password: 'wrong'
152 152 assert_response :success
153 153 assert_select 'h2', 'Confirm your password to continue'
154 154 assert_select 'form[action="/my/account"]'
155 assert_match /"newmail@test\.com"/, response.body
156 assert assigns(:sudo_form).errors[:password].present?
155 assert_select 'input[type=hidden][name=?][value=?]', 'user[mail]', 'newmail@test.com'
156 assert_select '#flash_error'
157 157
158 158 # correct password
159 159 post '/my/account', user: { mail: 'newmail@test.com' }, sudo_password: 'jsmith'
160 160 assert_redirected_to '/my/account'
161 161 assert_equal 'newmail@test.com', User.find_by_login('jsmith').mail
162 162
163 163 # sudo mode should now be active and not require password again
164 164 post '/my/account', user: { mail: 'even.newer.mail@test.com' }
165 165 assert_redirected_to '/my/account'
166 166 assert_equal 'even.newer.mail@test.com', User.find_by_login('jsmith').mail
167 167 end
168 168
169 169 def test_sudo_mode_should_skip_api_requests
170 170 with_settings :rest_api_enabled => '1' do
171 171 assert_difference('User.count') do
172 172 post '/users.json', {
173 173 :user => {
174 174 :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
175 175 :mail => 'foo@example.net', :password => 'secret123',
176 176 :mail_notification => 'only_assigned'}
177 177 },
178 178 credentials('admin')
179 179
180 180 assert_response :created
181 181 end
182 182 end
183 183 end
184 184
185 185 private
186 186
187 187 # sudo mode is active after sign, let it expire by advancing the time
188 188 def expire_sudo_mode!
189 189 travel_to 20.minutes.from_now
190 190 end
191 191 end
@@ -1,401 +1,401
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 if ENV["COVERAGE"]
19 19 require 'simplecov'
20 20 require File.expand_path(File.dirname(__FILE__) + "/coverage/html_formatter")
21 21 SimpleCov.formatter = Redmine::Coverage::HtmlFormatter
22 22 SimpleCov.start 'rails'
23 23 end
24 24
25 25 $redmine_test_ldap_server = ENV['REDMINE_TEST_LDAP_SERVER'] || '127.0.0.1'
26 26
27 27 ENV["RAILS_ENV"] = "test"
28 28 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
29 29 require 'rails/test_help'
30 30 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
31 31
32 32 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
33 33 include ObjectHelpers
34 34
35 35 require 'net/ldap'
36 36 require 'mocha/setup'
37 37
38 38 Redmine::SudoMode.disable!
39 39
40 40 class ActionView::TestCase
41 41 helper :application
42 42 include ApplicationHelper
43 43 end
44 44
45 45 class ActiveSupport::TestCase
46 46 include ActionDispatch::TestProcess
47 47
48 48 self.use_transactional_fixtures = true
49 49 self.use_instantiated_fixtures = false
50 50
51 51 def uploaded_test_file(name, mime)
52 52 fixture_file_upload("files/#{name}", mime, true)
53 53 end
54 54
55 55 # Mock out a file
56 56 def self.mock_file
57 57 file = 'a_file.png'
58 58 file.stubs(:size).returns(32)
59 59 file.stubs(:original_filename).returns('a_file.png')
60 60 file.stubs(:content_type).returns('image/png')
61 61 file.stubs(:read).returns(false)
62 62 file
63 63 end
64 64
65 65 def mock_file
66 66 self.class.mock_file
67 67 end
68 68
69 69 def mock_file_with_options(options={})
70 70 file = ''
71 71 file.stubs(:size).returns(32)
72 72 original_filename = options[:original_filename] || nil
73 73 file.stubs(:original_filename).returns(original_filename)
74 74 content_type = options[:content_type] || nil
75 75 file.stubs(:content_type).returns(content_type)
76 76 file.stubs(:read).returns(false)
77 77 file
78 78 end
79 79
80 80 # Use a temporary directory for attachment related tests
81 81 def set_tmp_attachments_directory
82 82 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
83 83 unless File.directory?("#{Rails.root}/tmp/test/attachments")
84 84 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
85 85 end
86 86 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
87 87 end
88 88
89 89 def set_fixtures_attachments_directory
90 90 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
91 91 end
92 92
93 93 def with_settings(options, &block)
94 94 saved_settings = options.keys.inject({}) do |h, k|
95 95 h[k] = case Setting[k]
96 96 when Symbol, false, true, nil
97 97 Setting[k]
98 98 else
99 99 Setting[k].dup
100 100 end
101 101 h
102 102 end
103 103 options.each {|k, v| Setting[k] = v}
104 104 yield
105 105 ensure
106 106 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
107 107 end
108 108
109 109 # Yields the block with user as the current user
110 110 def with_current_user(user, &block)
111 111 saved_user = User.current
112 112 User.current = user
113 113 yield
114 114 ensure
115 115 User.current = saved_user
116 116 end
117 117
118 118 def with_locale(locale, &block)
119 119 saved_localed = ::I18n.locale
120 120 ::I18n.locale = locale
121 121 yield
122 122 ensure
123 123 ::I18n.locale = saved_localed
124 124 end
125 125
126 126 def self.ldap_configured?
127 127 @test_ldap = Net::LDAP.new(:host => $redmine_test_ldap_server, :port => 389)
128 128 return @test_ldap.bind
129 129 rescue Exception => e
130 130 # LDAP is not listening
131 131 return nil
132 132 end
133 133
134 134 def self.convert_installed?
135 135 Redmine::Thumbnail.convert_available?
136 136 end
137 137
138 138 def convert_installed?
139 139 self.class.convert_installed?
140 140 end
141 141
142 142 # Returns the path to the test +vendor+ repository
143 143 def self.repository_path(vendor)
144 144 path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
145 145 # Unlike ruby, JRuby returns Rails.root with backslashes under Windows
146 146 path.tr("\\", "/")
147 147 end
148 148
149 149 # Returns the url of the subversion test repository
150 150 def self.subversion_repository_url
151 151 path = repository_path('subversion')
152 152 path = '/' + path unless path.starts_with?('/')
153 153 "file://#{path}"
154 154 end
155 155
156 156 # Returns true if the +vendor+ test repository is configured
157 157 def self.repository_configured?(vendor)
158 158 File.directory?(repository_path(vendor))
159 159 end
160 160
161 161 def repository_path_hash(arr)
162 162 hs = {}
163 163 hs[:path] = arr.join("/")
164 164 hs[:param] = arr.join("/")
165 165 hs
166 166 end
167 167
168 168 def sqlite?
169 169 ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
170 170 end
171 171
172 172 def mysql?
173 173 ActiveRecord::Base.connection.adapter_name =~ /mysql/i
174 174 end
175 175
176 176 def postgresql?
177 177 ActiveRecord::Base.connection.adapter_name =~ /postgresql/i
178 178 end
179 179
180 180 def quoted_date(date)
181 181 date = Date.parse(date) if date.is_a?(String)
182 182 ActiveRecord::Base.connection.quoted_date(date)
183 183 end
184 184
185 185 # Asserts that a new record for the given class is created
186 186 # and returns it
187 187 def new_record(klass, &block)
188 188 new_records(klass, 1, &block).first
189 189 end
190 190
191 191 # Asserts that count new records for the given class are created
192 192 # and returns them as an array order by object id
193 193 def new_records(klass, count, &block)
194 194 assert_difference "#{klass}.count", count do
195 195 yield
196 196 end
197 197 klass.order(:id => :desc).limit(count).to_a.reverse
198 198 end
199 199
200 200 def assert_save(object)
201 201 saved = object.save
202 202 message = "#{object.class} could not be saved"
203 203 errors = object.errors.full_messages.map {|m| "- #{m}"}
204 204 message << ":\n#{errors.join("\n")}" if errors.any?
205 205 assert_equal true, saved, message
206 206 end
207 207
208 208 def assert_select_error(arg)
209 209 assert_select '#errorExplanation', :text => arg
210 210 end
211 211
212 212 def assert_include(expected, s, message=nil)
213 213 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
214 214 end
215 215
216 216 def assert_not_include(expected, s, message=nil)
217 217 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
218 218 end
219 219
220 220 def assert_select_in(text, *args, &block)
221 221 d = Nokogiri::HTML(CGI::unescapeHTML(String.new(text))).root
222 222 assert_select(d, *args, &block)
223 223 end
224 224
225 225 def assert_select_email(*args, &block)
226 226 email = ActionMailer::Base.deliveries.last
227 227 assert_not_nil email
228 228 html_body = email.parts.detect {|part| part.content_type.include?('text/html')}.try(&:body)
229 229 assert_not_nil html_body
230 230 assert_select_in html_body.encoded, *args, &block
231 231 end
232 232
233 233 def assert_mail_body_match(expected, mail, message=nil)
234 234 if expected.is_a?(String)
235 235 assert_include expected, mail_body(mail), message
236 236 else
237 237 assert_match expected, mail_body(mail), message
238 238 end
239 239 end
240 240
241 241 def assert_mail_body_no_match(expected, mail, message=nil)
242 242 if expected.is_a?(String)
243 243 assert_not_include expected, mail_body(mail), message
244 244 else
245 245 assert_no_match expected, mail_body(mail), message
246 246 end
247 247 end
248 248
249 249 def mail_body(mail)
250 250 mail.parts.first.body.encoded
251 251 end
252 252
253 253 # Returns the lft value for a new root issue
254 254 def new_issue_lft
255 255 1
256 256 end
257 257 end
258 258
259 259 module Redmine
260 260 class RoutingTest < ActionDispatch::IntegrationTest
261 261 def should_route(arg)
262 262 arg = arg.dup
263 263 request = arg.keys.detect {|key| key.is_a?(String)}
264 264 raise ArgumentError unless request
265 265 options = arg.slice!(request)
266 266
267 267 raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/
268 268 method, path = $1.downcase.to_sym, $2
269 269
270 270 raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/
271 271 controller, action = $1, $2
272 272
273 273 assert_routing(
274 274 {:method => method, :path => path},
275 275 options.merge(:controller => controller, :action => action)
276 276 )
277 277 end
278 278 end
279 279
280 280 class HelperTest < ActionView::TestCase
281 281
282 282 end
283 283
284 284 class ControllerTest < ActionController::TestCase
285 285 # Returns the issues that are displayed in the list in the same order
286 286 def issues_in_list
287 287 ids = css_select('tr.issue td.id').map(&:text).map(&:to_i)
288 288 Issue.where(:id => ids).sort_by {|issue| ids.index(issue.id)}
289 289 end
290 290
291 291 # Return the columns that are displayed in the list
292 292 def columns_in_issues_list
293 293 css_select('table.issues thead th:not(.checkbox)').map(&:text)
294 294 end
295 295
296 296 # Verifies that the query filters match the expected filters
297 297 def assert_query_filters(expected_filters)
298 298 response.body =~ /initFilters\(\);\s*((addFilter\(.+\);\s*)*)/
299 299 filter_init = $1.to_s
300 300
301 301 expected_filters.each do |field, operator, values|
302 302 s = "addFilter(#{field.to_json}, #{operator.to_json}, #{Array(values).to_json});"
303 303 assert_include s, filter_init
304 304 end
305 305 assert_equal expected_filters.size, filter_init.scan("addFilter").size, "filters counts don't match"
306 306 end
307 307
308 308 def process(method, path, parameters={}, session={}, flash={})
309 309 if parameters.key?(:params) || parameters.key?(:session)
310 310 raise ArgumentError if session.present?
311 311 super method, path, parameters[:params], parameters[:session], parameters.except(:params, :session)
312 312 else
313 313 super
314 314 end
315 315 end
316 316 end
317 317
318 318 class IntegrationTest < ActionDispatch::IntegrationTest
319 319 def log_user(login, password)
320 320 User.anonymous
321 321 get "/login"
322 322 assert_equal nil, session[:user_id]
323 323 assert_response :success
324 assert_template "account/login"
324
325 325 post "/login", :username => login, :password => password
326 326 assert_equal login, User.find(session[:user_id]).login
327 327 end
328 328
329 329 def credentials(user, password=nil)
330 330 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
331 331 end
332 332 end
333 333
334 334 module ApiTest
335 335 API_FORMATS = %w(json xml).freeze
336 336
337 337 # Base class for API tests
338 338 class Base < Redmine::IntegrationTest
339 339 def setup
340 340 Setting.rest_api_enabled = '1'
341 341 end
342 342
343 343 def teardown
344 344 Setting.rest_api_enabled = '0'
345 345 end
346 346
347 347 # Uploads content using the XML API and returns the attachment token
348 348 def xml_upload(content, credentials)
349 349 upload('xml', content, credentials)
350 350 end
351 351
352 352 # Uploads content using the JSON API and returns the attachment token
353 353 def json_upload(content, credentials)
354 354 upload('json', content, credentials)
355 355 end
356 356
357 357 def upload(format, content, credentials)
358 358 set_tmp_attachments_directory
359 359 assert_difference 'Attachment.count' do
360 360 post "/uploads.#{format}", content, {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials)
361 361 assert_response :created
362 362 end
363 363 data = response_data
364 364 assert_kind_of Hash, data['upload']
365 365 token = data['upload']['token']
366 366 assert_not_nil token
367 367 token
368 368 end
369 369
370 370 # Parses the response body based on its content type
371 371 def response_data
372 372 unless response.content_type.to_s =~ /^application\/(.+)/
373 373 raise "Unexpected response type: #{response.content_type}"
374 374 end
375 375 format = $1
376 376 case format
377 377 when 'xml'
378 378 Hash.from_xml(response.body)
379 379 when 'json'
380 380 ActiveSupport::JSON.decode(response.body)
381 381 else
382 382 raise "Unknown response format: #{format}"
383 383 end
384 384 end
385 385 end
386 386
387 387 class Routing < Redmine::RoutingTest
388 388 def should_route(arg)
389 389 arg = arg.dup
390 390 request = arg.keys.detect {|key| key.is_a?(String)}
391 391 raise ArgumentError unless request
392 392 options = arg.slice!(request)
393 393
394 394 API_FORMATS.each do |format|
395 395 format_request = request.sub /$/, ".#{format}"
396 396 super options.merge(format_request => arg[request], :format => format)
397 397 end
398 398 end
399 399 end
400 400 end
401 401 end
General Comments 0
You need to be logged in to leave comments. Login now