savon 2.11.2 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +103 -73
  4. data/CONTRIBUTING.md +15 -19
  5. data/Gemfile +2 -7
  6. data/README.md +26 -15
  7. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
  8. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  9. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
  10. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
  11. data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  12. data/coverage/assets/0.12.3/application.css +1 -0
  13. data/coverage/assets/0.12.3/application.js +7 -0
  14. data/coverage/assets/0.12.3/colorbox/border.png +0 -0
  15. data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
  16. data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
  17. data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
  18. data/coverage/assets/0.12.3/favicon_green.png +0 -0
  19. data/coverage/assets/0.12.3/favicon_red.png +0 -0
  20. data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
  21. data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  22. data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  23. data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  24. data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  25. data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  26. data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  27. data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  28. data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  29. data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
  30. data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
  31. data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
  32. data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
  33. data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
  34. data/coverage/assets/0.12.3/loading.gif +0 -0
  35. data/coverage/assets/0.12.3/magnify.png +0 -0
  36. data/coverage/index.html +21518 -0
  37. data/lib/savon/block_interface.rb +1 -0
  38. data/lib/savon/builder.rb +95 -29
  39. data/lib/savon/client.rb +1 -0
  40. data/lib/savon/core_ext/string.rb +1 -0
  41. data/lib/savon/header.rb +2 -6
  42. data/lib/savon/http_error.rb +4 -4
  43. data/lib/savon/log_message.rb +1 -0
  44. data/lib/savon/message.rb +1 -0
  45. data/lib/savon/mock/expectation.rb +1 -0
  46. data/lib/savon/mock/spec_helper.rb +1 -0
  47. data/lib/savon/mock.rb +1 -0
  48. data/lib/savon/model.rb +1 -0
  49. data/lib/savon/operation.rb +20 -18
  50. data/lib/savon/options.rb +70 -1
  51. data/lib/savon/qualified_message.rb +5 -4
  52. data/lib/savon/request.rb +18 -3
  53. data/lib/savon/request_logger.rb +8 -2
  54. data/lib/savon/response.rb +49 -2
  55. data/lib/savon/soap_fault.rb +2 -3
  56. data/lib/savon/version.rb +2 -1
  57. data/lib/savon.rb +1 -0
  58. data/savon.gemspec +10 -9
  59. data/spec/fixtures/response/empty_soap_fault.xml +13 -0
  60. data/spec/fixtures/response/no_body.xml +1 -0
  61. data/spec/fixtures/wsdl/elements_in_types.xml +43 -0
  62. data/spec/integration/support/application.rb +34 -2
  63. data/spec/integration/support/server.rb +1 -0
  64. data/spec/integration/zipcode_example_spec.rb +5 -8
  65. data/spec/savon/builder_spec.rb +2 -1
  66. data/spec/savon/client_spec.rb +5 -4
  67. data/spec/savon/core_ext/string_spec.rb +2 -1
  68. data/spec/savon/features/message_tag_spec.rb +2 -1
  69. data/spec/savon/http_error_spec.rb +9 -1
  70. data/spec/savon/log_message_spec.rb +2 -1
  71. data/spec/savon/message_spec.rb +2 -11
  72. data/spec/savon/mock_spec.rb +2 -1
  73. data/spec/savon/model_spec.rb +2 -1
  74. data/spec/savon/multipart_request_spec.rb +46 -0
  75. data/spec/savon/observers_spec.rb +2 -1
  76. data/spec/savon/operation_spec.rb +20 -43
  77. data/spec/savon/options_spec.rb +84 -5
  78. data/spec/savon/qualified_message_spec.rb +35 -1
  79. data/spec/savon/request_logger_spec.rb +2 -1
  80. data/spec/savon/request_spec.rb +99 -14
  81. data/spec/savon/response_spec.rb +7 -1
  82. data/spec/savon/soap_fault_spec.rb +12 -1
  83. data/spec/savon/softlayer_spec.rb +3 -2
  84. data/spec/spec_helper.rb +5 -4
  85. data/spec/support/adapters.rb +1 -0
  86. data/spec/support/endpoint.rb +1 -0
  87. data/spec/support/fixture.rb +1 -0
  88. data/spec/support/integration.rb +2 -1
  89. data/spec/support/stdout.rb +1 -0
  90. metadata +86 -33
  91. data/.travis.yml +0 -19
  92. data/donate.png +0 -0
  93. data/spec/integration/centra_spec.rb +0 -66
  94. data/spec/integration/email_example_spec.rb +0 -32
  95. data/spec/integration/random_quote_spec.rb +0 -23
  96. data/spec/integration/ratp_example_spec.rb +0 -28
  97. data/spec/integration/stockquote_example_spec.rb +0 -34
  98. data/spec/integration/temperature_example_spec.rb +0 -46
