require File.dirname(__FILE__) + '/abstract_unit' require 'stringio' class ActionController::Base; def rescue_action(e) raise e end; end module DispatcherTest Utf8String = "One World Caf\303\251" WsdlNamespace = 'http://rubyonrails.com/some/namespace' class Node < ActiveRecord::Base def initialize(*args) super(*args) @new_record = false end class << self def name "DispatcherTest::Node" end def columns(*args) [ ActiveRecord::ConnectionAdapters::Column.new('id', 0, 'int'), ActiveRecord::ConnectionAdapters::Column.new('name', nil, 'string'), ActiveRecord::ConnectionAdapters::Column.new('description', nil, 'string'), ] end def connection self end end end class Person < ActionWebService::Struct member :id, :int member :name, :string def ==(other) self.id == other.id && self.name == other.name end end class API < ActionWebService::API::Base api_method :add, :expects => [:int, :int], :returns => [:int] api_method :interceptee api_method :struct_return, :returns => [[Node]] api_method :void end class DirectAPI < ActionWebService::API::Base api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] api_method :add2, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] api_method :before_filtered api_method :after_filtered, :returns => [[:int]] api_method :struct_return, :returns => [[Node]] api_method :struct_pass, :expects => [{:person => Person}] api_method :base_struct_return, :returns => [[Person]] api_method :hash_struct_return, :returns => [[Person]] api_method :thrower api_method :void api_method :test_utf8, :returns => [:string] api_method :hex, :expects => [:base64], :returns => [:string] api_method :unhex, :expects => [:string], :returns => [:base64] api_method :time, :expects => [:time], :returns => [:time] end class VirtualAPI < ActionWebService::API::Base default_api_method :fallback end class Service < ActionWebService::Base web_service_api API before_invocation :do_intercept, :only => [:interceptee] attr :added attr :intercepted attr :void_called def initialize @void_called = false end def add(a, b) @added = a + b end def interceptee @intercepted = false end def struct_return n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1') n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2') [n1, n2] end def void(*args) @void_called = args end def do_intercept(name, args) [false, "permission denied"] end end class MTAPI < ActionWebService::API::Base inflect_names false api_method :getCategories, :returns => [[:string]] api_method :bool, :returns => [:bool] api_method :alwaysFail api_method :person, :returns => [Person] end class BloggerAPI < ActionWebService::API::Base inflect_names false api_method :getCategories, :returns => [[:string]] api_method :str, :expects => [:int], :returns => [:string] api_method :alwaysFail end class MTService < ActionWebService::Base web_service_api MTAPI def getCategories ["mtCat1", "mtCat2"] end def bool 'y' end def alwaysFail raise "MT AlwaysFail" end def person Person.new('id' => 1, 'name' => 'person1') end end class BloggerService < ActionWebService::Base web_service_api BloggerAPI def getCategories ["bloggerCat1", "bloggerCat2"] end def str(int) unless int.is_a?(Integer) raise "Not an integer!" end 500 + int end def alwaysFail raise "Blogger AlwaysFail" end end class AbstractController < ActionController::Base def generate_wsdl self.request ||= ::ActionController::TestRequest.new to_wsdl end end class DelegatedController < AbstractController web_service_dispatching_mode :delegated wsdl_namespace WsdlNamespace web_service(:test_service) { @service ||= Service.new; @service } end class LayeredController < AbstractController web_service_dispatching_mode :layered wsdl_namespace WsdlNamespace web_service(:mt) { @mt_service ||= MTService.new; @mt_service } web_service(:blogger) { @blogger_service ||= BloggerService.new; @blogger_service } end class DirectController < AbstractController web_service_api DirectAPI web_service_dispatching_mode :direct wsdl_namespace WsdlNamespace before_invocation :alwaysfail, :only => [:before_filtered] after_invocation :alwaysok, :only => [:after_filtered] attr :added attr :added2 attr :before_filter_called attr :before_filter_target_called attr :after_filter_called attr :after_filter_target_called attr :void_called attr :struct_pass_value def initialize @before_filter_called = false @before_filter_target_called = false @after_filter_called = false @after_filter_target_called = false @void_called = false @struct_pass_value = false end def add @added = params['a'] + params['b'] end def add2(a, b) @added2 = a + b end def before_filtered @before_filter_target_called = true end def after_filtered @after_filter_target_called = true [5, 6, 7] end def thrower raise "Hi, I'm an exception" end def struct_return n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1') n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2') [n1, n2] end def struct_pass(person) @struct_pass_value = person end def base_struct_return p1 = Person.new('id' => 1, 'name' => 'person1') p2 = Person.new('id' => 2, 'name' => 'person2') [p1, p2] end def hash_struct_return p1 = { :id => '1', 'name' => 'test' } p2 = { 'id' => '2', :name => 'person2' } [p1, p2] end def void @void_called = @method_params end def test_utf8 Utf8String end def hex(s) return s.unpack("H*")[0] end def unhex(s) return [s].pack("H*") end def time(t) t end protected def alwaysfail(method_name, params) @before_filter_called = true false end def alwaysok(method_name, params, return_value) @after_filter_called = true end end class VirtualController < AbstractController web_service_api VirtualAPI wsdl_namespace WsdlNamespace def fallback "fallback!" end end end module DispatcherCommonTests def test_direct_dispatching assert_equal(70, do_method_call(@direct_controller, 'Add', 20, 50)) assert_equal(70, @direct_controller.added) assert_equal(50, do_method_call(@direct_controller, 'Add2', 25, 25)) assert_equal(50, @direct_controller.added2) assert(@direct_controller.void_called == false) assert(do_method_call(@direct_controller, 'Void', 3, 4, 5).nil?) assert(@direct_controller.void_called == []) result = do_method_call(@direct_controller, 'BaseStructReturn') assert(result[0].is_a?(DispatcherTest::Person)) assert(result[1].is_a?(DispatcherTest::Person)) assert_equal("cafe", do_method_call(@direct_controller, 'Hex', "\xca\xfe")) assert_equal("\xca\xfe", do_method_call(@direct_controller, 'Unhex', "cafe")) time = Time.gm(1998, "Feb", 02, 15, 12, 01) assert_equal(time, do_method_call(@direct_controller, 'Time', time)) end def test_direct_entrypoint assert(@direct_controller.respond_to?(:api)) end def test_virtual_dispatching assert_equal("fallback!", do_method_call(@virtual_controller, 'VirtualOne')) assert_equal("fallback!", do_method_call(@virtual_controller, 'VirtualTwo')) end def test_direct_filtering assert_equal(false, @direct_controller.before_filter_called) assert_equal(false, @direct_controller.before_filter_target_called) do_method_call(@direct_controller, 'BeforeFiltered') assert_equal(true, @direct_controller.before_filter_called) assert_equal(false, @direct_controller.before_filter_target_called) assert_equal(false, @direct_controller.after_filter_called) assert_equal(false, @direct_controller.after_filter_target_called) assert_equal([5, 6, 7], do_method_call(@direct_controller, 'AfterFiltered')) assert_equal(true, @direct_controller.after_filter_called) assert_equal(true, @direct_controller.after_filter_target_called) end def test_delegated_dispatching assert_equal(130, do_method_call(@delegated_controller, 'Add', 50, 80)) service = @delegated_controller.web_service_object(:test_service) assert_equal(130, service.added) @delegated_controller.web_service_exception_reporting = true assert(service.intercepted.nil?) result = do_method_call(@delegated_controller, 'Interceptee') assert(service.intercepted.nil?) assert(is_exception?(result)) assert_match(/permission denied/, exception_message(result)) result = do_method_call(@delegated_controller, 'NonExistentMethod') assert(is_exception?(result)) assert_match(/NonExistentMethod/, exception_message(result)) assert(service.void_called == false) assert(do_method_call(@delegated_controller, 'Void', 3, 4, 5).nil?) assert(service.void_called == []) end def test_garbage_request [@direct_controller, @delegated_controller].each do |controller| controller.class.web_service_exception_reporting = true send_garbage_request = lambda do service_name = service_name(controller) request = protocol.encode_action_pack_request(service_name, 'broken, method, name!', 'broken request body', :request_class => ActionController::TestRequest) response = ActionController::TestResponse.new controller.process(request, response) # puts response.body assert(response.headers['Status'] =~ /^500/) end send_garbage_request.call controller.class.web_service_exception_reporting = false send_garbage_request.call end end def test_exception_marshaling @direct_controller.web_service_exception_reporting = true result = do_method_call(@direct_controller, 'Thrower') assert(is_exception?(result)) assert_equal("Hi, I'm an exception", exception_message(result)) @direct_controller.web_service_exception_reporting = false result = do_method_call(@direct_controller, 'Thrower') assert(exception_message(result) != "Hi, I'm an exception") end def test_ar_struct_return [@direct_controller, @delegated_controller].each do |controller| result = do_method_call(controller, 'StructReturn') assert(result[0].is_a?(DispatcherTest::Node)) assert(result[1].is_a?(DispatcherTest::Node)) assert_equal('node1', result[0].name) assert_equal('node2', result[1].name) end end def test_casting assert_equal 70, do_method_call(@direct_controller, 'Add', "50", "20") assert_equal false, @direct_controller.struct_pass_value person = DispatcherTest::Person.new(:id => 1, :name => 'test') result = do_method_call(@direct_controller, 'StructPass', person) assert(nil == result || true == result) assert_equal person, @direct_controller.struct_pass_value assert !person.equal?(@direct_controller.struct_pass_value) result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test'}) case when soap? assert_equal(person, @direct_controller.struct_pass_value) assert !person.equal?(@direct_controller.struct_pass_value) when xmlrpc? assert_equal(person, @direct_controller.struct_pass_value) assert !person.equal?(@direct_controller.struct_pass_value) end assert_equal person, do_method_call(@direct_controller, 'HashStructReturn')[0] result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test', 'nonexistent_attribute' => 'value'}) case when soap? assert_equal(person, @direct_controller.struct_pass_value) assert !person.equal?(@direct_controller.struct_pass_value) when xmlrpc? assert_equal(person, @direct_controller.struct_pass_value) assert !person.equal?(@direct_controller.struct_pass_value) end end def test_logging buf = "" ActionController::Base.logger = Logger.new(StringIO.new(buf)) test_casting test_garbage_request test_exception_marshaling ActionController::Base.logger = nil assert_match /Web Service Response/, buf assert_match /Web Service Request/, buf end def test_allowed_http_methods webservice_api = @direct_controller.class.web_service_api original_allowed_http_methods = webservice_api.allowed_http_methods # check defaults assert_equal false, http_method_allowed?(:get) assert_equal false, http_method_allowed?(:head) assert_equal false, http_method_allowed?(:put) assert_equal false, http_method_allowed?(:delete) assert_equal true, http_method_allowed?(:post) # allow get and post webservice_api.allowed_http_methods = [ :get, :post ] assert_equal true, http_method_allowed?(:get) assert_equal true, http_method_allowed?(:post) # allow get only webservice_api.allowed_http_methods = [ :get ] assert_equal true, http_method_allowed?(:get) assert_equal false, http_method_allowed?(:post) # allow delete only webservice_api.allowed_http_methods = [ 'DELETE' ] assert_equal false, http_method_allowed?(:get) assert_equal false, http_method_allowed?(:head) assert_equal false, http_method_allowed?(:post) assert_equal false, http_method_allowed?(:put) assert_equal true, http_method_allowed?(:delete) ensure webservice_api.allowed_http_methods = original_allowed_http_methods end protected def service_name(container) raise NotImplementedError end def exception_message(obj) raise NotImplementedError end def is_exception?(obj) raise NotImplementedError end def protocol @protocol end def soap? protocol.is_a? ActionWebService::Protocol::Soap::SoapProtocol end def xmlrpc? protocol.is_a? ActionWebService::Protocol::XmlRpc::XmlRpcProtocol end def do_method_call(container, public_method_name, *params) request_env = {} mode = container.web_service_dispatching_mode case mode when :direct service_name = service_name(container) api = container.class.web_service_api method = api.public_api_method_instance(public_method_name) when :delegated service_name = service_name(container) api = container.web_service_object(service_name).class.web_service_api method = api.public_api_method_instance(public_method_name) when :layered service_name = nil real_method_name = nil if public_method_name =~ /^([^\.]+)\.(.*)$/ service_name = $1 real_method_name = $2 end if soap? public_method_name = real_method_name request_env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{real_method_name}" end api = container.web_service_object(service_name.to_sym).class.web_service_api rescue nil method = api.public_api_method_instance(real_method_name) rescue nil service_name = self.service_name(container) end protocol.register_api(api) virtual = false unless method virtual = true method ||= ActionWebService::API::Method.new(public_method_name.underscore.to_sym, public_method_name, nil, nil) end body = protocol.encode_request(public_method_name, params.dup, method.expects) # puts body ap_request = protocol.encode_action_pack_request(service_name, public_method_name, body, :request_class => ActionController::TestRequest) ap_request.env.update(request_env) ap_response = ActionController::TestResponse.new container.process(ap_request, ap_response) # puts ap_response.body @response_body = ap_response.body public_method_name, return_value = protocol.decode_response(ap_response.body) unless is_exception?(return_value) || virtual return_value = method.cast_returns(return_value) end if soap? # http://dev.rubyonrails.com/changeset/920 assert_match(/Response$/, public_method_name) unless public_method_name == "fault" end return_value end def http_method_allowed?(method) method = method.to_s.upcase test_request = ActionController::TestRequest.new({ 'action' => 'api' }) test_response = ActionController::TestResponse.new test_request.env['REQUEST_METHOD'] = method result = @direct_controller.process(test_request, test_response) result.body =~ /(GET|POST|PUT|DELETE|TRACE|CONNECT) not supported/ ? false : true end end