johnreitano-savon 0.7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/CHANGELOG +124 -0
  2. data/README.textile +75 -0
  3. data/Rakefile +45 -0
  4. data/lib/savon/client.rb +84 -0
  5. data/lib/savon/core_ext/datetime.rb +8 -0
  6. data/lib/savon/core_ext/hash.rb +78 -0
  7. data/lib/savon/core_ext/net_http.rb +20 -0
  8. data/lib/savon/core_ext/object.rb +21 -0
  9. data/lib/savon/core_ext/string.rb +47 -0
  10. data/lib/savon/core_ext/symbol.rb +8 -0
  11. data/lib/savon/core_ext/uri.rb +10 -0
  12. data/lib/savon/core_ext.rb +3 -0
  13. data/lib/savon/request.rb +149 -0
  14. data/lib/savon/response.rb +99 -0
  15. data/lib/savon/soap.rb +176 -0
  16. data/lib/savon/wsdl.rb +122 -0
  17. data/lib/savon/wsse.rb +136 -0
  18. data/lib/savon.rb +34 -0
  19. data/spec/basic_spec_helper.rb +12 -0
  20. data/spec/endpoint_helper.rb +22 -0
  21. data/spec/fixtures/response/response_fixture.rb +36 -0
  22. data/spec/fixtures/response/xml/authentication.xml +14 -0
  23. data/spec/fixtures/response/xml/multi_ref.xml +39 -0
  24. data/spec/fixtures/response/xml/soap_fault.xml +8 -0
  25. data/spec/fixtures/response/xml/soap_fault12.xml +18 -0
  26. data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
  27. data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
  28. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
  29. data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
  30. data/spec/http_stubs.rb +23 -0
  31. data/spec/integration/http_basic_auth_spec.rb +16 -0
  32. data/spec/integration/server.rb +51 -0
  33. data/spec/savon/client_spec.rb +77 -0
  34. data/spec/savon/core_ext/datetime_spec.rb +12 -0
  35. data/spec/savon/core_ext/hash_spec.rb +138 -0
  36. data/spec/savon/core_ext/net_http_spec.rb +38 -0
  37. data/spec/savon/core_ext/object_spec.rb +40 -0
  38. data/spec/savon/core_ext/string_spec.rb +68 -0
  39. data/spec/savon/core_ext/symbol_spec.rb +11 -0
  40. data/spec/savon/core_ext/uri_spec.rb +15 -0
  41. data/spec/savon/request_spec.rb +89 -0
  42. data/spec/savon/response_spec.rb +137 -0
  43. data/spec/savon/savon_spec.rb +23 -0
  44. data/spec/savon/soap_spec.rb +171 -0
  45. data/spec/savon/wsdl_spec.rb +84 -0
  46. data/spec/savon/wsse_spec.rb +132 -0
  47. data/spec/spec_helper.rb +5 -0
  48. metadata +218 -0
