savon 0.7.9 → 0.8.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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