savon 0.7.9 → 0.8.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/.gitignore +9 -0
  2. data/.rspec +1 -0
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +332 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +37 -0
  8. data/Rakefile +28 -39
  9. data/autotest/discover.rb +1 -0
  10. data/lib/savon.rb +10 -31
  11. data/lib/savon/client.rb +116 -98
  12. data/lib/savon/core_ext/array.rb +36 -22
  13. data/lib/savon/core_ext/datetime.rb +15 -6
  14. data/lib/savon/core_ext/hash.rb +122 -94
  15. data/lib/savon/core_ext/object.rb +19 -11
  16. data/lib/savon/core_ext/string.rb +62 -57
  17. data/lib/savon/core_ext/symbol.rb +13 -5
  18. data/lib/savon/error.rb +6 -0
  19. data/lib/savon/global.rb +75 -0
  20. data/lib/savon/http/error.rb +42 -0
  21. data/lib/savon/soap.rb +8 -283
  22. data/lib/savon/soap/fault.rb +48 -0
  23. data/lib/savon/soap/request.rb +61 -0
  24. data/lib/savon/soap/response.rb +65 -0
  25. data/lib/savon/soap/xml.rb +132 -0
  26. data/lib/savon/version.rb +2 -2
  27. data/lib/savon/wsdl/document.rb +107 -0
  28. data/lib/savon/wsdl/parser.rb +90 -0
  29. data/lib/savon/wsdl/request.rb +35 -0
  30. data/lib/savon/wsse.rb +42 -104
  31. data/savon.gemspec +26 -0
  32. data/spec/fixtures/response/response_fixture.rb +26 -26
  33. data/spec/fixtures/response/xml/list.xml +18 -0
  34. data/spec/fixtures/wsdl/wsdl_fixture.rb +6 -0
  35. data/spec/fixtures/wsdl/wsdl_fixture.yml +4 -4
  36. data/spec/savon/client_spec.rb +274 -51
  37. data/spec/savon/core_ext/datetime_spec.rb +1 -1
  38. data/spec/savon/core_ext/hash_spec.rb +40 -4
  39. data/spec/savon/core_ext/object_spec.rb +1 -1
  40. data/spec/savon/core_ext/string_spec.rb +0 -12
  41. data/spec/savon/http/error_spec.rb +52 -0
  42. data/spec/savon/savon_spec.rb +90 -0
  43. data/spec/savon/soap/fault_spec.rb +80 -0
  44. data/spec/savon/soap/request_spec.rb +45 -0
  45. data/spec/savon/soap/response_spec.rb +153 -0
  46. data/spec/savon/soap/xml_spec.rb +249 -0
  47. data/spec/savon/soap_spec.rb +4 -177
  48. data/spec/savon/{wsdl_spec.rb → wsdl/document_spec.rb} +54 -17
  49. data/spec/savon/wsdl/request_spec.rb +15 -0
  50. data/spec/savon/wsse_spec.rb +123 -92
  51. data/spec/spec_helper.rb +19 -4
  52. data/spec/support/endpoint.rb +25 -0
  53. metadata +97 -97
  54. data/.autotest +0 -5
  55. data/CHANGELOG +0 -176
  56. data/README.rdoc +0 -64
  57. data/lib/savon/core_ext.rb +0 -8
  58. data/lib/savon/core_ext/net_http.rb +0 -19
  59. data/lib/savon/core_ext/uri.rb +0 -10
  60. data/lib/savon/logger.rb +0 -56
  61. data/lib/savon/request.rb +0 -138
  62. data/lib/savon/response.rb +0 -174
  63. data/lib/savon/wsdl.rb +0 -137
  64. data/lib/savon/wsdl_stream.rb +0 -85
  65. data/spec/basic_spec_helper.rb +0 -11
  66. data/spec/endpoint_helper.rb +0 -23
  67. data/spec/http_stubs.rb +0 -26
  68. data/spec/integration/http_basic_auth_spec.rb +0 -16
  69. data/spec/integration/server.rb +0 -51
  70. data/spec/savon/core_ext/net_http_spec.rb +0 -38
  71. data/spec/savon/core_ext/uri_spec.rb +0 -19
  72. data/spec/savon/request_spec.rb +0 -117
  73. data/spec/savon/response_spec.rb +0 -179
  74. data/spec/spec.opts +0 -4
