regenersis-savon 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +639 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +42 -0
  8. data/Rakefile +7 -0
  9. data/lib/regenersis-savon.rb +1 -0
  10. data/lib/savon.rb +15 -0
  11. data/lib/savon/client.rb +168 -0
  12. data/lib/savon/core_ext/object.rb +14 -0
  13. data/lib/savon/core_ext/string.rb +23 -0
  14. data/lib/savon/error.rb +6 -0
  15. data/lib/savon/global.rb +115 -0
  16. data/lib/savon/hooks/group.rb +46 -0
  17. data/lib/savon/hooks/hook.rb +36 -0
  18. data/lib/savon/http/error.rb +42 -0
  19. data/lib/savon/model.rb +103 -0
  20. data/lib/savon/soap.rb +21 -0
  21. data/lib/savon/soap/fault.rb +59 -0
  22. data/lib/savon/soap/request.rb +71 -0
  23. data/lib/savon/soap/response.rb +109 -0
  24. data/lib/savon/soap/xml.rb +227 -0
  25. data/lib/savon/version.rb +5 -0
  26. data/lib/savon/wasabi/document.rb +41 -0
  27. data/regenersis-savon.gemspec +35 -0
  28. data/spec/fixtures/gzip/message.gz +0 -0
  29. data/spec/fixtures/response/another_soap_fault.xml +14 -0
  30. data/spec/fixtures/response/authentication.xml +14 -0
  31. data/spec/fixtures/response/header.xml +13 -0
  32. data/spec/fixtures/response/list.xml +18 -0
  33. data/spec/fixtures/response/multi_ref.xml +39 -0
  34. data/spec/fixtures/response/soap_fault.xml +8 -0
  35. data/spec/fixtures/response/soap_fault12.xml +18 -0
  36. data/spec/fixtures/response/taxcloud.xml +1 -0
  37. data/spec/fixtures/wsdl/authentication.xml +63 -0
  38. data/spec/fixtures/wsdl/lower_camel.xml +52 -0
  39. data/spec/fixtures/wsdl/multiple_namespaces.xml +61 -0
  40. data/spec/fixtures/wsdl/multiple_types.xml +60 -0
  41. data/spec/fixtures/wsdl/taxcloud.xml +934 -0
  42. data/spec/savon/client_spec.rb +461 -0
  43. data/spec/savon/core_ext/object_spec.rb +19 -0
  44. data/spec/savon/core_ext/string_spec.rb +37 -0
  45. data/spec/savon/http/error_spec.rb +52 -0
  46. data/spec/savon/model_spec.rb +194 -0
  47. data/spec/savon/savon_spec.rb +85 -0
  48. data/spec/savon/soap/fault_spec.rb +89 -0
  49. data/spec/savon/soap/request_spec.rb +57 -0
  50. data/spec/savon/soap/response_spec.rb +224 -0
  51. data/spec/savon/soap/xml_spec.rb +309 -0
  52. data/spec/savon/soap_spec.rb +16 -0
  53. data/spec/savon/wasabi/document_spec.rb +45 -0
  54. data/spec/spec_helper.rb +15 -0
  55. data/spec/support/endpoint.rb +25 -0
  56. data/spec/support/fixture.rb +35 -0
  57. metadata +323 -0
