require "spec_helper" require "integration/support/server" require "json" require "ostruct" describe Savon::Operation do let(:globals) { Savon::GlobalOptions.new(:endpoint => @server.url(:repeat), :log => false) } let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:taxcloud) } let(:no_wsdl) { wsdl = Wasabi::Document.new wsdl.endpoint = "http://example.com" wsdl.namespace = "http://v1.example.com" wsdl } def new_operation(operation_name, wsdl, globals) Savon::Operation.create(operation_name, wsdl, globals) end before :all do @server = IntegrationServer.run end after :all do @server.stop end describe ".create with a WSDL" do it "returns a new operation" do operation = new_operation(:verify_address, wsdl, globals) expect(operation).to be_a(Savon::Operation) end it "raises if the operation name is not a Symbol" do expect { new_operation("not a symbol", wsdl, globals) }. to raise_error(ArgumentError, /Expected the first parameter \(the name of the operation to call\) to be a symbol/) end it "raises if the operation is not available for the service" do expect { new_operation(:no_such_operation, wsdl, globals) }. to raise_error(Savon::UnknownOperationError, /Unable to find SOAP operation: :no_such_operation/) end it "raises if the endpoint cannot be reached" do message = "Error!" response = HTTPI::Response.new(500, {}, message) error = Wasabi::Resolver::HTTPError.new(message, response) Wasabi::Document.any_instance.stubs(:soap_actions).raises(error) expect { new_operation(:verify_address, wsdl, globals) }. to raise_error(Savon::HTTPError, /#{message}/) end end describe ".create without a WSDL" do it "returns a new operation" do operation = new_operation(:verify_address, no_wsdl, globals) expect(operation).to be_a(Savon::Operation) end end describe "#build" do it "returns the Builder" do operation = new_operation(:verify_address, wsdl, globals) builder = operation.build(:message => { :test => 'message' }) expect(builder).to be_a(Savon::Builder) expect(builder.to_s).to include('message') end end describe "#call" do it "returns a response object" do operation = new_operation(:verify_address, wsdl, globals) expect(operation.call).to be_a(Savon::Response) end it "uses the global :endpoint option for the request" do globals.endpoint("http://v1.example.com") HTTPI::Request.any_instance.expects(:url=).with("http://v1.example.com") operation = new_operation(:verify_address, wsdl, globals) # stub the actual request http_response = HTTPI::Response.new(200, {}, "") operation.expects(:call_with_logging).returns(http_response) operation.call end it "falls back to use the WSDL's endpoint if the :endpoint option was not set" do globals_without_endpoint = Savon::GlobalOptions.new(:log => false) HTTPI::Request.any_instance.expects(:url=).with(wsdl.endpoint) operation = new_operation(:verify_address, wsdl, globals_without_endpoint) # stub the actual request http_response = HTTPI::Response.new(200, {}, "") operation.expects(:call_with_logging).returns(http_response) operation.call end it "sets the Content-Length header" do # XXX: probably the worst spec ever written. refactor! [dh, 2013-01-05] http_request = HTTPI::Request.new http_request.headers.expects(:[]=).with("Content-Length", "312") Savon::SOAPRequest.any_instance.expects(:build).returns(http_request) new_operation(:verify_address, wsdl, globals).call end it "passes the local :soap_action option to the request builder" do globals.endpoint @server.url(:inspect_request) soap_action = "http://v1.example.com/VerifyAddress" operation = new_operation(:verify_address, wsdl, globals) response = operation.call(:soap_action => soap_action) actual_soap_action = inspect_request(response).soap_action expect(actual_soap_action).to eq(%("#{soap_action}")) end it "uses the local :cookies option" do globals.endpoint @server.url(:inspect_request) cookies = [HTTPI::Cookie.new("some-cookie=choc-chip")] HTTPI::Request.any_instance.expects(:set_cookies).with(cookies) operation = new_operation(:verify_address, wsdl, globals) operation.call(:cookies => cookies) end it "passes nil to the request builder if the :soap_action was set to nil" do globals.endpoint @server.url(:inspect_request) operation = new_operation(:verify_address, wsdl, globals) response = operation.call(:soap_action => nil) actual_soap_action = inspect_request(response).soap_action expect(actual_soap_action).to be_nil end it "gets the SOAP action from the WSDL if available" do globals.endpoint @server.url(:inspect_request) operation = new_operation(:verify_address, wsdl, globals) response = operation.call actual_soap_action = inspect_request(response).soap_action expect(actual_soap_action).to eq('"http://taxcloud.net/VerifyAddress"') end it "falls back to Gyoku if both option and WSDL are not available" do globals.endpoint @server.url(:inspect_request) operation = new_operation(:authenticate, no_wsdl, globals) response = operation.call actual_soap_action = inspect_request(response).soap_action expect(actual_soap_action).to eq(%("authenticate")) end it "returns a Savon::Multipart::Response if available and requested globally" do globals.multipart true with_multipart_mocked do operation = new_operation(:authenticate, no_wsdl, globals) response = operation.call expect(response).to be_a(Savon::Multipart::Response) end end it "returns a Savon::Multipart::Response if available and requested locally" do with_multipart_mocked do operation = new_operation(:authenticate, no_wsdl, globals) response = operation.call(:multipart => true) expect(response).to be_a(Savon::Multipart::Response) end end it "raises if savon-multipart is not available and it was requested globally" do globals.multipart true operation = new_operation(:authenticate, no_wsdl, globals) expect { operation.call }. to raise_error RuntimeError, /Unable to find Savon::Multipart/ end it "raises if savon-multipart is not available and it was requested locally" do operation = new_operation(:authenticate, no_wsdl, globals) expect { operation.call(:multipart => true) }. to raise_error RuntimeError, /Unable to find Savon::Multipart/ end end describe "#request" do it "returns the request" do operation = new_operation(:verify_address, wsdl, globals) request = operation.request expect(request.body).to include('') end end def with_multipart_mocked multipart_response = Class.new { def initialize(*args); end } multipart_mock = Module.new multipart_mock.const_set('Response', multipart_response) Savon.const_set('Multipart', multipart_mock) yield ensure Savon.send(:remove_const, :Multipart) if Savon.const_defined? :Multipart end def inspect_request(response) hash = JSON.parse(response.http.body) OpenStruct.new(hash) end end