@@ -0,0 +1,35 @@
1
+ require "httpi"
2
+
3
+ module Savon
4
+ module WSDL
5
+
6
+ # = Savon::WSDL::Request
7
+ #
8
+ # Executes WSDL requests.
9
+ class Request
10
+
11
+ # Expects an <tt>HTTPI::Request</tt>.
12
+ def initialize(request)
13
+ self.request = request
14
+ end
15
+
16
+ # Accessor for the <tt>HTTPI::Request</tt>.
17
+ attr_accessor :request
18
+
19
+ # Executes the request and returns the response.
20
+ def response
21
+ @response ||= with_logging { HTTPI.get request }
22
+ end
23
+
24
+ private
25
+
26
+ # Logs the HTTP request and yields to a given +block+.
27
+ def with_logging
28
+ Savon.log "Retrieving WSDL from: #{request.url}"
29
+ Savon.log "Using :#{request.auth.type} authentication" if request.auth?
30
+ yield
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -1,131 +1,53 @@
1
+ require "base64"
2
+ require "digest/sha1"
3
+ require "builder"
4
+
5
+ require "savon/core_ext/string"
6
+ require "savon/soap"
7
+
1
8
  module Savon
2
9
 
3
10
  # = Savon::WSSE
4
11
  #
5
- # Savon::WSSE represents WSSE authentication. Pass a block to your SOAP call and the WSSE object
6
- # is passed to it as the second argument. The object allows setting the WSSE username, password
7
- # and whether to use digest authentication.
8
- #
9
- # == Credentials
10
- #
11
- # By default, Savon does not use WSSE authentication. Simply specify a username and password to
12
- # change this.
13
- #
14
- # response = client.get_all_users do |soap, wsse|
15
- # wsse.username = "eve"
16
- # wsse.password = "secret"
17
- # end
18
- #
19
- # == Digest
20
- #
21
- # To use WSSE digest authentication, just use the digest method and set it to +true+.
22
- #
23
- # response = client.get_all_users do |soap, wsse|
24
- # wsse.username = "eve"
25
- # wsse.password = "secret"
26
- # wsse.digest = true
27
- # end
28
- #
29
- # == Default to WSSE
30
- #
31
- # In case all you're services require WSSE authentication, you can set your credentials and whether
32
- # to use WSSE digest for every request:
33
- #
34
- # Savon::WSSE.username = "eve"
35
- # Savon::WSSE.password = "secret"
36
- # Savon::WSSE.digest = true
12
+ # Provides WSSE authentication.
37
13
  class WSSE
38
14
 
39
- # Base address for WSSE docs.
40
- BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
41
-
42
15
  # Namespace for WS Security Secext.
43
- WSENamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-secext-1.0.xsd"
16
+ WSENamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
44
17
 
45
18
  # Namespace for WS Security Utility.
46
- WSUNamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-utility-1.0.xsd"
19
+ WSUNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
47
20
 
48
21
  # URI for "wsse:Password/@Type" #PasswordText.
49
- PasswordTextURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordText"
22
+ PasswordTextURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
50
23
 
51
24
  # URI for "wsse:Password/@Type" #PasswordDigest.
52
- PasswordDigestURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
53
-
54
- # Global WSSE username.
55
- @@username = nil
56
-
57
- # Returns the global WSSE username.
58
- def self.username
59
- @@username
60
- end
61
-
62
- # Sets the global WSSE username.
63
- def self.username=(username)
64
- @@username = username.nil? ? nil : username.to_s
65
- end
66
-
67
- # Global WSSE password.
68
- @@password = nil
69
-
70
- # Returns the global WSSE password.
71
- def self.password
72
- @@password
73
- end
25
+ PasswordDigestURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
74
26
 
75
- # Sets the global WSSE password.
76
- def self.password=(password)
77
- @@password = password.nil? ? nil : password.to_s
27
+ # Sets the authentication credentials. Also accepts whether to use WSSE digest.
28
+ def credentials(username, password, digest = false)
29
+ self.username = username
30
+ self.password = password
31
+ self.digest = digest
78
32
  end