@@ -0,0 +1,461 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Client do
4
+ let(:client) { Savon::Client.new { wsdl.document = Endpoint.wsdl } }
5
+
6
+ describe ".new" do
7
+ context "with a String" do
8
+ it "should set the WSDL document" do
9
+ wsdl = "http://example.com/UserService?wsdl"
10
+ client = Savon::Client.new(wsdl)
11
+ client.wsdl.instance_variable_get("@document").should == wsdl
12
+ end
13
+ end
14
+
15
+ context "with a block expecting one argument" do
16
+ it "should yield the WSDL object" do
17
+ Savon::Client.new { |wsdl| wsdl.should be_a(Savon::Wasabi::Document) }
18
+ end
19
+ end
20
+
21
+ context "with a block expecting two arguments" do
22
+ it "should yield the WSDL and HTTP objects" do
23
+ Savon::Client.new do |wsdl, http|
24
+ wsdl.should be_an(Savon::Wasabi::Document)
25
+ http.should be_an(HTTPI::Request)
26
+ end
27
+ end
28
+ end
29
+
30
+ context "with a block expecting three arguments" do
31
+ it "should yield the WSDL, HTTP and WSSE objects" do
32
+ Savon::Client.new do |wsdl, http, wsse|
33
+ wsdl.should be_an(Savon::Wasabi::Document)
34
+ http.should be_an(HTTPI::Request)
35
+ wsse.should be_an(Akami::WSSE)
36
+ end
37
+ end
38
+ end
39
+
40
+ context "with a block expecting no arguments" do
41
+ it "should let you access the WSDL object" do
42
+ Savon::Client.new { wsdl.should be_a(Savon::Wasabi::Document) }
43
+ end
44
+
45
+ it "should let you access the HTTP object" do
46
+ Savon::Client.new { http.should be_an(HTTPI::Request) }
47
+ end
48
+
49
+ it "should let you access the WSSE object" do
50
+ Savon::Client.new { wsse.should be_a(Akami::WSSE) }
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "#wsdl" do
56
+ it "should return the Savon::Wasabi::Document" do
57
+ client.wsdl.should be_a(Savon::Wasabi::Document)
58
+ end
59
+ end
60
+
61
+ describe "#http" do
62
+ it "should return the HTTPI::Request" do
63
+ client.http.should be_an(HTTPI::Request)
64
+ end
65
+ end
66
+
67
+ describe "#wsse" do
68
+ it "should return the Akami::WSSE object" do
69
+ client.wsse.should be_a(Akami::WSSE)
70
+ end
71
+ end
72
+
73
+ describe "#request" do
74
+ before do
75
+ HTTPI.stubs(:get).returns(new_response(:body => Fixture.wsdl(:authentication)))
76
+ HTTPI.stubs(:post).returns(new_response)
77
+ end
78
+
79
+ context "without any arguments" do
80
+ it "should raise an ArgumentError" do
81
+ lambda { client.request }.should raise_error(ArgumentError)
82
+ end
83
+ end
84
+
85
+ context "with a single argument (Symbol)" do
86
+ it "should set the input tag to result in <getUser>" do
87
+ client.request(:get_user) { soap.input.should == [nil, :getUser, {}] }
88
+ end
89
+
90
+ it "should set the target namespace with the default identifier" do
91
+ namespace = 'xmlns:wsdl="http://v1_0.ws.auth.order.example.com/"'
92
+ HTTPI::Request.any_instance.expects(:body=).with { |value| value.include? namespace }
93
+
94
+ client.request :get_user
95
+ end
96
+
97
+ it "should not set the target namespace if soap.namespace was set to nil" do
98
+ namespace = 'wsdl="http://v1_0.ws.auth.order.example.com/"'
99
+ HTTPI::Request.any_instance.expects(:body=).with { |value| !value.include?(namespace) }
100
+
101
+ client.request(:get_user) { soap.namespace = nil }
102
+ end
103
+ end
104
+
105
+ context "with a single argument (String)" do
106
+ it "should set the input tag to result in <get_user>" do
107
+ client.request("get_user") { soap.input.should == [nil, :get_user, {}] }
108
+ end
109
+ end
110
+
111
+ context "with a Symbol and a Hash" do
112
+ it "should set the input tag to result in <getUser active='true'>" do
113
+ client.request(:get_user, :active => true) { soap.input.should == [nil, :getUser, { :active => true }] }
114
+ end
115
+ end
116
+
117
+ context "with two Symbols" do
118
+ it "should set the input tag to result in <wsdl:getUser>" do
119
+ client.request(:v1, :get_user) { soap.input.should == [:v1, :getUser, {}] }
120
+ end
121
+
122
+ it "should set the target namespace with the given identifier" do
123
+ namespace = 'xmlns:v1="http://v1_0.ws.auth.order.example.com/"'
124
+ HTTPI::Request.any_instance.expects(:body=).with { |value| value.include? namespace }
125
+
126
+ client.request :v1, :get_user
127
+ end
128
+
129
+ it "should not set the target namespace if soap.namespace was set to nil" do
130
+ namespace = 'xmlns:v1="http://v1_0.ws.auth.order.example.com/"'
131
+ HTTPI::Request.any_instance.expects(:body=).with { |value| !value.include?(namespace) }
132
+
133
+ client.request(:v1, :get_user) { soap.namespace = nil }
134
+ end
135
+ end
136
+
137
+ context "with two Symbols and a Hash" do
138
+ it "should set the input tag to result in <wsdl:getUser active='true'>" do
139
+ client.request(:wsdl, :get_user, :active => true) { soap.input.should == [:wsdl, :getUser, { :active => true }] }
140
+ end
141
+ end
142
+
143
+ context "with a block expecting one argument" do
144
+ it "should yield the SOAP object" do
145
+ client.request(:authenticate) { |soap| soap.should be_a(Savon::SOAP::XML) }
146
+ end
147
+ end
148
+
149
+ context "with a block expecting two arguments" do
150
+ it "should yield the SOAP and WSDL objects" do
151
+ client.request(:authenticate) do |soap, wsdl|
152
+ soap.should be_a(Savon::SOAP::XML)
153
+ wsdl.should be_an(Savon::Wasabi::Document)
154
+ end
155
+ end
156
+ end
157
+
158
+ context "with a block expecting three arguments" do
159
+ it "should yield the SOAP, WSDL and HTTP objects" do
160
+ client.request(:authenticate) do |soap, wsdl, http|
161
+ soap.should be_a(Savon::SOAP::XML)
162
+ wsdl.should be_an(Savon::Wasabi::Document)
163
+ http.should be_an(HTTPI::Request)
164
+ end
165
+ end
166
+ end
167
+
168
+ context "with a block expecting four arguments" do
169
+ it "should yield the SOAP, WSDL, HTTP and WSSE objects" do
170
+ client.request(:authenticate) do |soap, wsdl, http, wsse|
171
+ soap.should be_a(Savon::SOAP::XML)
172
+ wsdl.should be_a(Savon::Wasabi::Document)
173
+ http.should be_an(HTTPI::Request)
174
+ wsse.should be_a(Akami::WSSE)
175
+ end
176
+ end
177
+ end
178
+
179
+ context "with a block expecting no arguments" do
180
+ it "should let you access the SOAP object" do
181
+ client.request(:authenticate) { soap.should be_a(Savon::SOAP::XML) }
182
+ end
183
+
184
+ it "should let you access the HTTP object" do
185
+ client.request(:authenticate) { http.should be_an(HTTPI::Request) }
186
+ end
187
+
188
+ it "should let you access the WSSE object" do
189
+ client.request(:authenticate) { wsse.should be_a(Akami::WSSE) }
190
+ end
191
+
192
+ it "should let you access the WSDL object" do
193
+ client.request(:authenticate) { wsdl.should be_a(Savon::Wasabi::Document) }
194
+ end
195
+ end
196
+
197
+ it "should not set the Cookie header for the next request" do
198
+ client.http.headers.expects(:[]=).with("Cookie", anything).never
199
+ client.http.headers.stubs(:[]=).with("SOAPAction", '"authenticate"')
200
+ client.http.headers.stubs(:[]=).with("Content-Type", "text/xml;charset=UTF-8")
201
+ client.http.headers.stubs(:[]=).with("Content-Length", "384")
202
+
203
+ client.request :authenticate
204
+ end
205
+ end
206
+
207
+ context "#request with a Set-Cookie response header" do
208
+ before do
209
+ HTTPI.stubs(:get).returns(new_response(:body => Fixture.wsdl(:authentication)))
210
+ HTTPI.stubs(:post).returns(new_response(:headers => { "Set-Cookie" => "some-cookie" }))
211
+ end
212
+
213
+ it "should set the Cookie header for the next request" do
214
+ client.http.headers.expects(:[]=).with("Cookie", "some-cookie")
215
+ client.http.headers.stubs(:[]=).with("SOAPAction", '"authenticate"')
216
+ client.http.headers.stubs(:[]=).with("Content-Type", "text/xml;charset=UTF-8")
217
+ client.http.headers.stubs(:[]=).with("Content-Length", "384")
218
+
219
+ client.request :authenticate
220
+ end
221
+ end
222
+
223
+ context "with a remote WSDL document" do
224
+ let(:client) { Savon::Client.new { wsdl.document = Endpoint.wsdl } }
225
+ before { HTTPI.expects(:get).returns(new_response(:body => Fixture.wsdl(:authentication))) }
226
+
227
+ it "adds a SOAPAction header containing the SOAP action name" do
228
+ HTTPI.stubs(:post).returns(new_response)
229
+
230
+ client.request :authenticate do
231
+ http.headers["SOAPAction"].should == %{"authenticate"}
232
+ end
233
+ end
234
+
235
+ it "should execute SOAP requests and return the response" do
236
+ HTTPI.expects(:post).returns(new_response)
237
+ response = client.request(:authenticate)
238
+
239
+ response.should be_a(Savon::SOAP::Response)
240
+ response.to_xml.should == Fixture.response(:authentication)
241
+ end
242
+ end
243
+
244
+ context "with a local WSDL document" do
245
+ let(:client) { Savon::Client.new { wsdl.document = "spec/fixtures/wsdl/authentication.xml" } }
246
+
247
+ before { HTTPI.expects(:get).never }
248
+
249
+ it "adds a SOAPAction header containing the SOAP action name" do
250
+ HTTPI.stubs(:post).returns(new_response)
251
+
252
+ client.request :authenticate do
253
+ http.headers["SOAPAction"].should == %{"authenticate"}
254
+ end
255
+ end
256
+
257
+ it "should execute SOAP requests and return the response" do
258
+ HTTPI.expects(:post).returns(new_response)
259
+ response = client.request(:authenticate)
260
+
261
+ response.should be_a(Savon::SOAP::Response)
262
+ response.to_xml.should == Fixture.response(:authentication)
263
+ end
264
+ end
265
+
266
+ context "when the WSDL specifies multiple namespaces" do
267
+ before do
268
+ HTTPI.stubs(:get).returns(new_response(:body => Fixture.wsdl(:multiple_namespaces)))
269
+ HTTPI.stubs(:post).returns(new_response)
270
+ end
271
+
272
+ it "qualifies each element with the appropriate namespace" do
273
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
274
+ xml = Nokogiri::XML(value)
275
+
276
+ title = xml.at_xpath(
277
+ ".//actions:Save/actions:article/article:Title/text()",
278
+ "article" => "http://example.com/article",
279
+ "actions" => "http://example.com/actions").to_s
280
+ author = xml.at_xpath(
281
+ ".//actions:Save/actions:article/article:Author/text()",
282
+ "article" => "http://example.com/article",
283
+ "actions" => "http://example.com/actions").to_s
284
+
285
+ title == "Hamlet" && author == "Shakespeare"
286
+ end
287
+
288
+ client.request :save do
289
+ soap.body = { :article => { "Title" => "Hamlet", "Author" => "Shakespeare" } }
290
+ end
291
+ end
292
+
293
+ it "still sends nil as xsi:nil as in the non-namespaced case" do
294
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
295
+ xml = Nokogiri::XML(value)
296
+
297
+ attribute = xml.at_xpath(".//article:Title/@xsi:nil",
298
+ "xsi" => "http://www.w3.org/2001/XMLSchema-instance",
299
+ "article" => "http://example.com/article").to_s
300
+
301
+ attribute == "true"
302
+ end
303
+
304
+ client.request(:save) { soap.body = { :article => { "Title" => nil } } }
305
+ end
306
+
307
+ it "translates between symbol :save and string 'Save'" do
308
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
309
+ xml = Nokogiri::XML(value)
310
+ !!xml.at_xpath(".//actions:Save", "actions" => "http://example.com/actions")
311
+ end
312
+
313
+ client.request :save do
314
+ soap.body = { :article => { :title => "Hamlet", :author => "Shakespeare" } }
315
+ end
316
+ end
317
+
318
+ it "qualifies Save with the appropriate namespace" do
319
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
320
+ xml = Nokogiri::XML(value)
321
+ !!xml.at_xpath(".//actions:Save", "actions" => "http://example.com/actions")
322
+ end
323
+
324
+ client.request "Save" do
325
+ soap.body = { :article => { :title => "Hamlet", :author => "Shakespeare" } }
326
+ end
327
+ end
328
+ end
329
+
330
+ context "when the WSDL has a lowerCamel name" do
331
+ before do
332
+ HTTPI.stubs(:get).returns(new_response(:body => Fixture.wsdl(:lower_camel)))
333
+ HTTPI.stubs(:post).returns(new_response)
334
+ end
335
+
336
+ it "appends namespace when name is specified explicitly" do
337
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
338
+ xml = Nokogiri::XML(value)
339
+ !!xml.at_xpath(".//actions:Save/actions:lowerCamel", "actions" => "http://example.com/actions")
340
+ end
341
+
342
+ client.request("Save") { soap.body = { 'lowerCamel' => 'theValue' } }
343
+ end
344
+
345
+ it "still appends namespace when converting from symbol" do
346
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
347
+ xml = Nokogiri::XML(value)
348
+ !!xml.at_xpath(".//actions:Save/actions:lowerCamel", "actions" => "http://example.com/actions")
349
+ end
350
+
351
+ client.request("Save") { soap.body = { :lower_camel => 'theValue' } }
352
+ end
353
+ end
354
+
355
+ context "with multiple types" do
356
+ before do
357
+ HTTPI.stubs(:get).returns(new_response(:body => Fixture.wsdl(:multiple_types)))
358
+ HTTPI.stubs(:post).returns(new_response)
359
+ end
360
+
361
+ it "does not blow up" do
362
+ HTTPI::Request.any_instance.expects(:body=).with { |value| value.include?("Save") }
363
+ client.request(:save) { soap.body = {} }
364
+ end
365
+ end
366
+
367
+ context "with an Array of namespaced items" do
368
+ let(:client) { Savon::Client.new { wsdl.document = "spec/fixtures/wsdl/taxcloud.xml" } }
369
+
370
+ before do
371
+ HTTPI.stubs(:get).returns(new_response(:body => Fixture.wsdl(:taxcloud)))
372
+ HTTPI.stubs(:post).returns(new_response)
373
+ end
374
+
375
+ it "should namespaces each Array item as expected" do
376
+ HTTPI::Request.any_instance.expects(:body=).with do |value|
377
+ value.include?("<ins0:cartItems><ins0:CartItem>") && value.include?("<wsdl:ItemID>SKU-TEST</wsdl:ItemID>")
378
+ end
379
+
380
+ address = { "Address1" => "888 6th Ave", "Address2" => nil, "City" => "New York", "State" => "NY", "Zip5" => "10001", "Zip4" => nil }
381
+ cart_item = { "Index" => 0, "ItemID" => "SKU-TEST", "TIC" => "00000", "Price" => 50.0, "Qty" => 1 }
382
+
383
+ client.request :lookup, :body => {
384
+ "customerID" => 123,
385
+ "cartID" => 456,
386
+ "cartItems" => { "CartItem" => [cart_item] },
387
+ "origin" => address,
388
+ "destination" => address
389
+ }
390
+ end
391
+ end
392
+
393
+ context "without a WSDL document" do
394
+ let(:client) do
395
+ Savon::Client.new do
396
+ wsdl.endpoint = Endpoint.soap
397
+ wsdl.namespace = "http://v1_0.ws.auth.order.example.com/"
398
+ end
399
+ end
400
+
401
+ before { HTTPI.expects(:get).never }
402
+
403
+ it "raise an ArgumentError when trying to access the WSDL" do
404
+ lambda { client.wsdl.soap_actions }.should raise_error(ArgumentError, /Wasabi/)
405
+ end
406
+
407
+ it "adds a SOAPAction header containing the SOAP action name" do
408
+ HTTPI.stubs(:post).returns(new_response)
409
+
410
+ client.request :authenticate do
411
+ http.headers["SOAPAction"].should == %{"authenticate"}
412
+ end
413
+ end
414
+
415
+ it "should execute SOAP requests and return the response" do
416
+ HTTPI.expects(:post).returns(new_response)
417
+ response = client.request(:authenticate)
418
+
419
+ response.should be_a(Savon::SOAP::Response)
420
+ response.to_xml.should == Fixture.response(:authentication)
421
+ end
422
+ end
423
+
424
+ context "when encountering a SOAP fault" do
425
+ let(:client) do
426
+ Savon::Client.new do
427
+ wsdl.endpoint = Endpoint.soap
428
+ wsdl.namespace = "http://v1_0.ws.auth.order.example.com/"
429
+ end
430
+ end
431
+
432
+ before { HTTPI::expects(:post).returns(new_response(:code => 500, :body => Fixture.response(:soap_fault))) }
433
+
434
+ it "should raise a Savon::SOAP::Fault" do
435
+ lambda { client.request :authenticate }.should raise_error(Savon::SOAP::Fault)
436
+ end
437
+ end
438
+
439
+ context "when encountering an HTTP error" do
440
+ let(:client) do
441
+ Savon::Client.new do
442
+ wsdl.endpoint = Endpoint.soap
443
+ wsdl.namespace = "http://v1_0.ws.auth.order.example.com/"
444
+ end
445
+ end
446
+
447
+ before { HTTPI::expects(:post).returns(new_response(:code => 500)) }
448
+
449
+ it "should raise a Savon::HTTP::Error" do
450
+ lambda { client.request :authenticate }.should raise_error(Savon::HTTP::Error)
451
+ end
452
+ end
453
+
454
+ def new_response(options = {})
455
+ defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
456
+ response = defaults.merge options
457
+
458
+ HTTPI::Response.new response[:code], response[:headers], response[:body]
459
+ end
460
+
461
+ end