@@ -1,12 +1,11 @@
1
- require "savon"
2
-
1
+ # frozen_string_literal: true
3
2
  module Savon
4
3
  class SOAPFault < Error
5
4
 
6
5
  def self.present?(http, xml = nil)
7
6
  xml ||= http.body
8
7
  fault_node = xml.include?("Fault>")
9
- soap1_fault = xml.include?("faultcode>") && xml.include?("faultstring>")
8
+ soap1_fault = xml.match(/faultcode\/?\>/) && xml.match(/faultstring\/?\>/)
10
9
  soap2_fault = xml.include?("Code>") && xml.include?("Reason>")
11
10
 
12
11
  fault_node && (soap1_fault || soap2_fault)
data/lib/savon/version.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
- VERSION = '2.11.2'
3
+ VERSION = '2.13.0'
3
4
  end
data/lib/savon.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
3
 
3
4
  Error = Class.new(RuntimeError)
data/savon.gemspec CHANGED
@@ -12,29 +12,30 @@ Gem::Specification.new do |s|
12
12
  s.homepage = "http://savonrb.com"
13
13
  s.summary = "Heavy metal SOAP client"
14
14
  s.description = s.summary
15
- s.required_ruby_version = '>= 1.9.2'
15
+ s.required_ruby_version = '>= 2.5.0'
16
16
 
17
- s.rubyforge_project = s.name
18
17
  s.license = 'MIT'
19
18
 
20
19
  s.add_dependency "nori", "~> 2.4"
21
- s.add_dependency "httpi", "~> 2.3"
20
+ s.add_dependency "httpi", ">= 2.4.5"
22
21
  s.add_dependency "wasabi", "~> 3.4"
23
22
  s.add_dependency "akami", "~> 1.2"
24
23
  s.add_dependency "gyoku", "~> 1.2"
25
24
  s.add_dependency "builder", ">= 2.1.2"
26
- s.add_dependency "nokogiri", ">= 1.4.0"
25
+ s.add_dependency "nokogiri", ">= 1.8.1"
26
+ s.add_dependency "mail", "~> 2.5"
27
27
 
28
28
  s.add_development_dependency "rack"
29
- s.add_development_dependency "puma", "2.0.0.b4"
29
+ s.add_development_dependency "puma", ">= 4.3.8"
30
30
 
31
- s.add_development_dependency "rake", "~> 10.1"
32
- s.add_development_dependency "rspec", "~> 2.14"
31
+ s.add_development_dependency "byebug"
32
+ s.add_development_dependency "rake", ">= 12.3.3"
33
+ s.add_development_dependency "rspec", "~> 3.9"
33
34
  s.add_development_dependency "mocha", "~> 0.14"
34
- s.add_development_dependency "json", "~> 1.7"
35
+ s.add_development_dependency "json", ">= 2.3.0"
35
36
 
36
37
  ignores = File.readlines(".gitignore").grep(/\S+/).map(&:chomp)
37
- dotfiles = %w[.gitignore .travis.yml .yardopts]
38
+ dotfiles = %w[.gitignore .yardopts]
38
39
 