79
33
 
80
- # Global setting of whether to use WSSE digest.
81
- @@digest = false
34
+ attr_accessor :username, :password
82
35
 
83
- # Returns the global setting of whether to use WSSE digest.
84
- def self.digest?
85
- @@digest
86
- end
87
-
88
- # Global setting of whether to use WSSE digest.
89
- def self.digest=(digest)
90
- @@digest = digest
91
- end
92
-
93
- # Sets the WSSE username per request.
94
- def username=(username)
95
- @username = username.nil? ? nil : username.to_s
96
- end
97
-
98
- # Returns the WSSE username. Defaults to the global setting.
99
- def username
100
- @username || self.class.username
101
- end
102
-
103
- # Sets the WSSE password per request.
104
- def password=(password)
105
- @password = password.nil? ? nil : password.to_s
106
- end
107
-
108
- # Returns the WSSE password. Defaults to the global setting.
109
- def password
110
- @password || self.class.password
36
+ # Returns whether to use WSSE digest. Defaults to +false+.
37
+ def digest?
38
+ !!@digest
111
39
  end
112
40
 
113
- # Sets whether to use WSSE digest per request.
114
41
  attr_writer :digest
115
42
 
116
- # Returns whether to use WSSE digest. Defaults to the global setting.
117
- def digest?
118
- @digest || self.class.digest?
119
- end
120
-
121
- # Returns the XML for a WSSE header or an empty String unless both username and password
122
- # were specified.
123
- def header
43
+ # Returns the XML for a WSSE header or an empty String unless authentication
44
+ # credentials were specified.
45
+ def to_xml
124
46
  return "" unless username && password
125
47
 
126
48
  builder = Builder::XmlMarkup.new
127
49
  builder.wsse :Security, "xmlns:wsse" => WSENamespace do |xml|
128
- xml.wsse :UsernameToken, "xmlns:wsu" => WSUNamespace do
50
+ xml.wsse :UsernameToken, "wsu:Id" => wsu_id, "xmlns:wsu" => WSUNamespace do
129
51
  xml.wsse :Username, username
130
52
  xml.wsse :Nonce, nonce
131
53
  xml.wsu :Created, timestamp
@@ -151,7 +73,12 @@ module Savon
151
73
 
152
74
  # Returns a WSSE nonce.
153
75
  def nonce
154
- @nonce ||= Digest::SHA1.hexdigest String.random + timestamp
76
+ @nonce ||= Digest::SHA1.hexdigest random_string + timestamp
77
+ end
78
+
79
+ # Returns a random String of 100 characters.
80
+ def random_string
81
+ (0...100).map { ("a".."z").to_a[rand(26)] }.join
155
82
  end
156
83
 
157
84
  # Returns a WSSE timestamp.
@@ -159,5 +86,16 @@ module Savon
159
86
  @timestamp ||= Time.now.strftime Savon::SOAP::DateTimeFormat
160
87
  end
161
88
 
89
+ # Returns the "wsu:Id" attribute.
90
+ def wsu_id
91
+ "UsernameToken-#{count}"
92
+ end
93
+
94
+ # Simple counter.
95
+ def count
96
+ @count ||= 0
97
+ @count += 1
98
+ end
99
+
162
100
  end
163
101
  end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $:.unshift lib unless $:.include? lib
3
+
4
+ require "savon/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "savon"
8
+ s.version = Savon::Version
9
+ s.authors = "Daniel Harrington"
10
+ s.email = "me@rubiii.com"
11
+ s.homepage = "http://github.com/rubiii/#{s.name}"
12
+ s.summary = "Heavy metal Ruby SOAP client"
13
+ s.description = "Savon is the heavy metal Ruby SOAP client."
14
+
15
+ s.rubyforge_project = s.name
16
+
17
+ s.add_dependency "builder", "~> 2.1.2"
18
+ s.add_dependency "crack", "~> 0.1.8"
19
+ s.add_dependency "httpi", ">= 0.6.0"
20
+
21
+ s.add_development_dependency "rspec", "~> 2.0.0"
22
+ s.add_development_dependency "mocha", "~> 0.9.7"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.require_path = "lib"
26
+ end
@@ -1,36 +1,36 @@
1
1
  class ResponseFixture
