To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / test / .svn / text-base / test_helper.rb.svn-base @ 442:753f1380d6bc
History | View | Annotate | Download (16.8 KB)
| 1 | 441:cbce1fd3b1b7 | Chris | # Redmine - project management software |
|---|---|---|---|
| 2 | # Copyright (C) 2006-2011 Jean-Philippe Lang |
||
| 3 | 0:513646585e45 | Chris | # |
| 4 | # This program is free software; you can redistribute it and/or |
||
| 5 | # modify it under the terms of the GNU General Public License |
||
| 6 | # as published by the Free Software Foundation; either version 2 |
||
| 7 | # of the License, or (at your option) any later version. |
||
| 8 | # |
||
| 9 | # This program is distributed in the hope that it will be useful, |
||
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 12 | # GNU General Public License for more details. |
||
| 13 | # |
||
| 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 |
||
| 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 17 | |||
| 18 | ENV["RAILS_ENV"] = "test" |
||
| 19 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") |
||
| 20 | require 'test_help' |
||
| 21 | require File.expand_path(File.dirname(__FILE__) + '/helper_testcase') |
||
| 22 | require File.join(RAILS_ROOT,'test', 'mocks', 'open_id_authentication_mock.rb') |
||
| 23 | |||
| 24 | require File.expand_path(File.dirname(__FILE__) + '/object_daddy_helpers') |
||
| 25 | include ObjectDaddyHelpers |
||
| 26 | |||
| 27 | class ActiveSupport::TestCase |
||
| 28 | # Transactional fixtures accelerate your tests by wrapping each test method |
||
| 29 | # in a transaction that's rolled back on completion. This ensures that the |
||
| 30 | # test database remains unchanged so your fixtures don't have to be reloaded |
||
| 31 | # between every test method. Fewer database queries means faster tests. |
||
| 32 | # |
||
| 33 | # Read Mike Clark's excellent walkthrough at |
||
| 34 | # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting |
||
| 35 | # |
||
| 36 | # Every Active Record database supports transactions except MyISAM tables |
||
| 37 | # in MySQL. Turn off transactional fixtures in this case; however, if you |
||
| 38 | # don't care one way or the other, switching from MyISAM to InnoDB tables |
||
| 39 | # is recommended. |
||
| 40 | self.use_transactional_fixtures = true |
||
| 41 | |||
| 42 | # Instantiated fixtures are slow, but give you @david where otherwise you |
||
| 43 | # would need people(:david). If you don't want to migrate your existing |
||
| 44 | # test cases which use the @david style and don't mind the speed hit (each |
||
| 45 | # instantiated fixtures translates to a database query per test method), |
||
| 46 | # then set this back to true. |
||
| 47 | self.use_instantiated_fixtures = false |
||
| 48 | |||
| 49 | # Add more helper methods to be used by all tests here... |
||
| 50 | |||
| 51 | def log_user(login, password) |
||
| 52 | User.anonymous |
||
| 53 | get "/login" |
||
| 54 | assert_equal nil, session[:user_id] |
||
| 55 | assert_response :success |
||
| 56 | assert_template "account/login" |
||
| 57 | post "/login", :username => login, :password => password |
||
| 58 | assert_equal login, User.find(session[:user_id]).login |
||
| 59 | end |
||
| 60 | |||
| 61 | def uploaded_test_file(name, mime) |
||
| 62 | 119:8661b858af72 | Chris | ActionController::TestUploadedFile.new(ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime, true)
|
| 63 | 0:513646585e45 | Chris | end |
| 64 | |||
| 65 | # Mock out a file |
||
| 66 | 14:1d32c0a0efbf | Chris | def self.mock_file |
| 67 | 0:513646585e45 | Chris | file = 'a_file.png' |
| 68 | file.stubs(:size).returns(32) |
||
| 69 | file.stubs(:original_filename).returns('a_file.png')
|
||
| 70 | file.stubs(:content_type).returns('image/png')
|
||
| 71 | file.stubs(:read).returns(false) |
||
| 72 | file |
||
| 73 | end |
||
| 74 | 14:1d32c0a0efbf | Chris | |
| 75 | def mock_file |
||
| 76 | self.class.mock_file |
||
| 77 | end |
||
| 78 | |||
| 79 | 0:513646585e45 | Chris | # Use a temporary directory for attachment related tests |
| 80 | def set_tmp_attachments_directory |
||
| 81 | Dir.mkdir "#{RAILS_ROOT}/tmp/test" unless File.directory?("#{RAILS_ROOT}/tmp/test")
|
||
| 82 | Dir.mkdir "#{RAILS_ROOT}/tmp/test/attachments" unless File.directory?("#{RAILS_ROOT}/tmp/test/attachments")
|
||
| 83 | Attachment.storage_path = "#{RAILS_ROOT}/tmp/test/attachments"
|
||
| 84 | end |
||
| 85 | |||
| 86 | def with_settings(options, &block) |
||
| 87 | saved_settings = options.keys.inject({}) {|h, k| h[k] = Setting[k].dup; h}
|
||
| 88 | options.each {|k, v| Setting[k] = v}
|
||
| 89 | yield |
||
| 90 | 441:cbce1fd3b1b7 | Chris | ensure |
| 91 | 0:513646585e45 | Chris | saved_settings.each {|k, v| Setting[k] = v}
|
| 92 | end |
||
| 93 | |||
| 94 | 14:1d32c0a0efbf | Chris | def change_user_password(login, new_password) |
| 95 | user = User.first(:conditions => {:login => login})
|
||
| 96 | user.password, user.password_confirmation = new_password, new_password |
||
| 97 | user.save! |
||
| 98 | end |
||
| 99 | |||
| 100 | 0:513646585e45 | Chris | def self.ldap_configured? |
| 101 | @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389) |
||
| 102 | return @test_ldap.bind |
||
| 103 | rescue Exception => e |
||
| 104 | # LDAP is not listening |
||
| 105 | return nil |
||
| 106 | end |
||
| 107 | |||
| 108 | # Returns the path to the test +vendor+ repository |
||
| 109 | def self.repository_path(vendor) |
||
| 110 | File.join(RAILS_ROOT.gsub(%r{config\/\.\.}, ''), "/tmp/test/#{vendor.downcase}_repository")
|
||
| 111 | end |
||
| 112 | |||
| 113 | 441:cbce1fd3b1b7 | Chris | # Returns the url of the subversion test repository |
| 114 | def self.subversion_repository_url |
||
| 115 | path = repository_path('subversion')
|
||
| 116 | path = '/' + path unless path.starts_with?('/')
|
||
| 117 | "file://#{path}"
|
||
| 118 | end |
||
| 119 | |||
| 120 | 0:513646585e45 | Chris | # Returns true if the +vendor+ test repository is configured |
| 121 | def self.repository_configured?(vendor) |
||
| 122 | File.directory?(repository_path(vendor)) |
||
| 123 | end |
||
| 124 | 37:94944d00e43c | chris | |
| 125 | def assert_error_tag(options={})
|
||
| 126 | 119:8661b858af72 | Chris | assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
|
| 127 | 37:94944d00e43c | chris | end |
| 128 | 0:513646585e45 | Chris | |
| 129 | # Shoulda macros |
||
| 130 | def self.should_render_404 |
||
| 131 | should_respond_with :not_found |
||
| 132 | 37:94944d00e43c | chris | should_render_template 'common/error' |
| 133 | 0:513646585e45 | Chris | end |
| 134 | |||
| 135 | def self.should_have_before_filter(expected_method, options = {})
|
||
| 136 | should_have_filter('before', expected_method, options)
|
||
| 137 | end |
||
| 138 | |||
| 139 | def self.should_have_after_filter(expected_method, options = {})
|
||
| 140 | should_have_filter('after', expected_method, options)
|
||
| 141 | end |
||
| 142 | |||
| 143 | def self.should_have_filter(filter_type, expected_method, options) |
||
| 144 | description = "have #{filter_type}_filter :#{expected_method}"
|
||
| 145 | description << " with #{options.inspect}" unless options.empty?
|
||
| 146 | |||
| 147 | should description do |
||
| 148 | klass = "action_controller/filters/#{filter_type}_filter".classify.constantize
|
||
| 149 | expected = klass.new(:filter, expected_method.to_sym, options) |
||
| 150 | assert_equal 1, @controller.class.filter_chain.select { |filter|
|
||
| 151 | filter.method == expected.method && filter.kind == expected.kind && |
||
| 152 | filter.options == expected.options && filter.class == expected.class |
||
| 153 | }.size |
||
| 154 | end |
||
| 155 | end |
||
| 156 | |||
| 157 | def self.should_show_the_old_and_new_values_for(prop_key, model, &block) |
||
| 158 | context "" do |
||
| 159 | setup do |
||
| 160 | if block_given? |
||
| 161 | instance_eval &block |
||
| 162 | else |
||
| 163 | @old_value = model.generate! |
||
| 164 | @new_value = model.generate! |
||
| 165 | end |
||
| 166 | end |
||
| 167 | |||
| 168 | should "use the new value's name" do |
||
| 169 | @detail = JournalDetail.generate!(:property => 'attr', |
||
| 170 | :old_value => @old_value.id, |
||
| 171 | :value => @new_value.id, |
||
| 172 | :prop_key => prop_key) |
||
| 173 | |||
| 174 | assert_match @new_value.name, show_detail(@detail, true) |
||
| 175 | end |
||
| 176 | |||
| 177 | should "use the old value's name" do |
||
| 178 | @detail = JournalDetail.generate!(:property => 'attr', |
||
| 179 | :old_value => @old_value.id, |
||
| 180 | :value => @new_value.id, |
||
| 181 | :prop_key => prop_key) |
||
| 182 | |||
| 183 | assert_match @old_value.name, show_detail(@detail, true) |
||
| 184 | end |
||
| 185 | end |
||
| 186 | end |
||
| 187 | 14:1d32c0a0efbf | Chris | |
| 188 | def self.should_create_a_new_user(&block) |
||
| 189 | should "create a new user" do |
||
| 190 | user = instance_eval &block |
||
| 191 | assert user |
||
| 192 | assert_kind_of User, user |
||
| 193 | assert !user.new_record? |
||
| 194 | end |
||
| 195 | end |
||
| 196 | 37:94944d00e43c | chris | |
| 197 | # Test that a request allows the three types of API authentication |
||
| 198 | # |
||
| 199 | # * HTTP Basic with username and password |
||
| 200 | # * HTTP Basic with an api key for the username |
||
| 201 | # * Key based with the key=X parameter |
||
| 202 | # |
||
| 203 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
||
| 204 | # @param [String] url the request url |
||
| 205 | # @param [optional, Hash] parameters additional request parameters |
||
| 206 | # @param [optional, Hash] options additional options |
||
| 207 | # @option options [Symbol] :success_code Successful response code (:success) |
||
| 208 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
||
| 209 | def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
|
||
| 210 | should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options) |
||
| 211 | should_allow_http_basic_auth_with_key(http_method, url, parameters, options) |
||
| 212 | should_allow_key_based_auth(http_method, url, parameters, options) |
||
| 213 | end |
||
| 214 | |||
| 215 | # Test that a request allows the username and password for HTTP BASIC |
||
| 216 | # |
||
| 217 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
||
| 218 | # @param [String] url the request url |
||
| 219 | # @param [optional, Hash] parameters additional request parameters |
||
| 220 | # @param [optional, Hash] options additional options |
||
| 221 | # @option options [Symbol] :success_code Successful response code (:success) |
||
| 222 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
||
| 223 | def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
|
||
| 224 | success_code = options[:success_code] || :success |
||
| 225 | failure_code = options[:failure_code] || :unauthorized |
||
| 226 | |||
| 227 | context "should allow http basic auth using a username and password for #{http_method} #{url}" do
|
||
| 228 | context "with a valid HTTP authentication" do |
||
| 229 | setup do |
||
| 230 | @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project |
||
| 231 | @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password') |
||
| 232 | send(http_method, url, parameters, {:authorization => @authorization})
|
||
| 233 | end |
||
| 234 | |||
| 235 | should_respond_with success_code |
||
| 236 | should_respond_with_content_type_based_on_url(url) |
||
| 237 | should "login as the user" do |
||
| 238 | assert_equal @user, User.current |
||
| 239 | end |
||
| 240 | end |
||
| 241 | |||
| 242 | context "with an invalid HTTP authentication" do |
||
| 243 | setup do |
||
| 244 | @user = User.generate_with_protected! |
||
| 245 | @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password') |
||
| 246 | send(http_method, url, parameters, {:authorization => @authorization})
|
||
| 247 | end |
||
| 248 | |||
| 249 | should_respond_with failure_code |
||
| 250 | should_respond_with_content_type_based_on_url(url) |
||
| 251 | should "not login as the user" do |
||
| 252 | assert_equal User.anonymous, User.current |
||
| 253 | end |
||
| 254 | end |
||
| 255 | |||
| 256 | context "without credentials" do |
||
| 257 | setup do |
||
| 258 | send(http_method, url, parameters, {:authorization => ''})
|
||
| 259 | end |
||
| 260 | |||
| 261 | should_respond_with failure_code |
||
| 262 | should_respond_with_content_type_based_on_url(url) |
||
| 263 | should "include_www_authenticate_header" do |
||
| 264 | assert @controller.response.headers.has_key?('WWW-Authenticate')
|
||
| 265 | end |
||
| 266 | end |
||
| 267 | end |
||
| 268 | |||
| 269 | end |
||
| 270 | |||
| 271 | # Test that a request allows the API key with HTTP BASIC |
||
| 272 | # |
||
| 273 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
||
| 274 | # @param [String] url the request url |
||
| 275 | # @param [optional, Hash] parameters additional request parameters |
||
| 276 | # @param [optional, Hash] options additional options |
||
| 277 | # @option options [Symbol] :success_code Successful response code (:success) |
||
| 278 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
||
| 279 | def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
|
||
| 280 | success_code = options[:success_code] || :success |
||
| 281 | failure_code = options[:failure_code] || :unauthorized |
||
| 282 | |||
| 283 | context "should allow http basic auth with a key for #{http_method} #{url}" do
|
||
| 284 | context "with a valid HTTP authentication using the API token" do |
||
| 285 | setup do |
||
| 286 | @user = User.generate_with_protected!(:admin => true) |
||
| 287 | @token = Token.generate!(:user => @user, :action => 'api') |
||
| 288 | @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X') |
||
| 289 | send(http_method, url, parameters, {:authorization => @authorization})
|
||
| 290 | end |
||
| 291 | |||
| 292 | should_respond_with success_code |
||
| 293 | should_respond_with_content_type_based_on_url(url) |
||
| 294 | should_be_a_valid_response_string_based_on_url(url) |
||
| 295 | should "login as the user" do |
||
| 296 | assert_equal @user, User.current |
||
| 297 | end |
||
| 298 | end |
||
| 299 | |||
| 300 | context "with an invalid HTTP authentication" do |
||
| 301 | setup do |
||
| 302 | @user = User.generate_with_protected! |
||
| 303 | @token = Token.generate!(:user => @user, :action => 'feeds') |
||
| 304 | @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X') |
||
| 305 | send(http_method, url, parameters, {:authorization => @authorization})
|
||
| 306 | end |
||
| 307 | |||
| 308 | should_respond_with failure_code |
||
| 309 | should_respond_with_content_type_based_on_url(url) |
||
| 310 | should "not login as the user" do |
||
| 311 | assert_equal User.anonymous, User.current |
||
| 312 | end |
||
| 313 | end |
||
| 314 | end |
||
| 315 | end |
||
| 316 | |||
| 317 | # Test that a request allows full key authentication |
||
| 318 | # |
||
| 319 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
||
| 320 | # @param [String] url the request url, without the key=ZXY parameter |
||
| 321 | # @param [optional, Hash] parameters additional request parameters |
||
| 322 | # @param [optional, Hash] options additional options |
||
| 323 | # @option options [Symbol] :success_code Successful response code (:success) |
||
| 324 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
||
| 325 | def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
|
||
| 326 | success_code = options[:success_code] || :success |
||
| 327 | failure_code = options[:failure_code] || :unauthorized |
||
| 328 | |||
| 329 | context "should allow key based auth using key=X for #{http_method} #{url}" do
|
||
| 330 | context "with a valid api token" do |
||
| 331 | setup do |
||
| 332 | @user = User.generate_with_protected!(:admin => true) |
||
| 333 | @token = Token.generate!(:user => @user, :action => 'api') |
||
| 334 | # Simple url parse to add on ?key= or &key= |
||
| 335 | request_url = if url.match(/\?/) |
||
| 336 | url + "&key=#{@token.value}"
|
||
| 337 | else |
||
| 338 | url + "?key=#{@token.value}"
|
||
| 339 | end |
||
| 340 | send(http_method, request_url, parameters) |
||
| 341 | end |
||
| 342 | |||
| 343 | should_respond_with success_code |
||
| 344 | should_respond_with_content_type_based_on_url(url) |
||
| 345 | should_be_a_valid_response_string_based_on_url(url) |
||
| 346 | should "login as the user" do |
||
| 347 | assert_equal @user, User.current |
||
| 348 | end |
||
| 349 | end |
||
| 350 | |||
| 351 | context "with an invalid api token" do |
||
| 352 | setup do |
||
| 353 | @user = User.generate_with_protected! |
||
| 354 | @token = Token.generate!(:user => @user, :action => 'feeds') |
||
| 355 | # Simple url parse to add on ?key= or &key= |
||
| 356 | request_url = if url.match(/\?/) |
||
| 357 | url + "&key=#{@token.value}"
|
||
| 358 | else |
||
| 359 | url + "?key=#{@token.value}"
|
||
| 360 | end |
||
| 361 | send(http_method, request_url, parameters) |
||
| 362 | end |
||
| 363 | |||
| 364 | should_respond_with failure_code |
||
| 365 | should_respond_with_content_type_based_on_url(url) |
||
| 366 | should "not login as the user" do |
||
| 367 | assert_equal User.anonymous, User.current |
||
| 368 | end |
||
| 369 | end |
||
| 370 | end |
||
| 371 | |||
| 372 | 119:8661b858af72 | Chris | context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do
|
| 373 | setup do |
||
| 374 | @user = User.generate_with_protected!(:admin => true) |
||
| 375 | @token = Token.generate!(:user => @user, :action => 'api') |
||
| 376 | send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s})
|
||
| 377 | end |
||
| 378 | |||
| 379 | should_respond_with success_code |
||
| 380 | should_respond_with_content_type_based_on_url(url) |
||
| 381 | should_be_a_valid_response_string_based_on_url(url) |
||
| 382 | should "login as the user" do |
||
| 383 | assert_equal @user, User.current |
||
| 384 | end |
||
| 385 | end |
||
| 386 | 37:94944d00e43c | chris | end |
| 387 | |||
| 388 | # Uses should_respond_with_content_type based on what's in the url: |
||
| 389 | # |
||
| 390 | # '/project/issues.xml' => should_respond_with_content_type :xml |
||
| 391 | # '/project/issues.json' => should_respond_with_content_type :json |
||
| 392 | # |
||
| 393 | # @param [String] url Request |
||
| 394 | def self.should_respond_with_content_type_based_on_url(url) |
||
| 395 | case |
||
| 396 | when url.match(/xml/i) |
||
| 397 | should_respond_with_content_type :xml |
||
| 398 | when url.match(/json/i) |
||
| 399 | should_respond_with_content_type :json |
||
| 400 | else |
||
| 401 | raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
|
||
| 402 | end |
||
| 403 | |||
| 404 | end |
||
| 405 | |||
| 406 | # Uses the url to assert which format the response should be in |
||
| 407 | # |
||
| 408 | # '/project/issues.xml' => should_be_a_valid_xml_string |
||
| 409 | # '/project/issues.json' => should_be_a_valid_json_string |
||
| 410 | # |
||
| 411 | # @param [String] url Request |
||
| 412 | def self.should_be_a_valid_response_string_based_on_url(url) |
||
| 413 | case |
||
| 414 | when url.match(/xml/i) |
||
| 415 | should_be_a_valid_xml_string |
||
| 416 | when url.match(/json/i) |
||
| 417 | should_be_a_valid_json_string |
||
| 418 | else |
||
| 419 | raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
|
||
| 420 | end |
||
| 421 | |||
| 422 | end |
||
| 423 | |||
| 424 | # Checks that the response is a valid JSON string |
||
| 425 | def self.should_be_a_valid_json_string |
||
| 426 | should "be a valid JSON string (or empty)" do |
||
| 427 | 441:cbce1fd3b1b7 | Chris | assert(response.body.blank? || ActiveSupport::JSON.decode(response.body)) |
| 428 | 37:94944d00e43c | chris | end |
| 429 | end |
||
| 430 | |||
| 431 | # Checks that the response is a valid XML string |
||
| 432 | def self.should_be_a_valid_xml_string |
||
| 433 | should "be a valid XML string" do |
||
| 434 | assert REXML::Document.new(response.body) |
||
| 435 | end |
||
| 436 | end |
||
| 437 | |||
| 438 | 0:513646585e45 | Chris | end |
| 439 | 37:94944d00e43c | chris | |
| 440 | # Simple module to "namespace" all of the API tests |
||
| 441 | module ApiTest |
||
| 442 | end |