39
40
  all_files_without_ignores = Dir["**/*"].reject { |f|
40
41
  File.directory?(f) || ignores.any? { |i| File.fnmatch(i, f) }
@@ -0,0 +1,13 @@
1
+ <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
2
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
4
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
6
+ <SOAP-ENV:Body>
7
+ <SOAP-ENV:Fault>
8
+ <faultcode/>
9
+ <faultstring/>
10
+ <detail><soapVal><ERRNO xsi:type="xsd:string">80:1289245853:55</ERRNO></soapVal></detail>
11
+ </SOAP-ENV:Fault>
12
+ </SOAP-ENV:Body>
13
+ </SOAP-ENV:Envelope>
@@ -0,0 +1 @@
1
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">Text</soap:Envelope>
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="www.example.com/XML" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s1="http://microsoft.com/wsdl/types/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="www.example.com/XML" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
3
+ <wsdl:types>
4
+ <s:schema elementFormDefault="qualified" targetNamespace="www.example.com/XML">
5
+ <s:complexType name="Transaction" abstract="true">
6
+ <s:sequence>
7
+ <s:element minOccurs="0" maxOccurs="1" name="Qualified" type="s:string" />
8
+ </s:sequence>
9
+ </s:complexType>
10
+ <s:complexType name="TopLevelTransaction">
11
+ <s:complexContent mixed="false">
12
+ <s:extension base="tns:Transaction">
13
+ </s:extension>
14
+ </s:complexContent>
15
+ </s:complexType>
16
+ <s:element name="TopLevelTransaction">
17
+ <s:complexType>
18
+ <s:sequence>
19
+ <s:element minOccurs="0" maxOccurs="1" name="TopLevelTransaction" type="tns:TopLevelTransaction" />
20
+ </s:sequence>
21
+ </s:complexType>
22
+ </s:element>
23
+ </s:schema>
24
+ </wsdl:types>
25
+ <wsdl:message name="TopLevelTransactionSoapIn">
26
+ <wsdl:part name="parameters" element="tns:TopLevelTransaction" />
27
+ </wsdl:message>
28
+ <wsdl:portType name="XMLTESoap">
29
+ <wsdl:operation name="TopLevelTransaction">
30
+ <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">TopLevelTransaction.</wsdl:documentation>
31
+ <wsdl:input message="tns:TopLevelTransactionSoapIn" />
32
+ </wsdl:operation>
33
+ </wsdl:portType>
34
+ <wsdl:binding name="XMLTESoap12" type="tns:XMLTESoap">
35
+ <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
36
+ <wsdl:operation name="TopLevelTransaction">
37
+ <soap12:operation soapAction="www.example.com/XML/TopLevelTransaction" style="document" />
38
+ <wsdl:input>
39
+ <soap12:body use="literal" />
40
+ </wsdl:input>
41
+ </wsdl:operation>
42
+ </wsdl:binding>
43
+ </wsdl:definitions>
@@ -1,4 +1,5 @@
1
- require "rack/builder"
1
+ # frozen_string_literal: true
2
+ require "rack"
2
3
  require "json"
3
4
 
4
5
  class IntegrationServer
@@ -6,7 +7,7 @@ class IntegrationServer
6
7
  def self.respond_with(options = {})
7
8
  code = options.fetch(:code, 200)
8
9
  body = options.fetch(:body, "")
9
- headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s }
10
+ headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s }.merge options.fetch(:headers, {})
10
11
 
11
12
  [code, headers, [body]]
12
13
  end
@@ -78,5 +79,36 @@ class IntegrationServer
78
79
  run app
79
80
  end
80
81
 
82
+ map "/multipart" do
83
+ run lambda { |env|
84
+ boundary = 'mimepart_boundary'
85
+ message = Mail.new
86
+ xml_part = Mail::Part.new do
87
+ content_type 'text/xml'
88
+ body %{<?xml version='1.0' encoding='UTF-8'?>
89
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
90
+ <soapenv:Header>response header</soapenv:Header>
91
+ <soapenv:Body>response body</soapenv:Body>
92
+ </soapenv:Envelope>}
93
+ # in Content-Type the start parameter is recommended (RFC 2387)
94
+ content_id '<soap-request-body@soap>'
95
+ end
96
+ message.add_part xml_part
97
+
98
+ message.add_file File.expand_path("../../../fixtures/gzip/message.gz", __FILE__)
99
+ message.parts.last.content_location = 'message.gz'
100
+ message.parts.last.content_id = 'attachment1'
101
+
102
+ message.ready_to_send!
103
+ message.body.set_sort_order [ "text/xml" ]
104
+ message.body.encoded(message.content_transfer_encoding)
105
+
106
+ IntegrationServer.respond_with({
107
+ headers: { "Content-Type" => "multipart/related; boundary=\"#{message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\"" },
108
+ body: message.body.encoded(message.content_transfer_encoding)
109
+ })
110
+ }
111
+ end
112
+
81
113
  end