2
+ class << self
2
3
 
3
- def self.authentication(value = nil)
4
- case value
5
- when :to_hash
6
- { :success => true,
7
- :authentication_value => {
8
- :token => "a68d1d6379b62ff339a0e0c69ed4d9cf",
9
- :token_hash => "AAAJxA;cIedoT;mY10ExZwG6JuKgp2OYKxow==",
10
- :client => "radclient"
11
- }
12
- }
13
- else
14
- @@authentication ||= load_fixture :authentication
4
+ def authentication(value = nil)
5
+ @authentication ||= load_fixture :authentication
6
+
7
+ case value
8
+ when :to_hash then Savon::SOAP::XML.to_hash(@authentication)[:authenticate_response][:return]
9
+ else @authentication
10
+ end
15
11
  end
16
- end
17
12
 
18
- def self.soap_fault
19
- @@soap_fault ||= load_fixture :soap_fault
20
- end
13
+ def soap_fault
14
+ @soap_fault ||= load_fixture :soap_fault
15
+ end
21
16
 
22
- def self.soap_fault12
23
- @@soap_fault12 ||= load_fixture :soap_fault12
24
- end
17
+ def soap_fault12
18
+ @soap_fault12 ||= load_fixture :soap_fault12
19
+ end
25
20
 
26
- def self.multi_ref
27
- @@multi_ref ||= load_fixture :multi_ref
28
- end
21
+ def multi_ref
22
+ @multi_ref ||= load_fixture :multi_ref
23
+ end
29
24
 
30
- private
25
+ def list
26
+ @list ||= load_fixture :list
27
+ end
31
28
 
32
- def self.load_fixture(fixture)
33
- File.read File.dirname(__FILE__) + "/xml/#{fixture}.xml"
34
- end
29
+ private
35
30
 
31
+ def load_fixture(fixture)
32
+ File.read File.dirname(__FILE__) + "/xml/#{fixture}.xml"
33
+ end
34
+
35
+ end
36
36
  end
@@ -0,0 +1,18 @@
1
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2
+ <soapenv:Body>
3
+ <MultiNamespacedEntryResponse xmlns="http://www.example.com/BusinessRulesEngine/xsd">
4
+ <history>
5
+ <ns10:case xmlns:ns10="http://www.example.com/Common/xsd">
6
+ <ns10:logTime>2010-09-21T18:22:01.558+10:00</ns10:logTime>
7
+ <ns10:logType>Notes Log</ns10:logType>
8
+ <ns10:logText>test</ns10:logText>
9
+ </ns10:case>
10
+ <ns11:case xmlns:ns11="http://www.example.com/Common/xsd">
11
+ <ns11:logTime>2010-09-21T18:22:07.038+10:00</ns11:logTime>
12
+ <ns11:logType>Notes Log</ns11:logType>
13
+ <ns11:logText>another test</ns11:logText>
14
+ </ns11:case>
15
+ </history>
16
+ </MultiNamespacedEntryResponse>
17
+ </soapenv:Body>
18
+ </soapenv:Envelope>
@@ -3,6 +3,12 @@ require "erb"
3
3
 
4
4
  class WSDLFixture
5
5
 
6
+ # Returns a given +fixture+. Defaults to :authentication.
7
+ def self.load(fixture = nil)
8
+ fixture ||= :authentication
9
+ method_missing fixture
10
+ end
11
+
6
12
  # Returns a WSDL document matching a given +method+ name when called without
7
13
  # arguments. Otherwise returns the expected value for a given +method+ name
8
14
  # matching a fixture.
@@ -1,12 +1,12 @@
1
1
  :authentication:
2
- :namespace_uri: "http://v1_0.ws.auth.order.example.com/"
2
+ :namespace: "http://v1_0.ws.auth.order.example.com/"
3
3
  :operations:
4
4
  :authenticate:
5
5
  :action: "authenticate"
6
6
  :input: "authenticate"
7
7
 
8
8
  :no_namespace:
9
- :namespace_uri: "urn:ActionWebService"
9
+ :namespace: "urn:ActionWebService"
10
10
  :operations:
