s-savon 0.8.6

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 (61) hide show
  1. data/.gitignore +9 -0
  2. data/.rspec +1 -0
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +461 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +37 -0
  8. data/Rakefile +40 -0
  9. data/lib/savon.rb +14 -0
  10. data/lib/savon/client.rb +157 -0
  11. data/lib/savon/core_ext/hash.rb +70 -0
  12. data/lib/savon/core_ext/object.rb +14 -0
  13. data/lib/savon/core_ext/string.rb +51 -0
  14. data/lib/savon/core_ext/time.rb +14 -0
  15. data/lib/savon/error.rb +6 -0
  16. data/lib/savon/global.rb +75 -0
  17. data/lib/savon/http/error.rb +42 -0
  18. data/lib/savon/soap.rb +24 -0
  19. data/lib/savon/soap/fault.rb +59 -0
  20. data/lib/savon/soap/request.rb +61 -0
  21. data/lib/savon/soap/response.rb +80 -0
  22. data/lib/savon/soap/xml.rb +187 -0
  23. data/lib/savon/version.rb +5 -0
  24. data/lib/savon/wsdl/document.rb +112 -0
  25. data/lib/savon/wsdl/parser.rb +102 -0
  26. data/lib/savon/wsdl/request.rb +35 -0
  27. data/lib/savon/wsse.rb +150 -0
  28. data/savon.gemspec +29 -0
  29. data/spec/fixtures/gzip/message.gz +0 -0
  30. data/spec/fixtures/response/another_soap_fault.xml +14 -0
  31. data/spec/fixtures/response/authentication.xml +14 -0
  32. data/spec/fixtures/response/header.xml +13 -0
  33. data/spec/fixtures/response/list.xml +18 -0
  34. data/spec/fixtures/response/multi_ref.xml +39 -0
  35. data/spec/fixtures/response/soap_fault.xml +8 -0
  36. data/spec/fixtures/response/soap_fault12.xml +18 -0
  37. data/spec/fixtures/wsdl/authentication.xml +63 -0
  38. data/spec/fixtures/wsdl/geotrust.xml +156 -0
  39. data/spec/fixtures/wsdl/namespaced_actions.xml +307 -0
  40. data/spec/fixtures/wsdl/no_namespace.xml +115 -0
  41. data/spec/fixtures/wsdl/two_bindings.xml +25 -0
  42. data/spec/savon/client_spec.rb +346 -0
  43. data/spec/savon/core_ext/hash_spec.rb +121 -0
  44. data/spec/savon/core_ext/object_spec.rb +19 -0
  45. data/spec/savon/core_ext/string_spec.rb +57 -0
  46. data/spec/savon/core_ext/time_spec.rb +13 -0
  47. data/spec/savon/http/error_spec.rb +52 -0
  48. data/spec/savon/savon_spec.rb +85 -0
  49. data/spec/savon/soap/fault_spec.rb +89 -0
  50. data/spec/savon/soap/request_spec.rb +45 -0
  51. data/spec/savon/soap/response_spec.rb +174 -0
  52. data/spec/savon/soap/xml_spec.rb +335 -0
  53. data/spec/savon/soap_spec.rb +21 -0
  54. data/spec/savon/wsdl/document_spec.rb +132 -0
  55. data/spec/savon/wsdl/parser_spec.rb +99 -0
  56. data/spec/savon/wsdl/request_spec.rb +15 -0
  57. data/spec/savon/wsse_spec.rb +213 -0
  58. data/spec/spec_helper.rb +14 -0
  59. data/spec/support/endpoint.rb +25 -0
  60. data/spec/support/fixture.rb +37 -0
  61. metadata +251 -0
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::SOAP do
4
+
5
+ it "should contain the SOAP namespace for each supported SOAP version" do
6
+ Savon::SOAP::Versions.each do |soap_version|
7
+ Savon::SOAP::Namespace[soap_version].should be_a(String)
8
+ Savon::SOAP::Namespace[soap_version].should_not be_empty
9
+ end
10
+ end
11
+
12
+ it "should contain a Rage of supported SOAP versions" do
13
+ Savon::SOAP::Versions.should == (1..2)
14
+ end
15
+
16
+ it "should contain a Regexp matching the xs:dateTime format" do
17
+ Savon::SOAP::DateTimeRegexp.should be_a(Regexp)
18
+ (Savon::SOAP::DateTimeRegexp === "2012-03-22T16:22:33").should be_true
19
+ end
20
+
21
+ end
@@ -0,0 +1,132 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::WSDL::Document do
4
+
5
+ shared_examples_for "a WSDL document" do
6
+ it "should be present" do
7
+ wsdl.should be_present
8
+ end
9
+
10
+ describe "#namespace" do
11
+ it "should return the namespace URI" do
12
+ wsdl.namespace.should == "http://v1_0.ws.auth.order.example.com/"
13
+ end
14
+ end
15
+
16
+ describe "#soap_actions" do
17
+ it "should return an Array of available SOAP actions" do
18
+ wsdl.soap_actions.should == [:authenticate]
19
+ end
20
+ end
21
+
22
+ describe "#soap_action" do
23
+ it "should return the SOAP action for a given key" do
24
+ wsdl.soap_action(:authenticate).should == "authenticate"
25
+ end
26
+
27
+ it "should return nil if no SOAP action could be found" do
28
+ wsdl.soap_action(:unknown).should be_nil
29
+ end
30
+ end
31
+
32
+ describe "#soap_input" do
33
+ it "should return the SOAP input tag for a given key" do
34
+ wsdl.soap_input(:authenticate).should == :authenticate
35
+ end
36
+
37
+ it "should return nil if no SOAP input tag could be found" do
38
+ wsdl.soap_input(:unknown).should be_nil
39
+ end
40
+ end
41
+
42
+ describe "#operations" do
43
+ it "should return a Hash of SOAP operations" do
44
+ wsdl.operations.should == {
45
+ :authenticate => {
46
+ :input => "authenticate", :action => "authenticate"
47
+ }
48
+ }
49
+ end
50
+ end
51
+
52
+ describe "#document" do
53
+ it "should return the raw WSDL document" do
54
+ wsdl.document.should == Fixture.wsdl(:authentication)
55
+ end
56
+
57
+ it "should be memoized" do
58
+ wsdl.document.should equal(wsdl.document)
59
+ end
60
+ end
61
+ end
62
+
63
+ context "with a remote document" do
64
+ let(:wsdl) { Savon::WSDL::Document.new HTTPI::Request.new, Endpoint.wsdl }
65
+
66
+ before do
67
+ response = HTTPI::Response.new 200, {}, Fixture.wsdl(:authentication)
68
+ HTTPI.stubs(:get).returns(response)
69
+ end
70
+
71
+ it_should_behave_like "a WSDL document"
72
+
73
+ describe "#element_form_default" do
74
+ it "should return :unqualified" do
75
+ wsdl.element_form_default.should == :unqualified
76
+ end
77
+ end
78
+ end
79
+
80
+ context "with a local document" do
81
+ let(:wsdl) do
82
+ wsdl = "spec/fixtures/wsdl/authentication.xml"
83
+ Savon::WSDL::Document.new HTTPI::Request.new, wsdl
84
+ end
85
+
86
+ before { HTTPI.expects(:get).never }
87
+
88
+ it_should_behave_like "a WSDL document"
89
+ end
90
+
91
+ context "without a WSDL document" do
92
+ let(:wsdl) { Savon::WSDL::Document.new HTTPI::Request.new }
93
+
94
+ it "should not be present" do
95
+ wsdl.should_not be_present
96
+ end
97
+
98
+ describe "#soap_action" do
99
+ it "should return nil" do
100
+ wsdl.soap_action(:authenticate).should be_nil
101
+ end
102
+ end
103
+
104
+ describe "#soap_input" do
105
+ it "should return nil" do
106
+ wsdl.soap_input(:authenticate).should be_nil
107
+ end
108
+ end
109
+
110
+ describe "#document" do
111
+ it "should raise an ArgumentError" do
112
+ lambda { wsdl.document }.should raise_error(ArgumentError)
113
+ end
114
+ end
115
+ end
116
+
117
+ context "with a WSDL document containing elementFormDefault='qualified'" do
118
+ let(:wsdl) { Savon::WSDL::Document.new HTTPI::Request.new, Endpoint.wsdl }
119
+
120
+ before do
121
+ response = HTTPI::Response.new 200, {}, Fixture.wsdl(:geotrust)
122
+ HTTPI.stubs(:get).returns(response)
123
+ end
124
+
125
+ describe "#element_form_default" do
126
+ it "should return :qualified" do
127
+ wsdl.element_form_default.should == :qualified
128
+ end
129
+ end
130
+ end
131
+
132
+ end
@@ -0,0 +1,99 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::WSDL::Parser do
4
+
5
+ context "with namespaced_actions.xml" do
6
+ let(:parser) { new_parser :namespaced_actions }
7
+
8
+ it "should return the target namespace" do
9
+ parser.namespace.should == "http://api.example.com/api/"
10
+ end
11
+
12
+ it "should return the SOAP endpoint" do
13
+ parser.endpoint.should == URI("https://api.example.com/api/api.asmx")
14
+ end
15
+
16
+ it "should return the available SOAP operations" do
17
+ parser.operations.should match_operations(
18
+ :get_api_key => { :input => "GetApiKey", :action => "http://api.example.com/api/User.GetApiKey" },
19
+ :delete_client => { :input => "DeleteClient", :action => "http://api.example.com/api/Client.Delete" },
20
+ :get_clients => { :input => "GetClients", :action => "http://api.example.com/api/User.GetClients" }
21
+ )
22
+ end
23
+
24
+ it "should return that :element_form_default is set to :qualified" do
25
+ parser.element_form_default.should == :qualified
26
+ end
27
+ end
28
+
29
+ context "with no_namespace.xml" do
30
+ let(:parser) { new_parser :no_namespace }
31
+
32
+ it "should return the target namespace" do
33
+ parser.namespace.should == "urn:ActionWebService"
34
+ end
35
+
36
+ it "should return the SOAP endpoint" do
37
+ parser.endpoint.should == URI("http://example.com/api/api")
38
+ end
39
+
40
+ it "should return the available SOAP operations" do
41
+ parser.operations.should match_operations(
42
+ :search_user => { :input => "SearchUser", :action => "/api/api/SearchUser" },
43
+ :get_user_login_by_id => { :input => "GetUserLoginById", :action => "/api/api/GetUserLoginById" },
44
+ :get_all_contacts => { :input => "GetAllContacts", :action => "/api/api/GetAllContacts" }
45
+ )
46
+ end
47
+
48
+ it "should return that :element_form_default is set to :unqualified" do
49
+ parser.element_form_default.should == :unqualified
50
+ end
51
+ end
52
+
53
+ context "with geotrust.xml" do
54
+ let(:parser) { new_parser :geotrust }
55
+
56
+ it "should return the target namespace" do
57
+ parser.namespace.should == "http://api.geotrust.com/webtrust/query"
58
+ end
59
+
60
+ it "should return the SOAP endpoint" do
61
+ parser.endpoint.should == URI("https://test-api.geotrust.com/webtrust/query.jws")
62
+ end
63
+
64
+ it "should return the available SOAP operations" do
65
+ parser.operations.should match_operations(
66
+ :get_quick_approver_list => { :input => "GetQuickApproverList", :action => "GetQuickApproverList" },
67
+ :hello => { :input => "hello", :action => "hello" }
68
+ )
69
+ end
70
+
71
+ it "should return that :element_form_default is set to :qualified" do
72
+ parser.element_form_default.should == :qualified
73
+ end
74
+ end
75
+
76
+ context "with two_bindings.xml" do
77
+ let(:parser) { new_parser :two_bindings }
78
+
79
+ it "should merge operations from all binding sections (until we have an example where it makes sense to do otherwise)" do
80
+ parser.operations.keys.map(&:to_s).sort.should ==
81
+ %w{post post11only post12only}
82
+ end
83
+ end
84
+
85
+ RSpec::Matchers.define :match_operations do |expected|
86
+ match do |actual|
87
+ actual.should have(expected.keys.size).items
88
+ actual.keys.should include(*expected.keys)
89
+ actual.each { |key, value| value.should == expected[key] }
90
+ end
91
+ end
92
+
93
+ def new_parser(fixture)
94
+ parser = Savon::WSDL::Parser.new
95
+ REXML::Document.parse_stream Fixture[:wsdl, fixture], parser
96
+ parser
97
+ end
98
+
99
+ end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::WSDL::Request do
4
+ let(:http_request) { HTTPI::Request.new :url => Endpoint.wsdl }
5
+ let(:request) { Savon::WSDL::Request.new http_request }
6
+
7
+ describe "#response" do
8
+ it "execute an HTTP GET request and return the HTTPI::Response" do
9
+ response = HTTPI::Response.new 200, {}, Fixture.response(:authentication)
10
+ HTTPI.expects(:get).with(http_request).returns(response)
11
+ request.response.should == response
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,213 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::WSSE do
4
+ let(:wsse) { Savon::WSSE.new }
5
+
6
+ it "should contain the namespace for WS Security Secext" do
7
+ Savon::WSSE::WSENamespace.should ==
8
+ "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
9
+ end
10
+
11
+ it "should contain the namespace for WS Security Utility" do
12
+ Savon::WSSE::WSUNamespace.should ==
13
+ "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
14
+ end
15
+
16
+ it "should contain the namespace for the PasswordText type" do
17
+ Savon::WSSE::PasswordTextURI.should ==
18
+ "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
19
+ end
20
+
21
+ it "should contain the namespace for the PasswordDigest type" do
22
+ Savon::WSSE::PasswordDigestURI.should ==
23
+ "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
24
+ end
25
+
26
+ describe "#credentials" do
27
+ it "should set the username" do
28
+ wsse.credentials "username", "password"
29
+ wsse.username.should == "username"
30
+ end
31
+
32
+ it "should set the password" do
33
+ wsse.credentials "username", "password"
34
+ wsse.password.should == "password"
35
+ end
36
+
37
+ it "should default to set digest to false" do
38
+ wsse.credentials "username", "password"
39
+ wsse.should_not be_digest
40
+ end
41
+
42
+ it "should set digest to true if specified" do
43
+ wsse.credentials "username", "password", :digest
44
+ wsse.should be_digest
45
+ end
46
+ end
47
+
48
+ describe "#username" do
49
+ it "should set the username" do
50
+ wsse.username = "username"
51
+ wsse.username.should == "username"
52
+ end
53
+ end
54
+
55
+ describe "#password" do
56
+ it "should set the password" do
57
+ wsse.password = "password"
58
+ wsse.password.should == "password"
59
+ end
60
+ end
61
+
62
+ describe "#digest" do
63
+ it "should default to false" do
64
+ wsse.should_not be_digest
65
+ end
66
+
67
+ it "should specify whether to use digest auth" do
68
+ wsse.digest = true
69
+ wsse.should be_digest
70
+ end
71
+ end
72
+
73
+ describe "#to_xml" do
74
+ context "with no credentials" do
75
+ it "should return an empty String" do
76
+ wsse.to_xml.should == ""
77
+ end
78
+ end
79
+
80
+ context "with only a username" do
81
+ before { wsse.username = "username" }
82
+
83
+ it "should return an empty String" do
84
+ wsse.to_xml.should == ""
85
+ end
86
+ end
87
+
88
+ context "with only a password" do
89
+ before { wsse.password = "password" }
90
+
91
+ it "should return an empty String" do
92
+ wsse.to_xml.should == ""
93
+ end
94
+ end
95
+
96
+ context "with credentials" do
97
+ before { wsse.credentials "username", "password" }
98
+
99
+ it "should contain a wsse:Security tag" do
100
+ namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
101
+ wsse.to_xml.should include("<wsse:Security xmlns:wsse=\"#{namespace}\">")
102
+ end
103
+
104
+ it "should contain a wsu:Id attribute" do
105
+ wsse.to_xml.should include('<wsse:UsernameToken wsu:Id="UsernameToken-1"')
106
+ end
107
+
108
+ it "should increment the wsu:Id attribute count" do
109
+ wsse.to_xml.should include('<wsse:UsernameToken wsu:Id="UsernameToken-1"')
110
+ wsse.to_xml.should include('<wsse:UsernameToken wsu:Id="UsernameToken-2"')
111
+ end
112
+
113
+ it "should contain the WSE and WSU namespaces" do
114
+ wsse.to_xml.should include(Savon::WSSE::WSENamespace, Savon::WSSE::WSUNamespace)
115
+ end
116
+
117
+ it "should contain the username and password" do
118
+ wsse.to_xml.should include("username", "password")
119
+ end
120
+
121
+ it "should not contain a wsse:Nonce tag" do
122
+ wsse.to_xml.should_not match(/<wsse:Nonce>.*<\/wsse:Nonce>/)
123
+ end
124
+
125
+ it "should not contain a wsu:Created tag" do
126
+ wsse.to_xml.should_not match(/<wsu:Created>.*<\/wsu:Created>/)
127
+ end
128
+
129
+ it "should contain the PasswordText type attribute" do
130
+ wsse.to_xml.should include(Savon::WSSE::PasswordTextURI)
131
+ end
132
+ end
133
+
134
+ context "with credentials and digest auth" do
135
+ before { wsse.credentials "username", "password", :digest }
136
+
137
+ it "should contain the WSE and WSU namespaces" do
138
+ wsse.to_xml.should include(Savon::WSSE::WSENamespace, Savon::WSSE::WSUNamespace)
139
+ end
140
+
141
+ it "should contain the username" do
142
+ wsse.to_xml.should include("username")
143
+ end
144
+
145
+ it "should not contain the (original) password" do
146
+ wsse.to_xml.should_not include("password")
147
+ end
148
+
149
+ it "should contain a wsse:Nonce tag" do
150
+ wsse.to_xml.should match(/<wsse:Nonce>\w+<\/wsse:Nonce>/)
151
+ end
152
+
153
+ it "should contain a wsu:Created tag" do
154
+ wsse.to_xml.should match(/<wsu:Created>#{Savon::SOAP::DateTimeRegexp}.+<\/wsu:Created>/)
155
+ end
156
+
157
+ it "should contain the PasswordDigest type attribute" do
158
+ wsse.to_xml.should include(Savon::WSSE::PasswordDigestURI)
159
+ end
160
+ end
161
+
162
+ context "with #timestamp set to true" do
163
+ before { wsse.timestamp = true }
164
+
165
+ it "should contain a wsse:Timestamp node" do
166
+ wsse.to_xml.should include('<wsse:Timestamp wsu:Id="Timestamp-1" ' +
167
+ 'xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">')
168
+ end
169
+
170
+ it "should contain a wsu:Created node defaulting to Time.now" do
171
+ created_at = Time.now
172
+ Timecop.freeze created_at do
173
+ wsse.to_xml.should include("<wsu:Created>#{created_at.xs_datetime}</wsu:Created>")
174
+ end
175
+ end
176
+
177
+ it "should contain a wsu:Expires node defaulting to Time.now + 60 seconds" do
178
+ created_at = Time.now
179
+ Timecop.freeze created_at do
180
+ wsse.to_xml.should include("<wsu:Expires>#{(created_at + 60).xs_datetime}</wsu:Expires>")
181
+ end
182
+ end
183
+ end
184
+
185
+ context "with #created_at" do
186
+ before { wsse.created_at = Time.now + 86400 }
187
+
188
+ it "should contain a wsu:Created node with the given time" do
189
+ wsse.to_xml.should include("<wsu:Created>#{wsse.created_at.xs_datetime}</wsu:Created>")
190
+ end
191
+
192
+ it "should contain a wsu:Expires node set to #created_at + 60 seconds" do
193
+ wsse.to_xml.should include("<wsu:Expires>#{(wsse.created_at + 60).xs_datetime}</wsu:Expires>")
194
+ end
195
+ end
196
+
197
+ context "with #expires_at" do
198
+ before { wsse.expires_at = Time.now + 86400 }
199
+
200
+ it "should contain a wsu:Created node defaulting to Time.now" do
201
+ created_at = Time.now
202
+ Timecop.freeze created_at do
203
+ wsse.to_xml.should include("<wsu:Created>#{created_at.xs_datetime}</wsu:Created>")
204
+ end
205
+ end
206
+
207
+ it "should contain a wsu:Expires node set to the given time" do
208
+ wsse.to_xml.should include("<wsu:Expires>#{wsse.expires_at.xs_datetime}</wsu:Expires>")
209
+ end
210
+ end
211
+ end
212
+
213
+ end