82
114
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "puma"
2
3
  require "puma/minissl"
3
4
 
@@ -1,18 +1,16 @@
1
- require "spec_helper"
2
-
3
- describe "ZIP code example" do
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
4
3
 
4
+ RSpec.describe "ZIP code example" do
5
5
  it "supports threads making requests simultaneously" do
6
6
  client = Savon.client(
7
- # The WSDL document provided by the service.
8
7
  :wsdl => "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl",
9
8
 
10
9
  # Lower timeouts so these specs don't take forever when the service is not available.
11
10
  :open_timeout => 10,
12
11
  :read_timeout => 10,
13
12
 
14
- # Disable logging for cleaner spec output.
15
- :log => false
13
+ :log => false # Disable logging for cleaner spec output.
16
14
  )
17
15
 
18
16
  mutex = Mutex.new
@@ -22,7 +20,7 @@ describe "ZIP code example" do
22
20
 
23
21
  threads = request_data.map do |blz|
24
22
  thread = Thread.new do
25
- response = call_and_fail_gracefully client, :get_bank, :message => { :blz => blz }
23
+ response = call_and_fail_gracefully(client, :get_bank, :message => { :blz => blz })
26
24
  Thread.current[:value] = response.body[:get_bank_response][:details]
27
25
  mutex.synchronize { threads_waiting -= 1 }
28
26
  end
@@ -38,5 +36,4 @@ describe "ZIP code example" do
38
36
 
39
37
  expect(values.uniq.size).to eq(values.size)
40
38
  end
41
-
42
39
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
 
3
- describe Savon::Builder do
4
+ RSpec.describe Savon::Builder do
4
5
 
5
6
  subject(:builder) { Savon::Builder.new(:authenticate, wsdl, globals, locals) }
6
7
 
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "integration/support/server"
3
4
 
4
- describe Savon::Client do
5
+ RSpec.describe Savon::Client do
5
6
 
6
7
  before :all do
7
8
  @server = IntegrationServer.run
@@ -231,15 +232,15 @@ describe Savon::Client do
231
232
  end
232
233
 
233
234
  it "raises when the operation name is not a symbol" do
234
- expect { new_client.build_request("not a symbol") }.to raise_error
235
+ expect { new_client.build_request("not a symbol") }.to raise_error ArgumentError
235
236
  end
236
237
 
237
238
  it "raises when given an unknown option via the Hash syntax" do
238
- expect { new_client.build_request(:authenticate, :invalid_local_option => true) }.to raise_error
239
+ expect { new_client.build_request(:authenticate, :invalid_local_option => true) }.to raise_error Savon::UnknownOptionError
239
240
  end
240
241
 
241
242
  it "raises when given an unknown option via the block syntax" do
242
- expect { new_client.build_request(:authenticate) { another_invalid_local_option true } }.to raise_error
243
+ expect { new_client.build_request(:authenticate) { another_invalid_local_option true } }.to raise_error Savon::UnknownOptionError
243
244
  end
244
245
  end
245
246
 
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
 
3
- describe String do
4
+ RSpec.describe String do
4
5
 
5
6
  describe "snakecase" do
6
7
  it "lowercases one word CamelCase" do
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
- describe Savon do
4
+ RSpec.describe Savon do
4
5
 
5
6
  it 'knows the message tag for :authentication' do
6
7
  message_tag = message_tag_for(:authentication, :authenticate)
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
 
3
- describe Savon::HTTPError do
4
+ RSpec.describe Savon::HTTPError do
4
5
  let(:http_error) { Savon::HTTPError.new new_response(:code => 404, :body => "Not Found") }
6
+ let(:http_error_with_empty_body) { Savon::HTTPError.new new_response(:code => 404, :body => "") }
5
7
  let(:no_error) { Savon::HTTPError.new new_response }
6
8
 
7
9
  it "inherits from Savon::Error" do
@@ -30,6 +32,12 @@ describe Savon::HTTPError do
30
32
  it "returns the HTTP error message" do
31
33
  expect(http_error.send method).to eq("HTTP error (404): Not Found")
32
34
  end
35
+
36
+ context "when the body is empty" do
37
+ it "returns the HTTP error without the body message" do
38
+ expect(http_error_with_empty_body.send method).to eq("HTTP error (404)")
39
+ end
40
+ end
33
41
  end