11
11
  :get_all_contacts:
12
12
  :action: "/api/api/GetAllContacts"
@@ -19,7 +19,7 @@
19
19
  :input: "GetUserLoginById"
20
20
 
21
21
  :namespaced_actions:
22
- :namespace_uri: "http://api.example.com/api/"
22
+ :namespace: "http://api.example.com/api/"
23
23
  :operations:
24
24
  :get_api_key:
25
25
  :input: "GetApiKey"
@@ -32,7 +32,7 @@
32
32
  :action: "http://api.example.com/api/User.GetClients"
33
33
 
34
34
  :geotrust:
35
- :namespace_uri: "http://api.geotrust.com/webtrust/query"
35
+ :namespace: "http://api.geotrust.com/webtrust/query"
36
36
  :operations:
37
37
  :get_quick_approver_list:
38
38
  :action: "GetQuickApproverList"
@@ -1,86 +1,309 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Savon::Client do
4
- before { @client = Savon::Client.new EndpointHelper.wsdl_endpoint }
4
+ let(:client) { Savon::Client.new { wsdl.document = Endpoint.wsdl } }
5
5
 
6
- it "should be initialized with an endpoint String" do
7
- client = Savon::Client.new EndpointHelper.wsdl_endpoint
8
- client.request.http.proxy?.should be_false
9
- end
6
+ describe ".new" do
7
+ context "with a block expecting one argument" do
8
+ it "should yield the WSDL object" do
9
+ Savon::Client.new { |wsdl| wsdl.should be_a(Savon::WSDL::Document) }
10
+ end
11
+ end
10
12
 
11
- it "should accept a proxy URI via an optional Hash of options" do
12
- client = Savon::Client.new EndpointHelper.wsdl_endpoint, :proxy => "http://proxy"
13
- client.request.http.proxy?.should be_true
14
- client.request.http.proxy_address == "http://proxy"
13
+ context "with a block expecting two arguments" do
14
+ it "should yield the WSDL and HTTP objects" do
15
+ Savon::Client.new do |wsdl, http|
16
+ wsdl.should be_an(Savon::WSDL::Document)
17
+ http.should be_an(HTTPI::Request)
18
+ end
19
+ end
20
+ end
21
+
22
+ context "with a block expecting three arguments" do
23
+ it "should yield the WSDL, HTTP and WSSE objects" do
24
+ Savon::Client.new do |wsdl, http, wsse|
25
+ wsdl.should be_an(Savon::WSDL::Document)
26
+ http.should be_an(HTTPI::Request)
27
+ wsse.should be_an(Savon::WSSE)
28
+ end
29
+ end
30
+ end
31
+
32
+ context "with a block expecting no arguments" do
33
+ it "should let you access the WSDL object" do
34
+ Savon::Client.new { wsdl.should be_a(Savon::WSDL::Document) }
35
+ end
36
+
37
+ it "should let you access the HTTP object" do
38
+ Savon::Client.new { http.should be_an(HTTPI::Request) }
39
+ end
40
+
41
+ it "should let you access the WSSE object" do
42
+ Savon::Client.new { wsse.should be_a(Savon::WSSE) }
43
+ end
44
+ end
15
45
  end
16
46
 
17
- it "should accept a SOAP endpoint via an optional Hash of options" do
18
- client = Savon::Client.new EndpointHelper.wsdl_endpoint, :soap_endpoint => "http://localhost"
19
- client.wsdl.soap_endpoint.should == "http://localhost"
47
+ describe "#wsdl" do
48
+ it "should return the Savon::WSDL::Document" do
49
+ client.wsdl.should be_a(Savon::WSDL::Document)
50
+ end
20
51
  end
21
52
 
22
- it "should have a method that returns the Savon::WSDL" do
23
- @client.wsdl.should be_a(Savon::WSDL)
53
+ describe "#http" do
54
+ it "should return the HTTPI::Request" do
55
+ client.http.should be_an(HTTPI::Request)
56
+ end
24
57
  end
25
58
 
26
- it "should have a method that returns the Savon::Request" do
27
- @client.request.should be_a(Savon::Request)
59
+ describe "#wsse" do
60
+ it "should return the Savon::WSSE object" do
61
+ client.wsse.should be_a(Savon::WSSE)
62
+ end
28
63
  end
