diff test/test_helper.rb @ 37:94944d00e43c

* Update to SVN trunk rev 4411
author Chris Cannam <chris.cannam@soundsoftware.ac.uk>
date Fri, 19 Nov 2010 13:24:41 +0000
parents 1d32c0a0efbf
children af80e5618e9b 8661b858af72
line wrap: on
line diff
--- a/test/test_helper.rb	Fri Sep 24 14:06:04 2010 +0100
+++ b/test/test_helper.rb	Fri Nov 19 13:24:41 2010 +0000
@@ -113,11 +113,15 @@
   def self.repository_configured?(vendor)
     File.directory?(repository_path(vendor))
   end
+  
+  def assert_error_tag(options={})
+    assert_tag({:tag => 'p', :attributes => { :id => 'errorExplanation' }}.merge(options))
+  end
 
   # Shoulda macros
   def self.should_render_404
     should_respond_with :not_found
-    should_render_template 'common/404'
+    should_render_template 'common/error'
   end
 
   def self.should_have_before_filter(expected_method, options = {})
@@ -181,4 +185,236 @@
       assert !user.new_record?
     end
   end
+
+  # Test that a request allows the three types of API authentication
+  #
+  # * HTTP Basic with username and password
+  # * HTTP Basic with an api key for the username
+  # * Key based with the key=X parameter
+  #
+  # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
+  # @param [String] url the request url
+  # @param [optional, Hash] parameters additional request parameters
+  # @param [optional, Hash] options additional options
+  # @option options [Symbol] :success_code Successful response code (:success)
+  # @option options [Symbol] :failure_code Failure response code (:unauthorized)
+  def self.should_allow_api_authentication(http_method, url, parameters={}, options={})
+    should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options)
+    should_allow_http_basic_auth_with_key(http_method, url, parameters, options)
+    should_allow_key_based_auth(http_method, url, parameters, options)
+  end
+
+  # Test that a request allows the username and password for HTTP BASIC
+  #
+  # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
+  # @param [String] url the request url
+  # @param [optional, Hash] parameters additional request parameters
+  # @param [optional, Hash] options additional options
+  # @option options [Symbol] :success_code Successful response code (:success)
+  # @option options [Symbol] :failure_code Failure response code (:unauthorized)
+  def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={})
+    success_code = options[:success_code] || :success
+    failure_code = options[:failure_code] || :unauthorized
+    
+    context "should allow http basic auth using a username and password for #{http_method} #{url}" do
+      context "with a valid HTTP authentication" do
+        setup do
+          @user = User.generate_with_protected!(:password => 'my_password', :password_confirmation => 'my_password', :admin => true) # Admin so they can access the project
+          @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'my_password')
+          send(http_method, url, parameters, {:authorization => @authorization})
+        end
+        
+        should_respond_with success_code
+        should_respond_with_content_type_based_on_url(url)
+        should "login as the user" do
+          assert_equal @user, User.current
+        end
+      end
+
+      context "with an invalid HTTP authentication" do
+        setup do
+          @user = User.generate_with_protected!
+          @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@user.login, 'wrong_password')
+          send(http_method, url, parameters, {:authorization => @authorization})
+        end
+        
+        should_respond_with failure_code
+        should_respond_with_content_type_based_on_url(url)
+        should "not login as the user" do
+          assert_equal User.anonymous, User.current
+        end
+      end
+      
+      context "without credentials" do
+        setup do
+          send(http_method, url, parameters, {:authorization => ''})
+        end
+
+        should_respond_with failure_code
+        should_respond_with_content_type_based_on_url(url)
+        should "include_www_authenticate_header" do
+          assert @controller.response.headers.has_key?('WWW-Authenticate')
+        end
+      end
+    end
+
+  end
+
+  # Test that a request allows the API key with HTTP BASIC
+  #
+  # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
+  # @param [String] url the request url
+  # @param [optional, Hash] parameters additional request parameters
+  # @param [optional, Hash] options additional options
+  # @option options [Symbol] :success_code Successful response code (:success)
+  # @option options [Symbol] :failure_code Failure response code (:unauthorized)
+  def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={})
+    success_code = options[:success_code] || :success
+    failure_code = options[:failure_code] || :unauthorized
+
+    context "should allow http basic auth with a key for #{http_method} #{url}" do
+      context "with a valid HTTP authentication using the API token" do
+        setup do
+          @user = User.generate_with_protected!(:admin => true)
+          @token = Token.generate!(:user => @user, :action => 'api')
+          @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
+          send(http_method, url, parameters, {:authorization => @authorization})
+        end
+        
+        should_respond_with success_code
+        should_respond_with_content_type_based_on_url(url)
+        should_be_a_valid_response_string_based_on_url(url)
+        should "login as the user" do
+          assert_equal @user, User.current
+        end
+      end
+
+      context "with an invalid HTTP authentication" do
+        setup do
+          @user = User.generate_with_protected!
+          @token = Token.generate!(:user => @user, :action => 'feeds')
+          @authorization = ActionController::HttpAuthentication::Basic.encode_credentials(@token.value, 'X')
+          send(http_method, url, parameters, {:authorization => @authorization})
+        end
+
+        should_respond_with failure_code
+        should_respond_with_content_type_based_on_url(url)
+        should "not login as the user" do
+          assert_equal User.anonymous, User.current
+        end
+      end
+    end
+  end
+  
+  # Test that a request allows full key authentication
+  #
+  # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete)
+  # @param [String] url the request url, without the key=ZXY parameter
+  # @param [optional, Hash] parameters additional request parameters
+  # @param [optional, Hash] options additional options
+  # @option options [Symbol] :success_code Successful response code (:success)
+  # @option options [Symbol] :failure_code Failure response code (:unauthorized)
+  def self.should_allow_key_based_auth(http_method, url, parameters={}, options={})
+    success_code = options[:success_code] || :success
+    failure_code = options[:failure_code] || :unauthorized
+
+    context "should allow key based auth using key=X for #{http_method} #{url}" do
+      context "with a valid api token" do
+        setup do
+          @user = User.generate_with_protected!(:admin => true)
+          @token = Token.generate!(:user => @user, :action => 'api')
+          # Simple url parse to add on ?key= or &key=
+          request_url = if url.match(/\?/)
+                          url + "&key=#{@token.value}"
+                        else
+                          url + "?key=#{@token.value}"
+                        end
+          send(http_method, request_url, parameters)
+        end
+        
+        should_respond_with success_code
+        should_respond_with_content_type_based_on_url(url)
+        should_be_a_valid_response_string_based_on_url(url)
+        should "login as the user" do
+          assert_equal @user, User.current
+        end
+      end
+
+      context "with an invalid api token" do
+        setup do
+          @user = User.generate_with_protected!
+          @token = Token.generate!(:user => @user, :action => 'feeds')
+          # Simple url parse to add on ?key= or &key=
+          request_url = if url.match(/\?/)
+                          url + "&key=#{@token.value}"
+                        else
+                          url + "?key=#{@token.value}"
+                        end
+          send(http_method, request_url, parameters)
+        end
+        
+        should_respond_with failure_code
+        should_respond_with_content_type_based_on_url(url)
+        should "not login as the user" do
+          assert_equal User.anonymous, User.current
+        end
+      end
+    end
+    
+  end
+
+  # Uses should_respond_with_content_type based on what's in the url:
+  #
+  # '/project/issues.xml' => should_respond_with_content_type :xml
+  # '/project/issues.json' => should_respond_with_content_type :json
+  #
+  # @param [String] url Request
+  def self.should_respond_with_content_type_based_on_url(url)
+    case
+    when url.match(/xml/i)
+      should_respond_with_content_type :xml
+    when url.match(/json/i)
+      should_respond_with_content_type :json
+    else
+      raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}"
+    end
+    
+  end
+
+  # Uses the url to assert which format the response should be in
+  #
+  # '/project/issues.xml' => should_be_a_valid_xml_string
+  # '/project/issues.json' => should_be_a_valid_json_string
+  #
+  # @param [String] url Request
+  def self.should_be_a_valid_response_string_based_on_url(url)
+    case
+    when url.match(/xml/i)
+      should_be_a_valid_xml_string
+    when url.match(/json/i)
+      should_be_a_valid_json_string
+    else
+      raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}"
+    end
+    
+  end
+  
+  # Checks that the response is a valid JSON string
+  def self.should_be_a_valid_json_string
+    should "be a valid JSON string (or empty)" do
+      assert (response.body.blank? || ActiveSupport::JSON.decode(response.body))
+    end
+  end
+
+  # Checks that the response is a valid XML string
+  def self.should_be_a_valid_xml_string
+    should "be a valid XML string" do
+      assert REXML::Document.new(response.body)
+    end
+  end
+  
 end
+
+# Simple module to "namespace" all of the API tests
+module ApiTest
+end