savon_with_adapter 2.4.1

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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +11 -0
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +1042 -0
  6. data/CONTRIBUTING.md +46 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE +20 -0
  9. data/README.md +81 -0
  10. data/Rakefile +14 -0
  11. data/donate.png +0 -0
  12. data/lib/savon.rb +27 -0
  13. data/lib/savon/block_interface.rb +26 -0
  14. data/lib/savon/builder.rb +166 -0
  15. data/lib/savon/client.rb +89 -0
  16. data/lib/savon/core_ext/string.rb +29 -0
  17. data/lib/savon/header.rb +70 -0
  18. data/lib/savon/http_error.rb +27 -0
  19. data/lib/savon/log_message.rb +48 -0
  20. data/lib/savon/message.rb +35 -0
  21. data/lib/savon/mock.rb +5 -0
  22. data/lib/savon/mock/expectation.rb +71 -0
  23. data/lib/savon/mock/spec_helper.rb +62 -0
  24. data/lib/savon/model.rb +80 -0
  25. data/lib/savon/operation.rb +127 -0
  26. data/lib/savon/options.rb +336 -0
  27. data/lib/savon/qualified_message.rb +49 -0
  28. data/lib/savon/request.rb +89 -0
  29. data/lib/savon/request_logger.rb +48 -0
  30. data/lib/savon/response.rb +112 -0
  31. data/lib/savon/soap_fault.rb +48 -0
  32. data/lib/savon/version.rb +3 -0
  33. data/savon.gemspec +52 -0
  34. data/spec/fixtures/gzip/message.gz +0 -0
  35. data/spec/fixtures/response/another_soap_fault.xml +14 -0
  36. data/spec/fixtures/response/authentication.xml +14 -0
  37. data/spec/fixtures/response/header.xml +13 -0
  38. data/spec/fixtures/response/list.xml +18 -0
  39. data/spec/fixtures/response/multi_ref.xml +39 -0
  40. data/spec/fixtures/response/soap_fault.xml +8 -0
  41. data/spec/fixtures/response/soap_fault12.xml +18 -0
  42. data/spec/fixtures/response/taxcloud.xml +1 -0
  43. data/spec/fixtures/ssl/client_cert.pem +16 -0
  44. data/spec/fixtures/ssl/client_encrypted_key.pem +30 -0
  45. data/spec/fixtures/ssl/client_encrypted_key_cert.pem +24 -0
  46. data/spec/fixtures/ssl/client_key.pem +15 -0
  47. data/spec/fixtures/wsdl/authentication.xml +63 -0
  48. data/spec/fixtures/wsdl/betfair.xml +2981 -0
  49. data/spec/fixtures/wsdl/edialog.xml +15416 -0
  50. data/spec/fixtures/wsdl/interhome.xml +2137 -0
  51. data/spec/fixtures/wsdl/lower_camel.xml +52 -0
  52. data/spec/fixtures/wsdl/multiple_namespaces.xml +92 -0
  53. data/spec/fixtures/wsdl/multiple_types.xml +60 -0
  54. data/spec/fixtures/wsdl/taxcloud.xml +934 -0
  55. data/spec/fixtures/wsdl/team_software.xml +1 -0
  56. data/spec/fixtures/wsdl/vies.xml +176 -0
  57. data/spec/fixtures/wsdl/wasmuth.xml +153 -0
  58. data/spec/integration/centra_spec.rb +72 -0
  59. data/spec/integration/email_example_spec.rb +32 -0
  60. data/spec/integration/random_quote_spec.rb +23 -0
  61. data/spec/integration/ratp_example_spec.rb +28 -0
  62. data/spec/integration/stockquote_example_spec.rb +28 -0
  63. data/spec/integration/support/application.rb +82 -0
  64. data/spec/integration/support/server.rb +84 -0
  65. data/spec/integration/temperature_example_spec.rb +46 -0
  66. data/spec/integration/zipcode_example_spec.rb +42 -0
  67. data/spec/savon/builder_spec.rb +86 -0
  68. data/spec/savon/client_spec.rb +198 -0
  69. data/spec/savon/core_ext/string_spec.rb +37 -0
  70. data/spec/savon/features/message_tag_spec.rb +61 -0
  71. data/spec/savon/http_error_spec.rb +49 -0
  72. data/spec/savon/log_message_spec.rb +33 -0
  73. data/spec/savon/message_spec.rb +40 -0
  74. data/spec/savon/mock_spec.rb +157 -0
  75. data/spec/savon/model_spec.rb +154 -0
  76. data/spec/savon/observers_spec.rb +92 -0
  77. data/spec/savon/operation_spec.rb +211 -0
  78. data/spec/savon/options_spec.rb +772 -0
  79. data/spec/savon/request_spec.rb +493 -0
  80. data/spec/savon/response_spec.rb +258 -0
  81. data/spec/savon/soap_fault_spec.rb +126 -0
  82. data/spec/spec_helper.rb +30 -0
  83. data/spec/support/endpoint.rb +25 -0
  84. data/spec/support/fixture.rb +39 -0
  85. data/spec/support/integration.rb +9 -0
  86. data/spec/support/stdout.rb +25 -0
  87. metadata +310 -0
