##// END OF EJS Templates
define constant of escaped "Can't" for tests...
Toshi MARUYAMA -
r12518:b825198e0fd2
parent child
Show More
@@ -1,484 +1,486
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 #require 'shoulda'
19 19 ENV["RAILS_ENV"] = "test"
20 20 require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
21 21 require 'rails/test_help'
22 22 require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s
23 23
24 24 require File.expand_path(File.dirname(__FILE__) + '/object_helpers')
25 25 include ObjectHelpers
26 26
27 27 require 'awesome_nested_set/version'
28 28
29 29 class ActiveSupport::TestCase
30 30 include ActionDispatch::TestProcess
31 31
32 32 self.use_transactional_fixtures = true
33 33 self.use_instantiated_fixtures = false
34 34
35 ESCAPED_CANT = 'can't'
35 ESCAPED_CANT = 'can't'
36 ESCAPED_UCANT = 'Can't'
36 37 # Rails 4.0.2
37 #ESCAPED_CANT = 'can't'
38 #ESCAPED_CANT = 'can't'
39 #ESCAPED_UCANT = 'Can't'
38 40
39 41 def log_user(login, password)
40 42 User.anonymous
41 43 get "/login"
42 44 assert_equal nil, session[:user_id]
43 45 assert_response :success
44 46 assert_template "account/login"
45 47 post "/login", :username => login, :password => password
46 48 assert_equal login, User.find(session[:user_id]).login
47 49 end
48 50
49 51 def uploaded_test_file(name, mime)
50 52 fixture_file_upload("files/#{name}", mime, true)
51 53 end
52 54
53 55 def credentials(user, password=nil)
54 56 {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)}
55 57 end
56 58
57 59 # Mock out a file
58 60 def self.mock_file
59 61 file = 'a_file.png'
60 62 file.stubs(:size).returns(32)
61 63 file.stubs(:original_filename).returns('a_file.png')
62 64 file.stubs(:content_type).returns('image/png')
63 65 file.stubs(:read).returns(false)
64 66 file
65 67 end
66 68
67 69 def mock_file
68 70 self.class.mock_file
69 71 end
70 72
71 73 def mock_file_with_options(options={})
72 74 file = ''
73 75 file.stubs(:size).returns(32)
74 76 original_filename = options[:original_filename] || nil
75 77 file.stubs(:original_filename).returns(original_filename)
76 78 content_type = options[:content_type] || nil
77 79 file.stubs(:content_type).returns(content_type)
78 80 file.stubs(:read).returns(false)
79 81 file
80 82 end
81 83
82 84 # Use a temporary directory for attachment related tests
83 85 def set_tmp_attachments_directory
84 86 Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test")
85 87 unless File.directory?("#{Rails.root}/tmp/test/attachments")
86 88 Dir.mkdir "#{Rails.root}/tmp/test/attachments"
87 89 end
88 90 Attachment.storage_path = "#{Rails.root}/tmp/test/attachments"
89 91 end
90 92
91 93 def set_fixtures_attachments_directory
92 94 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
93 95 end
94 96
95 97 def with_settings(options, &block)
96 98 saved_settings = options.keys.inject({}) do |h, k|
97 99 h[k] = case Setting[k]
98 100 when Symbol, false, true, nil
99 101 Setting[k]
100 102 else
101 103 Setting[k].dup
102 104 end
103 105 h
104 106 end
105 107 options.each {|k, v| Setting[k] = v}
106 108 yield
107 109 ensure
108 110 saved_settings.each {|k, v| Setting[k] = v} if saved_settings
109 111 end
110 112
111 113 # Yields the block with user as the current user
112 114 def with_current_user(user, &block)
113 115 saved_user = User.current
114 116 User.current = user
115 117 yield
116 118 ensure
117 119 User.current = saved_user
118 120 end
119 121
120 122 def change_user_password(login, new_password)
121 123 user = User.where(:login => login).first
122 124 user.password, user.password_confirmation = new_password, new_password
123 125 user.save!
124 126 end
125 127
126 128 def self.ldap_configured?
127 129 @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389)
128 130 return @test_ldap.bind
129 131 rescue Exception => e
130 132 # LDAP is not listening
131 133 return nil
132 134 end
133 135
134 136 def self.convert_installed?
135 137 Redmine::Thumbnail.convert_available?
136 138 end
137 139
138 140 # Returns the path to the test +vendor+ repository
139 141 def self.repository_path(vendor)
140 142 Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s
141 143 end
142 144
143 145 # Returns the url of the subversion test repository
144 146 def self.subversion_repository_url
145 147 path = repository_path('subversion')
146 148 path = '/' + path unless path.starts_with?('/')
147 149 "file://#{path}"
148 150 end
149 151
150 152 # Returns true if the +vendor+ test repository is configured
151 153 def self.repository_configured?(vendor)
152 154 File.directory?(repository_path(vendor))
153 155 end
154 156
155 157 def repository_path_hash(arr)
156 158 hs = {}
157 159 hs[:path] = arr.join("/")
158 160 hs[:param] = arr.join("/")
159 161 hs
160 162 end
161 163
162 164 def assert_save(object)
163 165 saved = object.save
164 166 message = "#{object.class} could not be saved"
165 167 errors = object.errors.full_messages.map {|m| "- #{m}"}
166 168 message << ":\n#{errors.join("\n")}" if errors.any?
167 169 assert_equal true, saved, message
168 170 end
169 171
170 172 def assert_error_tag(options={})
171 173 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
172 174 end
173 175
174 176 def assert_include(expected, s, message=nil)
175 177 assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"")
176 178 end
177 179
178 180 def assert_not_include(expected, s, message=nil)
179 181 assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"")
180 182 end
181 183
182 184 def assert_select_in(text, *args, &block)
183 185 d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root
184 186 assert_select(d, *args, &block)
185 187 end
186 188
187 189 def assert_mail_body_match(expected, mail, message=nil)
188 190 if expected.is_a?(String)
189 191 assert_include expected, mail_body(mail), message
190 192 else
191 193 assert_match expected, mail_body(mail), message
192 194 end
193 195 end
194 196
195 197 def assert_mail_body_no_match(expected, mail, message=nil)
196 198 if expected.is_a?(String)
197 199 assert_not_include expected, mail_body(mail), message
198 200 else
199 201 assert_no_match expected, mail_body(mail), message
200 202 end
201 203 end
202 204
203 205 def mail_body(mail)
204 206 mail.parts.first.body.encoded
205 207 end
206 208
207 209 # awesome_nested_set new node lft and rgt value changed this refactor revision.
208 210 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61
209 211 # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line.
210 212 # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273
211 213 # It seems correct behavior because of this line comment.
212 214 # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278
213 215 def new_issue_lft
214 216 ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1
215 217 end
216 218 end
217 219
218 220 module Redmine
219 221 module ApiTest
220 222 # Base class for API tests
221 223 class Base < ActionDispatch::IntegrationTest
222 224 # Test that a request allows the three types of API authentication
223 225 #
224 226 # * HTTP Basic with username and password
225 227 # * HTTP Basic with an api key for the username
226 228 # * Key based with the key=X parameter
227 229 #
228 230 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
229 231 # @param [String] url the request url
230 232 # @param [optional, Hash] parameters additional request parameters
231 233 # @param [optional, Hash] options additional options
232 234 # @option options [Symbol] :success_code Successful response code (:success)
233 235 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
234 236 def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
235 237 should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
236 238 should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
237 239 should_allow_key_based_auth(http_method, url, parameters, options)
238 240 end
239 241
240 242 # Test that a request allows the username and password for HTTP BASIC
241 243 #
242 244 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
243 245 # @param [String] url the request url
244 246 # @param [optional, Hash] parameters additional request parameters
245 247 # @param [optional, Hash] options additional options
246 248 # @option options [Symbol] :success_code Successful response code (:success)
247 249 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
248 250 def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
249 251 success_code = options[:success_code] || :success
250 252 failure_code = options[:failure_code] || :unauthorized
251 253
252 254 context "should allow http basic auth using a username and password for #{http_method} #{url}" do
253 255 context "with a valid HTTP authentication" do
254 256 setup do
255 257 @user = User.generate! do |user|
256 258 user.admin = true
257 259 user.password = 'my_password'
258 260 end
259 261 send(http_method, url, parameters, credentials(@user.login, 'my_password'))
260 262 end
261 263
262 264 should_respond_with success_code
263 265 should_respond_with_content_type_based_on_url(url)
264 266 should "login as the user" do
265 267 assert_equal @user, User.current
266 268 end
267 269 end
268 270
269 271 context "with an invalid HTTP authentication" do
270 272 setup do
271 273 @user = User.generate!
272 274 send(http_method, url, parameters, credentials(@user.login, 'wrong_password'))
273 275 end
274 276
275 277 should_respond_with failure_code
276 278 should_respond_with_content_type_based_on_url(url)
277 279 should "not login as the user" do
278 280 assert_equal User.anonymous, User.current
279 281 end
280 282 end
281 283
282 284 context "without credentials" do
283 285 setup do
284 286 send(http_method, url, parameters)
285 287 end
286 288
287 289 should_respond_with failure_code
288 290 should_respond_with_content_type_based_on_url(url)
289 291 should "include_www_authenticate_header" do
290 292 assert @controller.response.headers.has_key?('WWW-Authenticate')
291 293 end
292 294 end
293 295 end
294 296 end
295 297
296 298 # Test that a request allows the API key with HTTP BASIC
297 299 #
298 300 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
299 301 # @param [String] url the request url
300 302 # @param [optional, Hash] parameters additional request parameters
301 303 # @param [optional, Hash] options additional options
302 304 # @option options [Symbol] :success_code Successful response code (:success)
303 305 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
304 306 def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
305 307 success_code = options[:success_code] || :success
306 308 failure_code = options[:failure_code] || :unauthorized
307 309
308 310 context "should allow http basic auth with a key for #{http_method} #{url}" do
309 311 context "with a valid HTTP authentication using the API token" do
310 312 setup do
311 313 @user = User.generate! do |user|
312 314 user.admin = true
313 315 end
314 316 @token = Token.create!(:user => @user, :action => 'api')
315 317 send(http_method, url, parameters, credentials(@token.value, 'X'))
316 318 end
317 319 should_respond_with success_code
318 320 should_respond_with_content_type_based_on_url(url)
319 321 should_be_a_valid_response_string_based_on_url(url)
320 322 should "login as the user" do
321 323 assert_equal @user, User.current
322 324 end
323 325 end
324 326
325 327 context "with an invalid HTTP authentication" do
326 328 setup do
327 329 @user = User.generate!
328 330 @token = Token.create!(:user => @user, :action => 'feeds')
329 331 send(http_method, url, parameters, credentials(@token.value, 'X'))
330 332 end
331 333 should_respond_with failure_code
332 334 should_respond_with_content_type_based_on_url(url)
333 335 should "not login as the user" do
334 336 assert_equal User.anonymous, User.current
335 337 end
336 338 end
337 339 end
338 340 end
339 341
340 342 # Test that a request allows full key authentication
341 343 #
342 344 # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
343 345 # @param [String] url the request url, without the key=ZXY parameter
344 346 # @param [optional, Hash] parameters additional request parameters
345 347 # @param [optional, Hash] options additional options
346 348 # @option options [Symbol] :success_code Successful response code (:success)
347 349 # @option options [Symbol] :failure_code Failure response code (:unauthorized)
348 350 def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
349 351 success_code = options[:success_code] || :success
350 352 failure_code = options[:failure_code] || :unauthorized
351 353
352 354 context "should allow key based auth using key=X for #{http_method} #{url}" do
353 355 context "with a valid api token" do
354 356 setup do
355 357 @user = User.generate! do |user|
356 358 user.admin = true
357 359 end
358 360 @token = Token.create!(:user => @user, :action => 'api')
359 361 # Simple url parse to add on ?key= or &key=
360 362 request_url = if url.match(/\?/)
361 363 url + "&key=#{@token.value}"
362 364 else
363 365 url + "?key=#{@token.value}"
364 366 end
365 367 send(http_method, request_url, parameters)
366 368 end
367 369 should_respond_with success_code
368 370 should_respond_with_content_type_based_on_url(url)
369 371 should_be_a_valid_response_string_based_on_url(url)
370 372 should "login as the user" do
371 373 assert_equal @user, User.current
372 374 end
373 375 end
374 376
375 377 context "with an invalid api token" do
376 378 setup do
377 379 @user = User.generate! do |user|
378 380 user.admin = true
379 381 end
380 382 @token = Token.create!(:user => @user, :action => 'feeds')
381 383 # Simple url parse to add on ?key= or &key=
382 384 request_url = if url.match(/\?/)
383 385 url + "&key=#{@token.value}"
384 386 else
385 387 url + "?key=#{@token.value}"
386 388 end
387 389 send(http_method, request_url, parameters)
388 390 end
389 391 should_respond_with failure_code
390 392 should_respond_with_content_type_based_on_url(url)
391 393 should "not login as the user" do
392 394 assert_equal User.anonymous, User.current
393 395 end
394 396 end
395 397 end
396 398
397 399 context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
398 400 setup do
399 401 @user = User.generate! do |user|
400 402 user.admin = true
401 403 end
402 404 @token = Token.create!(:user => @user, :action => 'api')
403 405 send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
404 406 end
405 407 should_respond_with success_code
406 408 should_respond_with_content_type_based_on_url(url)
407 409 should_be_a_valid_response_string_based_on_url(url)
408 410 should "login as the user" do
409 411 assert_equal @user, User.current
410 412 end
411 413 end
412 414 end
413 415
414 416 # Uses should_respond_with_content_type based on what's in the url:
415 417 #
416 418 # '/project/issues.xml' => should_respond_with_content_type :xml
417 419 # '/project/issues.json' => should_respond_with_content_type :json
418 420 #
419 421 # @param [String] url Request
420 422 def self.should_respond_with_content_type_based_on_url(url)
421 423 case
422 424 when url.match(/xml/i)
423 425 should "respond with XML" do
424 426 assert_equal 'application/xml', @response.content_type
425 427 end
426 428 when url.match(/json/i)
427 429 should "respond with JSON" do
428 430 assert_equal 'application/json', @response.content_type
429 431 end
430 432 else
431 433 raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
432 434 end
433 435 end
434 436
435 437 # Uses the url to assert which format the response should be in
436 438 #
437 439 # '/project/issues.xml' => should_be_a_valid_xml_string
438 440 # '/project/issues.json' => should_be_a_valid_json_string
439 441 #
440 442 # @param [String] url Request
441 443 def self.should_be_a_valid_response_string_based_on_url(url)
442 444 case
443 445 when url.match(/xml/i)
444 446 should_be_a_valid_xml_string
445 447 when url.match(/json/i)
446 448 should_be_a_valid_json_string
447 449 else
448 450 raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
449 451 end
450 452 end
451 453
452 454 # Checks that the response is a valid JSON string
453 455 def self.should_be_a_valid_json_string
454 456 should "be a valid JSON string (or empty)" do
455 457 assert(response.body.blank? || ActiveSupport::JSON.decode(response.body))
456 458 end
457 459 end
458 460
459 461 # Checks that the response is a valid XML string
460 462 def self.should_be_a_valid_xml_string
461 463 should "be a valid XML string" do
462 464 assert REXML::Document.new(response.body)
463 465 end
464 466 end
465 467
466 468 def self.should_respond_with(status)
467 469 should "respond with #{status}" do
468 470 assert_response status
469 471 end
470 472 end
471 473 end
472 474 end
473 475 end
474 476
475 477 # URL helpers do not work with config.threadsafe!
476 478 # https://github.com/rspec/rspec-rails/issues/476#issuecomment-4705454
477 479 ActionView::TestCase::TestController.instance_eval do
478 480 helper Rails.application.routes.url_helpers
479 481 end
480 482 ActionView::TestCase::TestController.class_eval do
481 483 def _routes
482 484 Rails.application.routes
483 485 end
484 486 end
General Comments 0
You need to be logged in to leave comments. Login now