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