29
64
 
30
- it "should respond to available SOAP actions while behaving as expected otherwise" do
31
- WSDLFixture.authentication(:operations).keys.each do |soap_action|
32
- @client.respond_to?(soap_action).should be_true
65
+ describe "#request" do
66
+ before do
67
+ HTTPI.stubs(:get).returns(new_response(:body => WSDLFixture.load))
68
+ HTTPI.stubs(:post).returns(new_response)
33
69
  end
34
70
 
35
- @client.respond_to?(:object_id).should be_true
36
- @client.respond_to?(:some_undefined_method).should be_false
37
- end
71
+ context "without any arguments" do
72
+ it "should raise an ArgumentError" do
73
+ lambda { client.request }.should raise_error(ArgumentError)
74
+ end
75
+ end
38
76
 
39
- it "should dispatch available SOAP calls via method_missing and return the Savon::Response" do
40
- @client.authenticate.should be_a(Savon::Response)
41
- end
77
+ context "with a single argument (Symbol)" do
78
+ it "should set the input tag to result in <getUser>" do
79
+ client.request(:get_user) { soap.input.should == [:getUser, {}] }
80
+ end
42
81
 
43
- it "should disable the Savon::WSDL when passed a method with an exclamation mark" do
44
- @client.wsdl.enabled?.should be_true
45
- [:operations, :namespace_uri, :soap_endpoint].each do |method|
46
- Savon::WSDL.any_instance.expects(method).never
82
+ it "should set the target namespace with the default identifier" do
83
+ namespace = 'xmlns:wsdl="http://v1_0.ws.auth.order.example.com/"'
84
+ HTTPI::Request.any_instance.expects(:body=).with { |value| value.include? namespace }
85
+
86
+ client.request :get_user
87
+ end
88
+
89
+ it "should not set the target namespace if soap.namespace was set to nil" do
90
+ namespace = "http://v1_0.ws.auth.order.example.com/"
91
+ HTTPI::Request.any_instance.expects(:body=).with { |value| !value.include?(namespace) }
92
+
93
+ client.request(:get_user) { soap.namespace = nil }
94
+ end
47
95
  end
48
96
 