@@ -0,0 +1,258 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Response do
4
+
5
+ let(:globals) { Savon::GlobalOptions.new }
6
+ let(:locals) { Savon::LocalOptions.new }
7
+
8
+ describe ".new" do
9
+ it "should raise a Savon::Fault in case of a SOAP fault" do
10
+ lambda { soap_fault_response }.should raise_error(Savon::SOAPFault)
11
+ end
12
+
13
+ it "should not raise a Savon::Fault in case the default is turned off" do
14
+ globals[:raise_errors] = false
15
+ lambda { soap_fault_response }.should_not raise_error
16
+ end
17
+
18
+ it "should raise a Savon::HTTP::Error in case of an HTTP error" do
19
+ lambda { soap_response :code => 500 }.should raise_error(Savon::HTTPError)
20
+ end
21
+
22
+ it "should not raise a Savon::HTTP::Error in case the default is turned off" do
23
+ globals[:raise_errors] = false
24
+ soap_response :code => 500
25
+ end
26
+ end
27
+
28
+ describe "#success?" do
29
+ before { globals[:raise_errors] = false }
30
+
31
+ it "should return true if the request was successful" do
32
+ soap_response.should be_a_success
33
+ end
34
+
35
+ it "should return false if there was a SOAP fault" do
36
+ soap_fault_response.should_not be_a_success
37
+ end
38
+
39
+ it "should return false if there was an HTTP error" do
40
+ http_error_response.should_not be_a_success
41
+ end
42
+ end
43
+
44
+ describe "#soap_fault?" do
45
+ before { globals[:raise_errors] = false }
46
+
47
+ it "should not return true in case the response seems to be ok" do
48
+ soap_response.soap_fault?.should be_false
49
+ end
50
+
51
+ it "should return true in case of a SOAP fault" do
52
+ soap_fault_response.soap_fault?.should be_true
53
+ end
54
+ end
55
+
56
+ describe "#soap_fault" do
57
+ before { globals[:raise_errors] = false }
58
+
59
+ it "should return nil in case the response seems to be ok" do
60
+ soap_response.soap_fault.should be_nil
61
+ end
62
+
63
+ it "should return a SOAPFault in case of a SOAP fault" do
64
+ soap_fault_response.soap_fault.should be_a(Savon::SOAPFault)
65
+ end
66
+ end
67
+
68
+ describe "#http_error?" do
69
+ before { globals[:raise_errors] = false }
70
+
71
+ it "should not return true in case the response seems to be ok" do
72
+ soap_response.http_error?.should_not be_true
73
+ end
74
+
75
+ it "should return true in case of an HTTP error" do
76
+ soap_response(:code => 500).http_error?.should be_true
77
+ end
78
+ end
79
+
80
+ describe "#http_error" do
81
+ before { globals[:raise_errors] = false }
82
+
83
+ it "should return nil in case the response seems to be ok" do
84
+ soap_response.http_error.should be_nil
85
+ end
86
+
87
+ it "should return a HTTPError in case of an HTTP error" do
88
+ soap_response(:code => 500).http_error.should be_a(Savon::HTTPError)
89
+ end
90
+ end
91
+
92
+ describe "#header" do
93
+ it "should return the SOAP response header as a Hash" do
94
+ response = soap_response :body => Fixture.response(:header)
95
+ response.header.should include(:session_number => "ABCD1234")
96
+ end
97
+
98
+ it 'respects the global :strip_namespaces option' do
99
+ globals[:strip_namespaces] = false
100
+
101
+ response_with_header = soap_response(:body => Fixture.response(:header))
102
+ header = response_with_header.header
103
+
104
+ expect(header).to be_a(Hash)
105
+
106
+ # notice: :session_number is a snake_case Symbol without namespaces,
107
+ # but the Envelope and Header elements are qualified.
108
+ expect(header.keys).to include(:session_number)
109
+ end
110
+
111
+ it 'respects the global :convert_response_tags_to option' do
112
+ globals[:convert_response_tags_to] = lambda { |key| key.upcase }
113
+
114
+ response_with_header = soap_response(:body => Fixture.response(:header))
115
+ header = response_with_header.header
116
+
117
+ expect(header).to be_a(Hash)
118
+ expect(header.keys).to include('SESSIONNUMBER')
119
+ end
120
+
121
+ it "should throw an exception when the response header isn't parsable" do
122
+ lambda { invalid_soap_response.header }.should raise_error Savon::InvalidResponseError
123
+ end
124
+ end
125
+
126
+ %w(body to_hash).each do |method|
127
+ describe "##{method}" do
128
+ it "should return the SOAP response body as a Hash" do
129
+ soap_response.send(method)[:authenticate_response][:return].should ==
130
+ Fixture.response_hash(:authentication)[:authenticate_response][:return]
131
+ end
132
+
133
+ it "should return a Hash for a SOAP multiRef response" do
134
+ hash = soap_response(:body => Fixture.response(:multi_ref)).send(method)
135
+
136
+ hash[:list_response].should be_a(Hash)
137
+ hash[:multi_ref].should be_an(Array)
138
+ end
139
+
140
+ it "should add existing namespaced elements as an array" do
141
+ hash = soap_response(:body => Fixture.response(:list)).send(method)
142
+
143
+ hash[:multi_namespaced_entry_response][:history].should be_a(Hash)
144
+ hash[:multi_namespaced_entry_response][:history][:case].should be_an(Array)
145
+ end
146
+
147
+ it 'respects the global :strip_namespaces option' do
148
+ globals[:strip_namespaces] = false
149
+
150
+ body = soap_response.body
151
+
152
+ expect(body).to be_a(Hash)
153
+ expect(body.keys).to include(:"ns2:authenticate_response")
154
+ end
155
+
156
+ it 'respects the global :convert_response_tags_to option' do
157
+ globals[:convert_response_tags_to] = lambda { |key| key.upcase }
158
+
159
+ body = soap_response.body
160
+
161
+ expect(body).to be_a(Hash)
162
+ expect(body.keys).to include('AUTHENTICATERESPONSE')
163
+ end
164
+ end
165
+ end
166
+
167
+ describe "#to_array" do
168
+ context "when the given path exists" do
169
+ it "should return an Array containing the path value" do
170
+ soap_response.to_array(:authenticate_response, :return).should ==
171
+ [Fixture.response_hash(:authentication)[:authenticate_response][:return]]
172
+ end
173
+
174
+ it "should properly return FalseClass values [#327]" do
175
+ body = Gyoku.xml(:envelope => { :body => { :return => { :success => false } } })
176
+ soap_response(:body => body).to_array(:return, :success).should == [false]
177
+ end
178
+ end
179
+
180
+ context "when the given path returns nil" do
181
+ it "should return an empty Array" do
182
+ soap_response.to_array(:authenticate_response, :undefined).should == []
183
+ end
184
+ end
185
+
186
+ context "when the given path does not exist at all" do
187
+ it "should return an empty Array" do
188
+ soap_response.to_array(:authenticate_response, :some, :undefined, :path).should == []
189
+ end
190
+ end
191
+ end
192
+
193
+ describe "#hash" do
194
+ it "should return the complete SOAP response XML as a Hash" do
195
+ response = soap_response :body => Fixture.response(:header)
196
+ response.hash[:envelope][:header][:session_number].should == "ABCD1234"
197
+ end
198
+ end
199
+
200
+ describe "#to_xml" do
201
+ it "should return the raw SOAP response body" do
202
+ soap_response.to_xml.should == Fixture.response(:authentication)
203
+ end
204
+ end
205
+
206
+ describe "#doc" do
207
+ it "returns a Nokogiri::XML::Document for the SOAP response XML" do
208
+ soap_response.doc.should be_a(Nokogiri::XML::Document)
209
+ end
210
+ end
211
+
212
+ describe "#xpath" do
213
+ it "permits XPath access to elements in the request" do
214
+ soap_response.xpath("//client").first.inner_text.should == "radclient"
215
+ soap_response.xpath("//ns2:authenticateResponse/return/success").first.inner_text.should == "true"
216
+ end
217
+ end
218
+
219
+ describe '#find' do
220
+ it 'delegates to Nori#find to find child elements inside the Envelope' do
221
+ result = soap_response.find('Body', 'authenticateResponse', 'return')
222
+
223
+ expect(result).to be_a(Hash)
224
+ expect(result.keys).to include(:authentication_value)
225
+ end
226
+ end
227
+
228
+ describe "#http" do
229
+ it "should return the HTTPI::Response" do
230
+ soap_response.http.should be_an(HTTPI::Response)
231
+ end
232
+ end
233
+
234
+ def soap_response(options = {})
235
+ defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
236
+ response = defaults.merge options
237
+ http_response = HTTPI::Response.new(response[:code], response[:headers], response[:body])
238
+
239
+ Savon::Response.new(http_response, globals, locals)
240
+ end
241
+
242
+ def soap_fault_response
243
+ soap_response :code => 500, :body => Fixture.response(:soap_fault)
244
+ end
245
+
246
+ def http_error_response
247
+ soap_response :code => 404, :body => "Not found"
248
+ end
249
+
250
+ def invalid_soap_response(options = {})
251
+ defaults = { :code => 200, :headers => {}, :body => "I'm not SOAP" }
252
+ response = defaults.merge options
253
+ http_response = HTTPI::Response.new(response[:code], response[:headers], response[:body])
254
+
255
+ Savon::Response.new(http_response, globals, locals)
256
+ end
257
+
258
+ end
@@ -0,0 +1,126 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::SOAPFault do
4
+ let(:soap_fault) { Savon::SOAPFault.new new_response(:body => Fixture.response(:soap_fault)), nori }
5
+ let(:soap_fault2) { Savon::SOAPFault.new new_response(:body => Fixture.response(:soap_fault12)), nori }
6
+ let(:soap_fault_nc) { Savon::SOAPFault.new new_response(:body => Fixture.response(:soap_fault)), nori_no_convert }
7
+ let(:soap_fault_nc2) { Savon::SOAPFault.new new_response(:body => Fixture.response(:soap_fault12)), nori_no_convert }
8
+ let(:another_soap_fault) { Savon::SOAPFault.new new_response(:body => Fixture.response(:another_soap_fault)), nori }
9
+ let(:no_fault) { Savon::SOAPFault.new new_response, nori }
10
+
11
+ let(:nori) { Nori.new(:strip_namespaces => true, :convert_tags_to => lambda { |tag| tag.snakecase.to_sym }) }
12
+ let(:nori_no_convert) { Nori.new(:strip_namespaces => true, :convert_tags_to => nil) }
13
+
14
+ it "inherits from Savon::Error" do
15
+ expect(Savon::SOAPFault.ancestors).to include(Savon::Error)
16
+ end
17
+
18
+ describe "#http" do
19
+ it "returns the HTTPI::Response" do
20
+ expect(soap_fault.http).to be_an(HTTPI::Response)
21
+ end
22
+ end
23
+
24
+ describe ".present?" do
25
+ it "returns true if the HTTP response contains a SOAP 1.1 fault" do
26
+ http = new_response(:body => Fixture.response(:soap_fault))
27
+ expect(Savon::SOAPFault.present? http).to be_true
28
+ end
29
+
30
+ it "returns true if the HTTP response contains a SOAP 1.2 fault" do
31
+ http = new_response(:body => Fixture.response(:soap_fault12))
32
+ expect(Savon::SOAPFault.present? http).to be_true
33
+ end
34
+
35
+ it "returns true if the HTTP response contains a SOAP fault with different namespaces" do
36
+ http = new_response(:body => Fixture.response(:another_soap_fault))
37
+ expect(Savon::SOAPFault.present? http).to be_true
38
+ end
39
+
40
+ it "returns false unless the HTTP response contains a SOAP fault" do
41
+ expect(Savon::SOAPFault.present? new_response).to be_false
42
+ end
43
+ end
44
+
45
+ [:message, :to_s].each do |method|
46
+ describe "##{method}" do
47
+ it "returns a SOAP 1.1 fault message" do
48
+ expect(soap_fault.send method).to eq("(soap:Server) Fault occurred while processing.")
49
+ end
50
+
51
+ it "returns a SOAP 1.2 fault message" do
52
+ expect(soap_fault2.send method).to eq("(soap:Sender) Sender Timeout")
53
+ end
54
+
55
+ it "returns a SOAP fault message (with different namespaces)" do
56
+ expect(another_soap_fault.send method).to eq("(ERR_NO_SESSION) Wrong session message")
57
+ end
58
+
59
+ it "works even if the keys are different in a SOAP 1.1 fault message" do
60
+ expect(soap_fault_nc.send method).to eq("(soap:Server) Fault occurred while processing.")
61
+ end
62
+
63
+ it "works even if the keys are different in a SOAP 1.2 fault message" do
64
+ expect(soap_fault_nc2.send method).to eq("(soap:Sender) Sender Timeout")
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#to_hash" do
70
+ it "returns the SOAP response as a Hash unless a SOAP fault is present" do
71
+ expect(no_fault.to_hash[:authenticate_response][:return][:success]).to be_true
72
+ end
73
+
74
+ it "returns a SOAP 1.1 fault as a Hash" do
75
+ expected = {
76
+ :fault => {
77
+ :faultstring => "Fault occurred while processing.",
78
+ :faultcode => "soap:Server"
79
+ }
80
+ }
81
+
82
+ expect(soap_fault.to_hash).to eq(expected)
83
+ end
84
+
85
+ it "returns a SOAP 1.2 fault as a Hash" do
86
+ expected = {
87
+ :fault => {
88
+ :detail => { :max_time => "P5M" },
89
+ :reason => { :text => "Sender Timeout" },
90
+ :code => { :value => "soap:Sender", :subcode => { :value => "m:MessageTimeout" } }
91
+ }
92
+ }
93
+
94
+ expect(soap_fault2.to_hash).to eq(expected)
95
+ end
96
+
97
+ it "works even if the keys are different" do
98
+ expected = {
99
+ "Fault" => {
100
+ "Code" => {
101
+ "Value" => "soap:Sender",
102
+ "Subcode"=> {
103
+ "Value" => "m:MessageTimeout"
104
+ }
105
+ },
106
+ "Reason" => {
107
+ "Text" => "Sender Timeout"
108
+ },
109
+ "Detail" => {
110
+ "MaxTime" => "P5M"
111
+ }
112
+ }
113
+ }
114
+
115
+ expect(soap_fault_nc2.to_hash).to eq(expected)
116
+ end
117
+ end
118
+
119
+ def new_response(options = {})
120
+ defaults = { :code => 500, :headers => {}, :body => Fixture.response(:authentication) }
121
+ response = defaults.merge options
122
+
123
+ HTTPI::Response.new response[:code], response[:headers], response[:body]
124
+ end
125
+
126
+ end
@@ -0,0 +1,30 @@
1
+ require "bundler"
2
+ Bundler.setup(:default, :development)
3
+
4
+ unless RUBY_PLATFORM =~ /java/
5
+ require "simplecov"
6
+ require "coveralls"
7
+
8
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
9
+ SimpleCov.start do
10
+ add_filter "spec"
11
+ end
12
+ end
13
+
14
+ require "savon"
15
+ require "rspec"
16
+
17
+ # don't have HTTPI lazy-load HTTPClient, because then
18
+ # it can't actually be refered to inside the specs.
19
+ require "httpclient"
20
+
21
+ support_files = File.expand_path("spec/support/**/*.rb")
22
+ Dir[support_files].each { |file| require file }
23
+
24
+ RSpec.configure do |config|
25
+ config.include SpecSupport
26
+ config.mock_with :mocha
27
+ config.order = "random"
28
+ end
29
+
30
+ HTTPI.log = false
@@ -0,0 +1,25 @@
1
+ class Endpoint
2
+ class << self
3
+
4
+ # Returns the WSDL endpoint for a given +type+ of request.
5
+ def wsdl(type = nil)
6
+ case type
7
+ when :no_namespace then "http://nons.example.com/Service?wsdl"
8
+ when :namespaced_actions then "http://nsactions.example.com/Service?wsdl"
9
+ when :geotrust then "https://test-api.geotrust.com/webtrust/query.jws?WSDL"
10
+ else soap(type)
11
+ end
12
+ end
13
+
14
+ # Returns the SOAP endpoint for a given +type+ of request.
15
+ def soap(type = nil)
16
+ case type
17
+ when :soap_fault then "http://soapfault.example.com/Service?wsdl"
18
+ when :http_error then "http://httperror.example.com/Service?wsdl"
19
+ when :invalid then "http://invalid.example.com/Service?wsdl"
20
+ else "http://example.com/validation/1.0/AuthenticationService"
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ class Fixture
2
+
3
+ TYPES = { :gzip => "gz", :response => "xml", :wsdl => "xml" }
4
+
5
+ class << self
6
+
7
+ def [](type, fixture)
8
+ fixtures(type)[fixture] ||= read_file type, fixture
9
+ end
10
+
11
+ def response_hash(fixture)
12
+ @response_hash ||= {}
13
+ @response_hash[fixture] ||= nori.parse(response(fixture))[:envelope][:body]
14
+ end
15
+
16
+ TYPES.each do |type, ext|
17
+ define_method(type) { |fixture| self[type, fixture] }
18
+ end
19
+
20
+ private
21
+
22
+ def nori
23
+ Nori.new(:strip_namespaces => true, :convert_tags_to => lambda { |tag| tag.snakecase.to_sym })
24
+ end
25
+
26
+ def fixtures(type)
27
+ @fixtures ||= {}
28
+ @fixtures[type] ||= {}
29
+ end
30
+
31
+ def read_file(type, fixture)
32
+ path = File.expand_path "../../fixtures/#{type}/#{fixture}.#{TYPES[type]}", __FILE__
33
+ raise ArgumentError, "Unable to load: #{path}" unless File.exist? path
34
+
35
+ File.read path
36
+ end
37
+
38
+ end
39
+ end