tictoc-savon 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.autotest +5 -0
  2. data/CHANGELOG +176 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +64 -0
  5. data/Rakefile +50 -0
  6. data/lib/savon.rb +35 -0
  7. data/lib/savon/client.rb +131 -0
  8. data/lib/savon/core_ext.rb +8 -0
  9. data/lib/savon/core_ext/array.rb +31 -0
  10. data/lib/savon/core_ext/datetime.rb +10 -0
  11. data/lib/savon/core_ext/hash.rb +107 -0
  12. data/lib/savon/core_ext/net_http.rb +19 -0
  13. data/lib/savon/core_ext/object.rb +16 -0
  14. data/lib/savon/core_ext/string.rb +69 -0
  15. data/lib/savon/core_ext/symbol.rb +8 -0
  16. data/lib/savon/core_ext/uri.rb +10 -0
  17. data/lib/savon/logger.rb +56 -0
  18. data/lib/savon/request.rb +138 -0
  19. data/lib/savon/response.rb +174 -0
  20. data/lib/savon/soap.rb +302 -0
  21. data/lib/savon/version.rb +5 -0
  22. data/lib/savon/wsdl.rb +137 -0
  23. data/lib/savon/wsdl_stream.rb +85 -0
  24. data/lib/savon/wsse.rb +163 -0
  25. data/spec/basic_spec_helper.rb +11 -0
  26. data/spec/endpoint_helper.rb +23 -0
  27. data/spec/fixtures/gzip/gzip_response_fixture.rb +7 -0
  28. data/spec/fixtures/gzip/message.gz +0 -0
  29. data/spec/fixtures/response/response_fixture.rb +36 -0
  30. data/spec/fixtures/response/xml/authentication.xml +14 -0
  31. data/spec/fixtures/response/xml/multi_ref.xml +39 -0
  32. data/spec/fixtures/response/xml/soap_fault.xml +8 -0
  33. data/spec/fixtures/response/xml/soap_fault12.xml +18 -0
  34. data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
  35. data/spec/fixtures/wsdl/wsdl_fixture.yml +42 -0
  36. data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
  37. data/spec/fixtures/wsdl/xml/geotrust.xml +156 -0
  38. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
  39. data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
  40. data/spec/http_stubs.rb +26 -0
  41. data/spec/integration/http_basic_auth_spec.rb +16 -0
  42. data/spec/integration/server.rb +51 -0
  43. data/spec/savon/client_spec.rb +86 -0
  44. data/spec/savon/core_ext/array_spec.rb +49 -0
  45. data/spec/savon/core_ext/datetime_spec.rb +21 -0
  46. data/spec/savon/core_ext/hash_spec.rb +190 -0
  47. data/spec/savon/core_ext/net_http_spec.rb +38 -0
  48. data/spec/savon/core_ext/object_spec.rb +34 -0
  49. data/spec/savon/core_ext/string_spec.rb +99 -0
  50. data/spec/savon/core_ext/symbol_spec.rb +12 -0
  51. data/spec/savon/core_ext/uri_spec.rb +19 -0
  52. data/spec/savon/request_spec.rb +117 -0
  53. data/spec/savon/response_spec.rb +179 -0
  54. data/spec/savon/soap_spec.rb +202 -0
  55. data/spec/savon/wsdl_spec.rb +107 -0
  56. data/spec/savon/wsse_spec.rb +132 -0
  57. data/spec/spec.opts +4 -0
  58. data/spec/spec_helper.rb +5 -0
  59. metadata +229 -0