49
- response = @client.authenticate! do |soap|
50
- soap.input.should == "authenticate"
51
- soap.input.should == "authenticate"
97
+ context "with a single argument (String)" do
98
+ it "should set the input tag to result in <get_user>" do
99
+ client.request("get_user") { soap.input.should == [:get_user, {}] }
100
+ end
101
+ end
102
+
103
+ context "with a Symbol and a Hash" do
104
+ it "should set the input tag to result in <getUser active='true'>" do
105
+ client.request(:get_user, :active => true) { soap.input.should == [:getUser, { :active => true }] }
106
+ end
107
+ end
108
+
109
+ context "with two Symbols" do
110
+ it "should set the input tag to result in <wsdl:getUser>" do
111
+ client.request(:v1, :get_user) { soap.input.should == [:v1, :getUser, {}] }
112
+ end
113
+
114
+ it "should set the target namespace with the given identifier" do
115
+ namespace = 'xmlns:v1="http://v1_0.ws.auth.order.example.com/"'
116
+ HTTPI::Request.any_instance.expects(:body=).with { |value| value.include? namespace }
117
+
118
+ client.request :v1, :get_user
119
+ end
120
+
121
+ it "should not set the target namespace if soap.namespace was set to nil" do
122
+ namespace = "http://v1_0.ws.auth.order.example.com/"
123
+ HTTPI::Request.any_instance.expects(:body=).with { |value| !value.include?(namespace) }
124
+
125
+ client.request(:v1, :get_user) { soap.namespace = nil }
126
+ end
127
+ end
128
+
129
+ context "with two Symbols and a Hash" do
130
+ it "should set the input tag to result in <wsdl:getUser active='true'>" do
131
+ client.request(:wsdl, :get_user, :active => true) { soap.input.should == [:wsdl, :getUser, { :active => true }] }
132
+ end
133
+ end
134
+
135
+ context "with a block expecting one argument" do
136
+ it "should yield the SOAP object" do
137
+ client.request(:authenticate) { |soap| soap.should be_a(Savon::SOAP::XML) }
138
+ end
139
+ end
140
+
141
+ context "with a block expecting two arguments" do
142
+ it "should yield the SOAP and WSDL objects" do
143
+ client.request(:authenticate) do |soap, wsdl|
144
+ soap.should be_a(Savon::SOAP::XML)
145
+ wsdl.should be_an(Savon::WSDL::Document)
146
+ end
147
+ end
148
+ end
149
+
150
+ context "with a block expecting three arguments" do
151
+ it "should yield the SOAP, WSDL and HTTP objects" do
152
+ client.request(:authenticate) do |soap, wsdl, http|
153
+ soap.should be_a(Savon::SOAP::XML)
154
+ wsdl.should be_an(Savon::WSDL::Document)
155
+ http.should be_an(HTTPI::Request)
156
+ end
157
+ end
158
+ end
159
+
160
+ context "with a block expecting four arguments" do
161
+ it "should yield the SOAP, WSDL, HTTP and WSSE objects" do
162
+ client.request(:authenticate) do |soap, wsdl, http, wsse|
163
+ soap.should be_a(Savon::SOAP::XML)
164
+ wsdl.should be_a(Savon::WSDL::Document)
165
+ http.should be_an(HTTPI::Request)
166
+ wsse.should be_a(Savon::WSSE)
167
+ end
168
+ end
169
+ end
170
+
171
+ context "with a block expecting no arguments" do
172
+ it "should let you access the SOAP object" do
173
+ client.request(:authenticate) { soap.should be_a(Savon::SOAP::XML) }
174
+ end
175
+
176
+ it "should let you access the HTTP object" do
177
+ client.request(:authenticate) { http.should be_an(HTTPI::Request) }
178
+ end
179
+
180
+ it "should let you access the WSSE object" do
181
+ client.request(:authenticate) { wsse.should be_a(Savon::WSSE) }
182
+ end
183
+
184
+ it "should let you access the WSDL object" do
185
+ client.request(:authenticate) { wsdl.should be_a(Savon::WSDL::Document) }
186
+ end
52
187
  end
53
- response.should be_a(Savon::Response)
54
- @client.wsdl.enabled?.should be_false
55
188
  end
56
189
 
57
- it "should raise a Savon::SOAPFault in case of a SOAP fault" do
58
- client = Savon::Client.new EndpointHelper.wsdl_endpoint(:soap_fault)
59
- lambda { client.authenticate! }.should raise_error(Savon::SOAPFault)
190
+ context "with a remote WSDL document" do
191
+ let(:client) { Savon::Client.new { wsdl.document = Endpoint.wsdl } }
192
+ before { HTTPI.expects(:get).returns(new_response(:body => WSDLFixture.load)) }
193
+
194
+ it "should return a list of available SOAP actions" do
195
+ client.wsdl.soap_actions.should == [:authenticate]
196
+ end
197
+
198
+ it "adds a SOAPAction header containing the SOAP action name" do
199
+ HTTPI.stubs(:post).returns(new_response)
200
+
201
+ client.request :authenticate do
202
+ http.headers["SOAPAction"].should == %{"authenticate"}
203
+ end
204
+ end
205
+
206
+ it "should execute SOAP requests and return the response" do
207
+ HTTPI.expects(:post).returns(new_response)
208
+ response = client.request(:authenticate)
209
+
210
+ response.should be_a(Savon::SOAP::Response)
211
+ response.to_xml.should == ResponseFixture.authentication
212
+ end
60
213
  end
61
214
 
