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