tictoc-savon 0.7.9

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 (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