34
42
  end
35
43
 
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
 
3
- describe Savon::LogMessage do
4
+ RSpec.describe Savon::LogMessage do
4
5
 
5
6
  it "returns the message if it's not XML" do
6
7
  message = log_message("hello", [:password], :pretty_print).to_s
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "integration/support/server"
3
4
 
4
- describe Savon::Message do
5
+ RSpec.describe Savon::Message do
5
6
 
6
7
  before do
7
8
  @server = IntegrationServer.run
@@ -55,16 +56,6 @@ describe Savon::Message do
55
56
  end
56
57
  end
57
58
  end
58
-
59
- context 'wsa:MessageID' do
60
- let(:message_id_tag) {
61
- '<wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">'
62
- }
63
- it 'should include xmlns:wsa attribute' do
64
- response = client.call(:something, message: {})
65
- expect(response.xml).to include(message_id_tag)
66
- end
67
- end
68
59
  end
69
60
 
70
61
  end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "savon/mock/spec_helper"
3
4
 
4
- describe "Savon's mock interface" do
5
+ RSpec.describe "Savon's mock interface" do
5
6
  include Savon::SpecHelper
6
7
 
7
8
  before :all do
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "integration/support/server"
3
4
 
4
- describe Savon::Model do
5
+ RSpec.describe Savon::Model do
5
6
 
6
7
  before :all do
7
8
  @server = IntegrationServer.run
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Savon::Builder do
4
+
5
+ let(:globals) { Savon::GlobalOptions.new({ :endpoint => "http://example.co", :namespace => "http://v1.example.com" }) }
6
+ let(:no_wsdl) { Wasabi::Document.new }
7
+
8
+ it "building multipart request from inline content" do
9
+ locals = {
10
+ attachments: [
11
+ { filename: 'x1.xml', content: '<xml>abc1</xml>'},
12
+ { filename: 'x2.xml', content: '<xml>abc2</xml>'},
13
+ ]
14
+ }
15
+ builder = Savon::Builder.new(:operation1, no_wsdl, globals, Savon::LocalOptions.new(locals))
16
+ request_body = builder.to_s
17
+
18
+ expect(request_body).to include('Content-Type')
19
+ expect(request_body).to match(/<[a-z]+:operation1>/)
20
+
21
+ locals[:attachments].each do |attachment|
22
+ expect(request_body).to match(/^Content-Location: #{attachment[:filename]}\s$/)
23
+ expect(request_body).to include(Base64.encode64(attachment[:content]).strip)
24
+ end
25
+
26
+ end
27
+
28
+ it "building multipart request from file" do
29
+ locals = {
30
+ attachments: {
31
+ 'file.gz' => File.expand_path("../../fixtures/gzip/message.gz", __FILE__)
32
+ }
33
+ }
34
+ builder = Savon::Builder.new(:operation1, no_wsdl, globals, Savon::LocalOptions.new(locals))
35
+ request_body = builder.to_s
36
+
37
+ expect(request_body).to include('Content-Type')
38
+ expect(request_body).to match(/<[a-z]+:operation1>/)
39
+
40
+ locals[:attachments].each do |id, file|
41
+ expect(request_body).to match(/^Content-Location: #{id}\s$/)
42
+ expect(request_body.gsub("\r", "")).to include(Base64.encode64(File.read(file)).strip)
43
+ end
44
+
45
+ end
46
+ end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "integration/support/server"
3
4
 
4
- describe Savon do
5
+ RSpec.describe Savon do
5
6
 
6
7
  before :all do
7
8
  @server = IntegrationServer.run
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "integration/support/server"
3
4
  require "json"
4
5
  require "ostruct"
5
6
 
6
- describe Savon::Operation do
7
+ RSpec.describe Savon::Operation do
7
8
 
8
9
  let(:globals) { Savon::GlobalOptions.new(:endpoint => @server.url(:repeat), :log => false) }
9
10
  let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:taxcloud) }
@@ -108,7 +109,7 @@ describe Savon::Operation do
108
109
  it "sets the Content-Length header" do
109
110
  # XXX: probably the worst spec ever written. refactor! [dh, 2013-01-05]
110
111
  http_request = HTTPI::Request.new