@@ -0,0 +1,115 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <definitions name="Api" xmlns:typens="urn:ActionWebService" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="urn:ActionWebService" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/">
3
+ <types>
4
+ <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:ActionWebService">
5
+ <xsd:complexType name="MpUser">
6
+ <xsd:all>
7
+ <xsd:element name="avatar_thumb_url" type="xsd:string"/>
8
+ <xsd:element name="speciality" type="xsd:string"/>
9
+ <xsd:element name="avatar_icon_url" type="xsd:string"/>
10
+ <xsd:element name="firstname" type="xsd:string"/>
11
+ <xsd:element name="city" type="xsd:string"/>
12
+ <xsd:element name="mp_id" type="xsd:int"/>
13
+ <xsd:element name="lastname" type="xsd:string"/>
14
+ <xsd:element name="login" type="xsd:string"/>
15
+ </xsd:all>
16
+ </xsd:complexType>
17
+ <xsd:complexType name="MpUserArray">
18
+ <xsd:complexContent>
19
+ <xsd:restriction base="soapenc:Array">
20
+ <xsd:attribute wsdl:arrayType="typens:MpUser[]" ref="soapenc:arrayType"/>
21
+ </xsd:restriction>
22
+ </xsd:complexContent>
23
+ </xsd:complexType>
24
+ <xsd:complexType name="McContact">
25
+ <xsd:all>
26
+ <xsd:element name="last_name" type="xsd:string"/>
27
+ <xsd:element name="email" type="xsd:string"/>
28
+ <xsd:element name="mp_id" type="xsd:int"/>
29
+ <xsd:element name="role" type="xsd:int"/>
30
+ <xsd:element name="login" type="xsd:string"/>
31
+ <xsd:element name="first_name" type="xsd:string"/>
32
+ </xsd:all>
33
+ </xsd:complexType>
34
+ <xsd:complexType name="McContactArray">
35
+ <xsd:complexContent>
36
+ <xsd:restriction base="soapenc:Array">
37
+ <xsd:attribute wsdl:arrayType="typens:McContact[]" ref="soapenc:arrayType"/>
38
+ </xsd:restriction>
39
+ </xsd:complexContent>
40
+ </xsd:complexType>
41
+ </xsd:schema>
42
+ </types>
43
+ <message name="GetUserLoginById">
44
+ <part name="api_key" type="xsd:string"/>
45
+ <part name="id" type="xsd:int"/>
46
+ </message>
47
+ <message name="GetUserLoginByIdResponse">
48
+ <part name="return" type="xsd:string"/>
49
+ </message>
50
+ <message name="GetAllContacts">
51
+ <part name="api_key" type="xsd:string"/>
52
+ <part name="login" type="xsd:string"/>
53
+ </message>
54
+ <message name="GetAllContactsResponse">
55
+ <part name="return" type="typens:McContactArray"/>
56
+ </message>
57
+ <message name="SearchUser">
58
+ <part name="api_key" type="xsd:string"/>
59
+ <part name="phrase" type="xsd:string"/>
60
+ <part name="page" type="xsd:string"/>
61
+ <part name="per_page" type="xsd:string"/>
62
+ </message>
63
+ <message name="SearchUserResponse">
64
+ <part name="return" type="typens:MpUserArray"/>
65
+ </message>
66
+ <portType name="ApiApiPort">
67
+ <operation name="GetUserLoginById">
68
+ <input message="typens:GetUserLoginById"/>
69
+ <output message="typens:GetUserLoginByIdResponse"/>
70
+ </operation>
71
+ <operation name="GetAllContacts">
72
+ <input message="typens:GetAllContacts"/>
73
+ <output message="typens:GetAllContactsResponse"/>
74
+ </operation>
75
+ <operation name="SearchUser">
76
+ <input message="typens:SearchUser"/>
77
+ <output message="typens:SearchUserResponse"/>
78
+ </operation>
79
+ </portType>
80
+ <binding name="ApiApiBinding" type="typens:ApiApiPort">
81
+ <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
82
+ <operation name="GetUserLoginById">
83
+ <soap:operation soapAction="/api/api/GetUserLoginById"/>
84
+ <input>
85
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
86
+ </input>
87
+ <output>
88
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
89
+ </output>
90
+ </operation>
91
+ <operation name="GetAllContacts">
92
+ <soap:operation soapAction="/api/api/GetAllContacts"/>
93
+ <input>
94
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
95
+ </input>
96
+ <output>
97
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
98
+ </output>
99
+ </operation>
100
+ <operation name="SearchUser">
101
+ <soap:operation soapAction="/api/api/SearchUser"/>
102
+ <input>
103
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
104
+ </input>
105
+ <output>
106
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
107
+ </output>
108
+ </operation>
109
+ </binding>
110
+ <service name="ApiService">
111
+ <port name="ApiApiPort" binding="typens:ApiApiBinding">
112
+ <soap:address location="http://example.com/api/api"/>
113
+ </port>
114
+ </service>
115
+ </definitions>
@@ -0,0 +1,26 @@
1
+ FakeWeb.allow_net_connect = false
2
+
3
+ # Some WSDL and SOAP request.
4
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint, :body => WSDLFixture.authentication
5
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint, :body => ResponseFixture.authentication
6
+
7
+ # WSDL and SOAP request with a Savon::SOAPFault.
8
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:soap_fault), :body => WSDLFixture.authentication
9
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:soap_fault), :body => ResponseFixture.soap_fault
10
+
11
+ # WSDL and SOAP request with a Savon::HTTPError.
12
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:http_error), :body => WSDLFixture.authentication
13
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:http_error), :body => "", :status => ["404", "Not Found"]
14
+
15
+ # WSDL and SOAP request with an invalid endpoint.
16
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:invalid), :body => ""
17
+ FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:invalid), :body => "", :status => ["404", "Not Found"]
18
+
19
+ # WSDL request returning a WSDL document where the main sections are not namespaced.
20
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:no_namespace), :body => WSDLFixture.no_namespace
21
+
22
+ # WSDL request returning a WSDL document with namespaced SOAP actions.
23
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:namespaced_actions), :body => WSDLFixture.namespaced_actions
24
+
25
+ # WSDL request returning a WSDL document with geotrust SOAP actions.
26
+ FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:geotrust), :body => WSDLFixture.geotrust
@@ -0,0 +1,16 @@
1
+ require "basic_spec_helper"
2
+
3
+ describe Savon do
4
+ before { @client = Savon::Client.new "http://localhost:8080/http-basic-auth" }
5
+
6
+ it "should be able to handle HTTP basic authentication" do
7
+ @client.request.basic_auth "user", "password"
8
+ response = @client.do_something!
9
+ response.to_hash[:authenticate_response][:return][:success].should == true
10
+ end
11
+
12
+ it "should raise a Savon::HTTPError in case authentication failed" do
13
+ lambda { @client.do_something! }.should raise_error(Savon::HTTPError)
14
+ end
15
+
16
+ end
@@ -0,0 +1,51 @@
1
+ require "webrick"
2
+
3
+ include WEBrick
4
+
5
+ # Run WEBrick. Yields the server to a given block.
6
+ def run_webrick(config = {})
7
+ config.update :Port => 8080
8
+ server = HTTPServer.new config
9
+ yield server if block_given?
10
+ ["INT", "TERM"].each { |signal| trap(signal) { server.shutdown } }
11
+ server.start
12
+ end
13
+
14
+ # Returns the SOAP response fixture for a given +file+.
15
+ def respond_with(file)
16
+ response_path = File.dirname(__FILE__) + "/../fixtures/response/xml"
17
+ File.read "#{response_path}/#{file}.xml"
18
+ end
19
+
20
+ # Returns HTML links for a given Hash of link URI's and names.
21
+ def link_to(links)
22
+ links.map { |link| "<a href='#{link[:uri]}'>#{link[:name]}</a>" }.join("<br>")
23
+ end
24
+
25
+ run_webrick do |server|
26
+ user, password, realm = "user", "password", "realm"
27
+
28
+ htdigest = HTTPAuth::Htdigest.new "/tmp/webrick-htdigest"
29
+ htdigest.set_passwd realm, user, password
30
+ authenticator = HTTPAuth::DigestAuth.new :UserDB => htdigest, :Realm => realm
31
+
32
+ # Homepage including links to subpages.
33
+ server.mount_proc("/") do |request, response|
34
+ response.body = link_to [
35
+ { :uri => "http-basic-auth", :name => "HTTP basic auth" },
36
+ { :uri => "http-digest-auth", :name => "HTTP digest auth" }
37
+ ]
38
+ end
39
+
40
+ # HTTP basic authentication.
41
+ server.mount_proc("/http-basic-auth") do |request, response|
42
+ HTTPAuth.basic_auth(request, response, realm) { |u, p| u == user && p == password }
43
+ response.body = respond_with :authentication
44
+ end
45
+
46
+ # HTTP digest authentication.
47
+ server.mount_proc("/http-digest-auth") do |request, response|
48
+ authenticator.authenticate request, response
49
+ response.body = "HTTP digest authentication successfull"
50
+ end
51
+ end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Client do
4
+ before { @client = Savon::Client.new EndpointHelper.wsdl_endpoint }
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
10
+
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"
15
+ end
16
+
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"
20
+ end
21
+
22
+ it "should have a method that returns the Savon::WSDL" do
23
+ @client.wsdl.should be_a(Savon::WSDL)
24
+ end
25
+
26
+ it "should have a method that returns the Savon::Request" do
27
+ @client.request.should be_a(Savon::Request)
28
+ end
29
+
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
33
+ end
34
+
35
+ @client.respond_to?(:object_id).should be_true
36
+ @client.respond_to?(:some_undefined_method).should be_false
37
+ end
38
+
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
42
+
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
47
+ end
48
+
49
+ response = @client.authenticate! do |soap|
50
+ soap.input.should == "authenticate"
51
+ soap.input.should == "authenticate"
52
+ end
53
+ response.should be_a(Savon::Response)
54
+ @client.wsdl.enabled?.should be_false
55
+ end
56
+
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)
60
+ end
61
+
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)
65
+ end
66
+
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) }
69
+ end
70
+
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)
75
+ end
76
+ end
77
+
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) }
80
+ end
81
+
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)
84
+ end
85
+
86
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe Array do
4
+
5
+ describe "to_soap_xml" do
6
+ it "should return the XML for an Array of Hashes" do
7
+ array = [{ :name => "adam" }, { :name => "eve" }]
8
+ result = "<user><name>adam</name></user><user><name>eve</name></user>"
9
+
10
+ array.to_soap_xml("user").should == result
11
+ end
12
+
13
+ it "should return the XML for an Array of different Objects" do
14
+ array = [:symbol, "string", 123]
15
+ result = "<value>symbol</value><value>string</value><value>123</value>"
16
+
17
+ array.to_soap_xml("value").should == result
18
+ end
19
+
20
+ it "should default to escape special characters" do
21
+ array = ["<tag />", "adam & eve"]
22
+ result = "<value>&lt;tag /&gt;</value><value>adam &amp; eve</value>"
23
+
24
+ array.to_soap_xml("value").should == result
25
+ end
26
+
27
+ it "should not escape special characters when told to" do
28
+ array = ["<tag />", "adam & eve"]
29
+ result = "<value><tag /></value><value>adam & eve</value>"
30
+
31
+ array.to_soap_xml("value", false).should == result
32
+ end
33
+
34
+ it "should add attributes to a given tag" do
35
+ array = ["adam", "eve"]
36
+ result = '<value active="true">adam</value><value active="true">eve</value>'
37
+
38
+ array.to_soap_xml("value", :escape_xml, :active => true).should == result
39
+ end
40
+
41
+ it "should add attributes to duplicate tags" do
42
+ array = ["adam", "eve"]
43
+ result = '<value id="1">adam</value><value id="2">eve</value>'
44
+
45
+ array.to_soap_xml("value", :escape_xml, :id => [1, 2]).should == result
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe DateTime do
4
+ before do
5
+ @datetime = DateTime.new 2012, 03, 22, 16, 22, 33
6
+ @datetime_string = "2012-03-22T16:22:33Z"
7
+ end
8
+
9
+ describe "to_soap_value" do
10
+ it "should return an xs:dateTime compliant String" do
11
+ @datetime.to_soap_value.should == @datetime_string
12
+ end
13
+ end
14
+
15
+ describe "to_soap_value!" do
16
+ it "should act like :to_soap_value" do
17
+ @datetime.to_soap_value.should == @datetime_string
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,190 @@
1
+ require "spec_helper"
2
+
3
+ describe Hash do
4
+
5
+ describe "find_soap_body" do
6
+ it "should return the content from the 'soap:Body' element" do
7
+ soap_body = { "soap:Envelope" => { "soap:Body" => "content" } }
8
+ soap_body.find_soap_body.should == "content"
9
+ end
10
+
11
+ it "should return an empty Hash in case the 'soap:Body' element could not be found" do
12
+ soap_body = { "some_hash" => "content" }
13
+ soap_body.find_soap_body.should == {}
14
+ end
15
+ end
16
+
17
+ describe "to_soap_xml" do
18
+ describe "should return SOAP request compatible XML" do
19
+ it "for a simple Hash" do
20
+ hash, result = { :some => "user" }, "<some>user</some>"
21
+ hash.to_soap_xml.should == result
22
+ end
23
+
24
+ it "for a nested Hash" do
25
+ hash, result = { :some => { :new => "user" } }, "<some><new>user</new></some>"
26
+ hash.to_soap_xml.should == result
27
+ end
28
+
29
+ it "for a Hash with multiple keys" do
30
+ hash = { :all => "users", :before => "whatever" }
31
+ hash.to_soap_xml.should include("<all>users</all>", "<before>whatever</before>")
32
+ end
33
+
34
+ it "for a Hash containing an Array" do
35
+ hash, result = { :some => ["user", "gorilla"] }, "<some>user</some><some>gorilla</some>"
36
+ hash.to_soap_xml.should == result
37
+ end
38
+
39
+ it "for a Hash containing an Array of Hashes" do
40
+ hash = { :some => [{ :new => "user" }, { :old => "gorilla" }] }
41
+ result = "<some><new>user</new></some><some><old>gorilla</old></some>"
42
+
43
+ hash.to_soap_xml.should == result
44
+ end
45
+ end
46
+
47
+ it "should convert Hash key Symbols to lowerCamelCase" do
48
+ hash, result = { :find_or_create => "user" }, "<findOrCreate>user</findOrCreate>"
49
+ hash.to_soap_xml.should == result
50
+ end
51
+
52
+ it "should not convert Hash key Strings" do
53
+ hash, result = { "find_or_create" => "user" }, "<find_or_create>user</find_or_create>"
54
+ hash.to_soap_xml.should == result
55
+ end
56
+
57
+ it "should convert DateTime objects to xs:dateTime compliant Strings" do
58
+ hash = { :before => DateTime.new(2012, 03, 22, 16, 22, 33) }
59
+ result = "<before>2012-03-22T16:22:33Z</before>"
60
+
61
+ hash.to_soap_xml.should == result
62
+ end
63
+
64
+ it "should convert Objects responding to to_datetime to xs:dateTime compliant Strings" do
65
+ singleton = Object.new
66
+ def singleton.to_datetime
67
+ DateTime.new(2012, 03, 22, 16, 22, 33)
68
+ end
69
+
70
+ hash, result = { :before => singleton }, "<before>2012-03-22T16:22:33Z</before>"
71
+ hash.to_soap_xml.should == result
72
+ end
73
+
74
+ it "should call to_s on Strings even if they respond to to_datetime" do
75
+ object = "gorilla"
76
+ object.expects(:to_datetime).never
77
+
78
+ hash, result = { :name => object }, "<name>gorilla</name>"
79
+ hash.to_soap_xml.should == result
80
+ end
81
+
82
+ it "should call to_s on any other Object" do
83
+ [666, true, false, nil].each do |object|
84
+ { :some => object }.to_soap_xml.should == "<some>#{object}</some>"
85
+ end
86
+ end
87
+
88
+ it "should default to escape special characters" do
89
+ result = { :some => { :nested => "<tag />" }, :tag => "<tag />" }.to_soap_xml
90
+ result.should include("<tag>&lt;tag /&gt;</tag>")
91
+ result.should include("<some><nested>&lt;tag /&gt;</nested></some>")
92
+ end
93
+
94
+ it "should not escape special characters for keys marked with an exclamation mark" do
95
+ result = { :some => { :nested! => "<tag />" }, :tag! => "<tag />" }.to_soap_xml
96
+ result.should include("<tag><tag /></tag>")
97
+ result.should include("<some><nested><tag /></nested></some>")
98
+ end
99
+
100
+ it "should preserve the order of Hash keys and values specified through :order!" do
101
+ hash = { :find_user => { :name => "Lucy", :id => 666, :order! => [:id, :name] } }
102
+ result = "<findUser><id>666</id><name>Lucy</name></findUser>"
103
+ hash.to_soap_xml.should == result
104
+
105
+ hash = { :find_user => { :mname => "in the", :lname => "Sky", :fname => "Lucy", :order! => [:fname, :mname, :lname] } }
106
+ result = "<findUser><fname>Lucy</fname><mname>in the</mname><lname>Sky</lname></findUser>"
107
+ hash.to_soap_xml.should == result
108
+ end
109
+
110
+ it "should raise an error if the :order! Array does not match the Hash keys" do
111
+ hash = { :name => "Lucy", :id => 666, :order! => [:name] }
112
+ lambda { hash.to_soap_xml }.should raise_error(ArgumentError)
113
+
114
+ hash = { :by_name => { :name => "Lucy", :lname => "Sky", :order! => [:mname, :name] } }
115
+ lambda { hash.to_soap_xml }.should raise_error(ArgumentError)
116
+ end
117
+
118
+ it "should add attributes to Hash keys specified through :attributes!" do
119
+ hash = { :find_user => { :person => "Lucy", :attributes! => { :person => { :id => 666 } } } }
120
+ result = '<findUser><person id="666">Lucy</person></findUser>'
121
+ hash.to_soap_xml.should == result
122
+
123
+ hash = { :find_user => { :person => "Lucy", :attributes! => { :person => { :id => 666, :city => "Hamburg" } } } }
124
+ soap_xml = hash.to_soap_xml
125
+ soap_xml.should include('id="666"', 'city="Hamburg"')
126
+ end
127
+
128
+ it "should add attributes to duplicate Hash keys specified through :attributes!" do
129
+ hash = { :find_user => { :person => ["Lucy", "Anna"], :attributes! => { :person => { :id => [1, 3] } } } }
130
+ result = '<findUser><person id="1">Lucy</person><person id="3">Anna</person></findUser>'
131
+ hash.to_soap_xml.should == result
132
+
133
+ hash = { :find_user => { :person => ["Lucy", "Anna"], :attributes! => { :person => { :active => "true" } } } }
134
+ result = '<findUser><person active="true">Lucy</person><person active="true">Anna</person></findUser>'
135
+ hash.to_soap_xml.should == result
136
+ end
137
+ end
138
+
139
+ describe "map_soap_response" do
140
+ it "should convert Hash key Strings to snake_case Symbols" do
141
+ soap_response = { "userResponse" => { "accountStatus" => "active" } }
142
+ result = { :user_response => { :account_status => "active" } }
143
+
144
+ soap_response.map_soap_response.should == result
145
+ end
146
+
147
+ it "should strip namespaces from Hash keys" do
148
+ soap_response = { "ns:userResponse" => { "ns2:id" => "666" } }
149
+ result = { :user_response => { :id => "666" } }
150
+
151
+ soap_response.map_soap_response.should == result
152
+ end
153
+
154
+ it "should convert Hash keys and values in Arrays" do
155
+ soap_response = { "response" => [{ "name" => "dude" }, { "name" => "gorilla" }] }
156
+ result = { :response=> [{ :name => "dude" }, { :name => "gorilla" }] }
157
+
158
+ soap_response.map_soap_response.should == result
159
+ end
160
+
161
+ it "should convert xsi:nil values to nil Objects" do
162
+ soap_response = { "userResponse" => { "xsi:nil" => "true" } }
163
+ result = { :user_response => nil }
164
+
165
+ soap_response.map_soap_response.should == result
166
+ end
167
+
168
+ it "should convert Hash values matching the xs:dateTime format into DateTime Objects" do
169
+ soap_response = { "response" => { "at" => "2012-03-22T16:22:33" } }
170
+ result = { :response => { :at => DateTime.new(2012, 03, 22, 16, 22, 33) } }
171
+
172
+ soap_response.map_soap_response.should == result
173
+ end
174
+
175
+ it "should convert Hash values matching 'true' to TrueClass" do
176
+ soap_response = { "response" => { "active" => "false" } }
177
+ result = { :response => { :active => false } }
178
+
179
+ soap_response.map_soap_response.should == result
180
+ end
181
+
182
+ it "should convert Hash values matching 'false' to FalseClass" do
183
+ soap_response = { "response" => { "active" => "true" } }
184
+ result = { :response => { :active => true } }
185
+
186
+ soap_response.map_soap_response.should == result
187
+ end
188
+ end
189
+
190
+ end