@@ -0,0 +1,23 @@
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
@@ -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,77 @@
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 have a method that returns the Savon::WSDL" do
18
+ @client.wsdl.should be_a(Savon::WSDL)
19
+ end
20
+
21
+ it "should have a method that returns the Savon::Request" do
22
+ @client.request.should be_a(Savon::Request)
23
+ end
24
+
25
+ it "should respond to available SOAP actions while behaving as expected otherwise" do
26
+ WSDLFixture.authentication(:operations).keys.each do |soap_action|
27
+ @client.respond_to?(soap_action).should be_true
28
+ end
29
+
30
+ @client.respond_to?(:object_id).should be_true
31
+ @client.respond_to?(:some_undefined_method).should be_false
32
+ end
33
+
34
+ it "should dispatch available SOAP calls via method_missing and return the Savon::Response" do
35
+ @client.authenticate.should be_a(Savon::Response)
36
+ end
37
+
38
+ it "should disable the Savon::WSDL when passed a method with an exclamation mark" do
39
+ @client.wsdl.enabled?.should be_true
40
+ [:respond_to?, :operations, :namespace_uri, :soap_endpoint].each do |method|
41
+ Savon::WSDL.any_instance.expects(method).never
42
+ end
43
+
44
+ response = @client.authenticate! do |soap|
45
+ soap.input.should == "authenticate"
46
+ soap.input.should == "authenticate"
47
+ end
48
+ response.should be_a(Savon::Response)
49
+ @client.wsdl.enabled?.should be_false
50
+ end
51
+
52
+ it "should raise a Savon::SOAPFault in case of a SOAP fault" do
53
+ client = Savon::Client.new EndpointHelper.wsdl_endpoint(:soap_fault)
54
+ lambda { client.authenticate! }.should raise_error(Savon::SOAPFault)
55
+ end
56
+
57
+ it "should raise a Savon::HTTPError in case of an HTTP error" do
58
+ client = Savon::Client.new EndpointHelper.wsdl_endpoint(:http_error)
59
+ lambda { client.authenticate! }.should raise_error(Savon::HTTPError)
60
+ end
61
+
62
+ it "should yield an instance of Savon::SOAP to a given block expecting one argument" do
63
+ @client.authenticate { |soap| soap.should be_a(Savon::SOAP) }
64
+ end
65
+
66
+ it "should yield an instance of Savon::SOAP and Savon::WSSE to a gven block expecting two arguments" do
67
+ @client.authenticate do |soap, wsse|
68
+ soap.should be_a(Savon::SOAP)
69
+ wsse.should be_a(Savon::WSSE)
70
+ end
71
+ end
72
+
73
+ it "should raise a NoMethodError when the method does not match an available SOAP action or method" do
74
+ lambda { @client.some_undefined_method }.should raise_error(NoMethodError)
75
+ end
76
+
77
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+
3
+ describe DateTime do
4
+
5
+ describe "to_soap_value" do
6
+ it "returns an xs:dateTime compliant String" do
7
+ DateTime.new(2012, 03, 22, 16, 22, 33).to_soap_value.
8
+ should == "2012-03-22T16:22:33"
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,138 @@
1
+ require "spec_helper"
2
+
3
+ describe Hash do
4
+
5
+ describe "find_soap_body" do
6
+ it "returns the content from the 'soap:Body' element" do
7
+ { "soap:Envelope" => { "soap:Body" => "content" } }.find_soap_body.should == "content"
8
+ end
9
+
10
+ it "returns an empty Hash in case the 'soap:Body' element could not be found" do
11
+ { "some_hash" => "content" }.find_soap_body.should == {}
12
+ end
13
+ end
14
+
15
+ describe "to_soap_xml" do
16
+ describe "returns SOAP request compatible XML" do
17
+ it "for a simple Hash" do
18
+ { :some => "user" }.to_soap_xml.should == "<some>user</some>"
19
+ end
20
+
21
+ it "for a nested Hash" do
22
+ { :some => { :new => "user" } }.to_soap_xml.
23
+ should == "<some><new>user</new></some>"
24
+ end
25
+
26
+ it "for a Hash with multiple keys" do
27
+ soap_xml = { :all => "users", :before => "whatever" }.to_soap_xml
28
+
29
+ soap_xml.should include("<all>users</all>")
30
+ soap_xml.should include("<before>whatever</before>")
31
+ end
32
+
33
+ it "for a Hash containing an Array" do
34
+ { :some => ["user", "gorilla"] }.to_soap_xml.
35
+ should == "<some>user</some><some>gorilla</some>"
36
+ end
37
+
38
+ it "for a Hash containing an Array of Hashes" do
39
+ { :some => [{ :new => "user" }, { :old => "gorilla" }] }.to_soap_xml.
40
+ should == "<some><new>user</new></some><some><old>gorilla</old></some>"
41
+ end
42
+ end
43
+
44
+ it "converts Hash key Symbols to lowerCamelCase" do
45
+ { :find_or_create => "user" }.to_soap_xml.
46
+ should == "<findOrCreate>user</findOrCreate>"
47
+ end
48
+
49
+ it "does not convert Hash key Strings" do
50
+ { "find_or_create" => "user" }.to_soap_xml.
51
+ should == "<find_or_create>user</find_or_create>"
52
+ end
53
+
54
+ it "converts DateTime objects to xs:dateTime compliant Strings" do
55
+ { :before => DateTime.new(2012, 03, 22, 16, 22, 33) }.to_soap_xml.
56
+ should == "<before>" << "2012-03-22T16:22:33" << "</before>"
57
+ end
58
+
59
+ it "converts Objects responding to to_datetime to xs:dateTime compliant Strings" do
60
+ singleton = Object.new
61
+ def singleton.to_datetime
62
+ DateTime.new(2012, 03, 22, 16, 22, 33)
63
+ end
64
+
65
+ { :before => singleton }.to_soap_xml.
66
+ should == "<before>" << "2012-03-22T16:22:33" << "</before>"
67
+ end
68
+
69
+ it "calls to_s on Strings even if they respond to to_datetime" do
70
+ object = "gorilla"
71
+ object.expects(:to_s).returns object
72
+ object.expects(:to_datetime).never
73
+
74
+ { :name => object }.to_soap_xml.should == "<name>gorilla</name>"
75
+ end
76
+
77
+ it "call to_s on any other Object" do
78
+ [666, true, false, nil].each do |object|
79
+ { :some => object }.to_soap_xml.should == "<some>#{object}</some>"
80
+ end
81
+ end
82
+
83
+ it "preserves the order of Hash keys and values specified through :@inorder" do
84
+ { :find_user => { :name => "Lucy", :id => 666, :@inorder => [:id, :name] } }.to_soap_xml.
85
+ should == "<findUser><id>666</id><name>Lucy</name></findUser>"
86
+
87
+ { :find_user => { :by_name => { :mname => "in the", :lname => "Sky", :fname => "Lucy",
88
+ :@inorder => [:fname, :mname, :lname] } } }.to_soap_xml. should ==
89
+ "<findUser><byName><fname>Lucy</fname><mname>in the</mname><lname>Sky</lname></byName></findUser>"
90
+ end
91
+
92
+ it "raises an error if the :@inorder Array does not match the Hash keys" do
93
+ lambda { { :name => "Lucy", :id => 666, :@inorder => [:name] }.to_soap_xml }.
94
+ should raise_error(RuntimeError)
95
+
96
+ lambda { { :by_name => { :name => "Lucy", :lname => "Sky", :@inorder => [:mname, :name] } }.to_soap_xml }.
97
+ should raise_error(RuntimeError)
98
+ end
99
+ end
100
+
101
+ describe "map_soap_response" do
102
+ it "converts Hash key Strings to snake_case Symbols" do
103
+ { "userResponse" => { "accountStatus" => "active" } }.map_soap_response.
104
+ should == { :user_response => { :account_status => "active" } }
105
+ end
106
+
107
+ it "strips namespaces from Hash keys" do
108
+ { "ns:userResponse" => { "ns2:id" => "666" } }.map_soap_response.
109
+ should == { :user_response => { :id => "666" } }
110
+ end
111
+
112
+ it "converts Hash keys and values in Arrays" do
113
+ { "response" => [{ "name" => "dude" }, { "name" => "gorilla" }] }.map_soap_response.
114
+ should == { :response=> [{ :name => "dude" }, { :name => "gorilla" }] }
115
+ end
116
+
117
+ it "converts xsi:nil values to nil Objects" do
118
+ { "userResponse" => { "xsi:nil" => "true" } }.map_soap_response.
119
+ should == { :user_response => nil }
120
+ end
121
+
122
+ it "converts Hash values matching the xs:dateTime format into DateTime Objects" do
123
+ { "response" => { "at" => "2012-03-22T16:22:33" } }.map_soap_response.
124
+ should == { :response => { :at => DateTime.new(2012, 03, 22, 16, 22, 33) } }
125
+ end
126
+
127
+ it "converts Hash values matching 'true' to TrueClass" do
128
+ { "response" => { "active" => "false" } }.map_soap_response.
129
+ should == { :response => { :active => false } }
130
+ end
131
+
132
+ it "converts Hash values matching 'false' to FalseClass" do
133
+ { "response" => { "active" => "true" } }.map_soap_response.
134
+ should == { :response => { :active => true } }
135
+ end
136
+ end
137
+
138
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ describe Net::HTTP do
4
+ before do
5
+ @some_uri = URI "http://example.com"
6
+ @another_uri = URI "http://example.org"
7
+ @http = Net::HTTP.new @some_uri.host, @some_uri.port
8
+ end
9
+
10
+ describe "endpoint" do
11
+ it "changes the address and port of a Net::HTTP object" do
12
+ @http.address.should == @some_uri.host
13
+ @http.port.should == @some_uri.port
14
+
15
+ @http.endpoint @another_uri.host, @another_uri.port
16
+ @http.address.should == @another_uri.host
17
+ @http.port.should == @another_uri.port
18
+ end
19
+ end
20
+
21
+ describe "ssl_client_auth" do
22
+ it "accepts a Hash of options for SSL client authentication" do
23
+ @http.cert.should be_nil
24
+ @http.key.should be_nil
25
+ @http.ca_file.should be_nil
26
+ @http.verify_mode.should be_nil
27
+
28
+ @http.ssl_client_auth :cert => "cert", :key => "key",
29
+ :ca_file => "ca_file", :verify_mode => OpenSSL::SSL::VERIFY_PEER
30
+
31
+ @http.cert.should == "cert"
32
+ @http.key.should == "key"
33
+ @http.ca_file.should == "ca_file"
34
+ @http.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+
3
+ describe Object do
4
+
5
+ describe "blank?" do
6
+ it "returns true for Objects perceived to be blank" do
7
+ ["", false, nil, [], {}].each do |object|
8
+ object.should be_blank
9
+ end
10
+ end
11
+
12
+ it "returns false for every other Object" do
13
+ ["!blank", true, [:a], {:a => "b"}].each do |object|
14
+ object.should_not be_blank
15
+ end
16
+ end
17
+ end
18
+
19
+ describe "to_soap_key" do
20
+ it "calls to_s for every Object" do
21
+ Object.to_soap_key.should == Object.to_s
22
+ end
23
+ end
24
+
25
+ describe "to_soap_value" do
26
+ it "returns an xs:dateTime compliant String for Objects responding to to_datetime" do
27
+ singleton = Object.new
28
+ def singleton.to_datetime
29
+ DateTime.new(2012, 03, 22, 16, 22, 33)
30
+ end
31
+
32
+ singleton.to_soap_value.should == "2012-03-22T16:22:33"
33
+ end
34
+
35
+ it "calls to_s unless the Object responds to to_datetime" do
36
+ "value".to_soap_value.should == "value".to_s
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+
3
+ describe String do
4
+
5
+ describe "self.random" do
6
+ it "returns a random 100-character String" do
7
+ String.random.should be_a(String)
8
+ String.random.length.should == 100
9
+ end
10
+
11
+ it "returns a random String of a given length" do
12
+ String.random(50).should be_a(String)
13
+ String.random(50).length.should == 50
14
+ end
15
+ end
16
+
17
+ describe "snakecase" do
18
+ it "converts a lowerCamelCase String to snakecase" do
19
+ "lowerCamelCase".snakecase.should == "lower_camel_case"
20
+ end
21
+
22
+ it "converts period characters to underscores" do
23
+ "User.GetEmail".snakecase.should == "user_get_email"
24
+ end
25
+ end
26
+
27
+ describe "lower_camelcase" do
28
+ it "converts a snakecase String to lowerCamelCase" do
29
+ "lower_camel_case".lower_camelcase.should == "lowerCamelCase"
30
+ end
31
+ end
32
+
33
+ describe "strip_namespace" do
34
+ it "strips the namespace from a namespaced String" do
35
+ "ns:customer".strip_namespace.should == "customer"
36
+ end
37
+
38
+ it "returns the original String for a String without namespace" do
39
+ "customer".strip_namespace.should == "customer"
40
+ end
41
+ end
42
+
43
+ describe "map_soap_response" do
44
+ it "returns a DateTime Object for Strings matching the xs:dateTime format" do
45
+ "2012-03-22T16:22:33".map_soap_response.should ==
46
+ DateTime.new(2012, 03, 22, 16, 22, 33)
47
+ end
48
+
49
+ it "returns true for Strings matching 'true'" do
50
+ "true".map_soap_response.should be_true
51
+ end
52
+
53
+ it "returns false for Strings matching 'false'" do
54
+ "false".map_soap_response.should be_false
55
+ end
56
+
57
+ it "defaults to return the original value" do
58
+ "whatever".map_soap_response.should == "whatever"
59
+ end
60
+ end
61
+
62
+ describe "to_soap_value" do
63
+ it "calls to_s, bypassing Rails to_datetime extension for Strings" do
64
+ "string".to_soap_value.should == "string".to_s
65
+ end
66
+ end
67
+
68
+ end