111
- http_request.headers.expects(:[]=).with("Content-Length", "312")
112
+ http_request.headers.expects(:[]=).with("Content-Length", "723")
112
113
  Savon::SOAPRequest.any_instance.expects(:build).returns(http_request)
113
114
 
114
115
  new_operation(:verify_address, wsdl, globals).call
@@ -165,40 +166,28 @@ describe Savon::Operation do
165
166
  expect(actual_soap_action).to eq(%("authenticate"))
166
167
  end
167
168
 
168
- it "returns a Savon::Multipart::Response if available and requested globally" do
169
- globals.multipart true
170
-
171
- with_multipart_mocked do
172
- operation = new_operation(:authenticate, no_wsdl, globals)
173
- response = operation.call
174
-
175
- expect(response).to be_a(Savon::Multipart::Response)
169
+ it "handle multipart response" do
170
+ globals.endpoint @server.url(:multipart)
171
+ operation = new_operation(:example, no_wsdl, globals)
172
+ response = operation.call do
173
+ attachments [
174
+ { filename: 'x1.xml', content: '<xml>abc</xml>'},
175
+ { filename: 'x2.xml', content: '<xml>cde</xml>'},
176
+ ]
176
177
  end
177
- end
178
-
179
- it "returns a Savon::Multipart::Response if available and requested locally" do
180
- with_multipart_mocked do
181
- operation = new_operation(:authenticate, no_wsdl, globals)
182
- response = operation.call(:multipart => true)
183
178
 
184
- expect(response).to be_a(Savon::Multipart::Response)
185
- end
179
+ expect(response.multipart?).to be true
180
+ expect(response.header).to eq 'response header'
181
+ expect(response.body).to eq 'response body'
182
+ expect(response.attachments.first.content_id).to eq 'attachment1'
186
183
  end
187
184
 
188
- it "raises if savon-multipart is not available and it was requested globally" do
189
- globals.multipart true
190
-
191
- operation = new_operation(:authenticate, no_wsdl, globals)
185
+ it "simple request is not multipart" do
186
+ operation = new_operation(:example, no_wsdl, globals)
187
+ response = operation.call
192
188
 
193
- expect { operation.call }.
194
- to raise_error RuntimeError, /Unable to find Savon::Multipart/
195
- end
196
-
197
- it "raises if savon-multipart is not available and it was requested locally" do
198
- operation = new_operation(:authenticate, no_wsdl, globals)
199
-
200
- expect { operation.call(:multipart => true) }.
201
- to raise_error RuntimeError, /Unable to find Savon::Multipart/
189
+ expect(response.multipart?).to be false
190
+ expect(response.attachments).to be_empty
202
191
  end
203
192
  end
204
193
 
@@ -211,18 +200,6 @@ describe Savon::Operation do
211
200
  end
212
201
  end
213
202
 
214
- def with_multipart_mocked
215
- multipart_response = Class.new { def initialize(*args); end }
216
- multipart_mock = Module.new
217
- multipart_mock.const_set('Response', multipart_response)
218
-
219
- Savon.const_set('Multipart', multipart_mock)
220
-
221
- yield
222
- ensure
223
- Savon.send(:remove_const, :Multipart) if Savon.const_defined? :Multipart
224
- end
225
-
226
203
  def inspect_request(response)
227
204
  hash = JSON.parse(response.http.body)
228
205
  OpenStruct.new(hash)
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require "spec_helper"
2
3
  require "integration/support/server"
3
4
  require "json"
4
5
  require "ostruct"
5
6
  require "logger"
6
7
 
7
- describe "Options" do
8
+ RSpec.describe "Options" do
8
9
 
9
10
  before :all do
10
11
  @server = IntegrationServer.run
@@ -219,6 +220,17 @@ describe "Options" do
219
220
  expect(response.http.body).to include("<user>lea</user>")
220
221
  expect(response.http.body).to include("<password>top-secret</password>")
221
222
  end
223
+
224
+ it "qualifies elements embedded in complex types" do
225
+ client = new_client(:endpoint => @server.url(:repeat),
226
+ :wsdl => Fixture.wsdl(:elements_in_types))
227
+ msg = {":TopLevelTransaction"=>{":Qualified"=>"A Value"}}
228
+
229
+ response = client.call(:top_level_transaction, :message => msg)
230
+
231
+ expect(response.http.body.scan(/<tns:Qualified>/).count).to eq(1)
232
+ end
233
+
222
234
  end