62
- it "should raise a Savon::HTTPError in case of an HTTP error" do
63
- client = Savon::Client.new EndpointHelper.wsdl_endpoint(:http_error)
64
- lambda { client.authenticate! }.should raise_error(Savon::HTTPError)
215
+ context "with a local WSDL document" do
216
+ let(:client) { Savon::Client.new { wsdl.document = "spec/fixtures/wsdl/xml/authentication.xml" } }
217
+
218
+ before { HTTPI.expects(:get).never }
219
+
220
+ it "should return a list of available SOAP actions" do
221
+ client.wsdl.soap_actions.should == [:authenticate]
222
+ end
223
+
224
+ it "adds a SOAPAction header containing the SOAP action name" do
225
+ HTTPI.stubs(:post).returns(new_response)
226
+
227
+ client.request :authenticate do
228
+ http.headers["SOAPAction"].should == %{"authenticate"}
229
+ end
230
+ end
231
+
232
+ it "should execute SOAP requests and return the response" do
233
+ HTTPI.expects(:post).returns(new_response)
234
+ response = client.request(:authenticate)
235
+
236
+ response.should be_a(Savon::SOAP::Response)
237
+ response.to_xml.should == ResponseFixture.authentication
238
+ end
65
239
  end
66
240
 
67
- it "should yield an instance of Savon::SOAP to a given block expecting one argument" do
68
- @client.authenticate { |soap| soap.should be_a(Savon::SOAP) }
241
+ context "without a WSDL document" do
242
+ let(:client) do
243
+ Savon::Client.new do
244
+ wsdl.endpoint = Endpoint.soap
245
+ wsdl.namespace = "http://v1_0.ws.auth.order.example.com/"
246
+ end
247
+ end
248
+
249
+ before { HTTPI.expects(:get).never }
250
+
251
+ it "raise an ArgumentError when trying to access the WSDL" do
252
+ lambda { client.wsdl.soap_actions }.should raise_error(ArgumentError)
253
+ end
254
+
255
+ it "adds a SOAPAction header containing the SOAP action name" do
256
+ HTTPI.stubs(:post).returns(new_response)
257
+
258
+ client.request :authenticate do
259
+ http.headers["SOAPAction"].should == %{"authenticate"}
260
+ end
261
+ end
262
+
263
+ it "should execute SOAP requests and return the response" do
264
+ HTTPI.expects(:post).returns(new_response)
265
+ response = client.request(:authenticate)
266
+
267
+ response.should be_a(Savon::SOAP::Response)
268
+ response.to_xml.should == ResponseFixture.authentication
269
+ end
69
270
  end
70
271
 
71
- it "should yield an instance of Savon::SOAP and Savon::WSSE to a gven block expecting two arguments" do
72
- @client.authenticate do |soap, wsse|
73
- soap.should be_a(Savon::SOAP)
74
- wsse.should be_a(Savon::WSSE)
272
+ context "when encountering a SOAP fault" do
273
+ let(:client) do
274
+ Savon::Client.new do
275
+ wsdl.endpoint = Endpoint.soap
276
+ wsdl.namespace = "http://v1_0.ws.auth.order.example.com/"
277
+ end
278
+ end
279
+
280
+ before { HTTPI::expects(:post).returns(new_response(:code => 500, :body => ResponseFixture.soap_fault)) }
281
+
282
+ it "should raise a Savon::SOAP::Fault" do
283
+ lambda { client.request :authenticate }.should raise_error(Savon::SOAP::Fault)
75
284
  end
76
285
  end
77
286
 
78
- it "should have a call method that forwards to method_missing for SOAP actions named after existing methods" do
79
- @client.call(:authenticate) { |soap| soap.should be_a(Savon::SOAP) }
287
+ context "when encountering an HTTP error" do
288
+ let(:client) do
289
+ Savon::Client.new do
290
+ wsdl.endpoint = Endpoint.soap
291
+ wsdl.namespace = "http://v1_0.ws.auth.order.example.com/"
292
+ end
293
+ end
294
+
295
+ before { HTTPI::expects(:post).returns(new_response(:code => 500)) }
296
+
297
+ it "should raise a Savon::HTTP::Error" do
298
+ lambda { client.request :authenticate }.should raise_error(Savon::HTTP::Error)
299
+ end
80
300
  end
81
301
 
82
- it "should raise a NoMethodError when the method does not match an available SOAP action or method" do
83
- lambda { @client.some_undefined_method }.should raise_error(NoMethodError)
302
+ def new_response(options = {})
303
+ defaults = { :code => 200, :headers => {}, :body => ResponseFixture.authentication }
304
+ response = defaults.merge options
305
+
306
+ HTTPI::Response.new response[:code], response[:headers], response[:body]
84
307
  end
85
308
 
86
- end
309
+ end