223
235
 
224
236
  context "global :env_namespace" do
@@ -387,20 +399,67 @@ describe "Options" do
387
399
  end
388
400
  end
389
401
 
402
+ context "global :log_headers" do
403
+ it "instructs Savon to log SOAP requests and responses headers" do
404
+ stdout = mock_stdout {
405
+ client = new_client(:endpoint => @server.url, :log => true)
406
+ client.call(:authenticate)
407
+ }
408
+ soap_header = stdout.string.include? "Content-Type"
409
+ expect(soap_header).to be true
410
+ end
411
+
412
+ it "stops Savon from logging SOAP requests and responses headers" do
413
+ stdout = mock_stdout {
414
+ client = new_client(:endpoint => @server.url, :log => true, :log_headers => false)
415
+ client.call(:authenticate)
416
+ }
417
+ soap_header = stdout.string.include? "Content-Type"
418
+ expect(soap_header).to be false
419
+ end
420
+ end
421
+
390
422
  context "global :ssl_version" do
391
423
  it "sets the SSL version to use" do
392
- HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:SSLv3).twice
424
+ HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:TLSv1).twice
425
+
426
+ client = new_client(:endpoint => @server.url, :ssl_version => :TLSv1)
427
+ client.call(:authenticate)
428
+ end
429
+ end
393
430
 
394
- client = new_client(:endpoint => @server.url, :ssl_version => :SSLv3)
431
+ context "global :ssl_min_version" do
432
+ it "sets the SSL min_version to use" do
433
+ HTTPI::Auth::SSL.any_instance.expects(:min_version=).with(:TLS1_2).twice
434
+
435
+ client = new_client(:endpoint => @server.url, :ssl_min_version => :TLS1_2)
436
+ client.call(:authenticate)
437
+ end
438
+ end
439
+
440
+ context "global :ssl_max_version" do
441
+ it "sets the SSL max_version to use" do
442
+ HTTPI::Auth::SSL.any_instance.expects(:max_version=).with(:TLS1_2).twice
443
+
444
+ client = new_client(:endpoint => @server.url, :ssl_max_version => :TLS1_2)
395
445
  client.call(:authenticate)
396
446
  end
397
447
  end
398
448
 
399
449
  context "global :ssl_verify_mode" do
400
450
  it "sets the verify mode to use" do
401
- HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:none).twice
451
+ HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:peer).twice
452
+
453
+ client = new_client(:endpoint => @server.url, :ssl_verify_mode => :peer)
454
+ client.call(:authenticate)
455
+ end
456
+ end
457
+
458
+ context "global :ssl_ciphers" do
459
+ it "sets the ciphers to use" do
460
+ HTTPI::Auth::SSL.any_instance.expects(:ciphers=).with(:none).twice
402
461
 
403
- client = new_client(:endpoint => @server.url, :ssl_verify_mode => :none)
462
+ client = new_client(:endpoint => @server.url, :ssl_ciphers => :none)
404
463
  client.call(:authenticate)
405
464
  end
406
465
  end
@@ -469,6 +528,26 @@ describe "Options" do
469
528
  end
470
529
  end
471
530
 
531
+ context "global :ssl_ca_cert_path" do
532
+ it "sets the ca cert path to use" do
533
+ ca_cert_path = "../../fixtures/ssl"
534
+ HTTPI::Auth::SSL.any_instance.expects(:ca_cert_path=).with(ca_cert_path).twice
535
+
536
+ client = new_client(:endpoint => @server.url, :ssl_ca_cert_path => ca_cert_path)
537
+ client.call(:authenticate)
538
+ end
539
+ end
540
+
541
+ context "global :ssl_ca_cert_store" do
542
+ it "sets the cert store to use" do
543
+ cert_store = OpenSSL::X509::Store.new
544
+ HTTPI::Auth::SSL.any_instance.expects(:cert_store=).with(cert_store).twice
545
+
546
+ client = new_client(:endpoint => @server.url, :ssl_cert_store => cert_store)
547
+ client.call(:authenticate)
548
+ end
549
+ end
550
+
472
551
  context "global :ssl_ca_cert" do
473
552
  it "sets the ca cert file to use" do
474
553
  ca_cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read