savon 2.12.0 → 2.13.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 (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +98 -76
  4. data/CONTRIBUTING.md +15 -19
  5. data/Gemfile +2 -7
  6. data/README.md +15 -19
  7. data/RELEASING.md +10 -0
  8. data/lib/savon/block_interface.rb +1 -0
  9. data/lib/savon/builder.rb +98 -29
  10. data/lib/savon/client.rb +1 -0
  11. data/lib/savon/core_ext/string.rb +1 -0
  12. data/lib/savon/header.rb +2 -6
  13. data/lib/savon/http_error.rb +4 -4
  14. data/lib/savon/log_message.rb +1 -0
  15. data/lib/savon/message.rb +1 -0
  16. data/lib/savon/mock/expectation.rb +1 -0
  17. data/lib/savon/mock/spec_helper.rb +1 -0
  18. data/lib/savon/mock.rb +1 -0
  19. data/lib/savon/model.rb +1 -0
  20. data/lib/savon/operation.rb +20 -18
  21. data/lib/savon/options.rb +56 -0
  22. data/lib/savon/qualified_message.rb +4 -3
  23. data/lib/savon/request.rb +5 -0
  24. data/lib/savon/request_logger.rb +8 -2
  25. data/lib/savon/response.rb +48 -1
  26. data/lib/savon/soap_fault.rb +2 -1
  27. data/lib/savon/version.rb +2 -1
  28. data/lib/savon.rb +1 -0
  29. data/savon.gemspec +9 -8
  30. data/spec/fixtures/response/empty_soap_fault.xml +13 -0
  31. data/spec/fixtures/wsdl/elements_in_types.xml +43 -0
  32. data/spec/integration/support/application.rb +33 -1
  33. data/spec/integration/support/server.rb +1 -0
  34. data/spec/integration/zipcode_example_spec.rb +5 -8
  35. data/spec/savon/builder_spec.rb +2 -1
  36. data/spec/savon/client_spec.rb +5 -4
  37. data/spec/savon/core_ext/string_spec.rb +2 -1
  38. data/spec/savon/features/message_tag_spec.rb +2 -1
  39. data/spec/savon/http_error_spec.rb +9 -1
  40. data/spec/savon/log_message_spec.rb +2 -1
  41. data/spec/savon/message_spec.rb +2 -11
  42. data/spec/savon/mock_spec.rb +2 -1
  43. data/spec/savon/model_spec.rb +2 -1
  44. data/spec/savon/multipart_request_spec.rb +46 -0
  45. data/spec/savon/observers_spec.rb +2 -1
  46. data/spec/savon/operation_spec.rb +20 -43
  47. data/spec/savon/options_spec.rb +51 -1
  48. data/spec/savon/qualified_message_spec.rb +2 -1
  49. data/spec/savon/request_logger_spec.rb +2 -1
  50. data/spec/savon/request_spec.rb +47 -6
  51. data/spec/savon/response_spec.rb +2 -1
  52. data/spec/savon/soap_fault_spec.rb +12 -1
  53. data/spec/savon/softlayer_spec.rb +17 -2
  54. data/spec/spec_helper.rb +5 -4
  55. data/spec/support/adapters.rb +1 -0
  56. data/spec/support/endpoint.rb +1 -0
  57. data/spec/support/fixture.rb +1 -0
  58. data/spec/support/integration.rb +1 -0
  59. data/spec/support/stdout.rb +1 -0
  60. metadata +57 -34
  61. data/.travis.yml +0 -18
  62. data/donate.png +0 -0
  63. data/spec/integration/centra_spec.rb +0 -67
  64. data/spec/integration/email_example_spec.rb +0 -32
  65. data/spec/integration/random_quote_spec.rb +0 -23
  66. data/spec/integration/ratp_example_spec.rb +0 -28
  67. data/spec/integration/stockquote_example_spec.rb +0 -34
  68. data/spec/integration/temperature_example_spec.rb +0 -46
@@ -1,14 +1,20 @@
1
+ # frozen_string_literal: true
1
2
  require "nori"
2
3
  require "savon/soap_fault"
3
4
  require "savon/http_error"
4
5
 
5
6
  module Savon
6
7
  class Response
8
+ CRLF = /\r\n/
9
+ WSP = /[#{%Q|\x9\x20|}]/
7
10
 
8
11
  def initialize(http, globals, locals)
9
12
  @http = http
10
13
  @globals = globals
11
14
  @locals = locals
15
+ @attachments = []
16
+ @xml = ''
17
+ @has_parsed_body = false
12
18
 
13
19
  build_soap_and_http_errors!
14
20
  raise_soap_and_http_errors! if @globals[:raise_errors]
@@ -53,7 +59,12 @@ module Savon
53
59
  end
54
60
 
55
61
  def xml
56
- @http.body
62
+ if multipart?
63
+ parse_body unless @has_parsed_body
64
+ @xml
65
+ else
66
+ @http.body
67
+ end
57
68
  end
58
69
 
59
70
  alias_method :to_xml, :xml
@@ -74,8 +85,44 @@ module Savon
74
85
  nori.find(envelope, *path)
75
86
  end
76
87
 
88
+ def attachments
89
+ if multipart?
90
+ parse_body unless @has_parsed_body
91
+ @attachments
92
+ else
93
+ []
94
+ end
95
+ end
96
+
97
+ def multipart?
98
+ !(http.headers['content-type'] =~ /^multipart/im).nil?
99
+ end
100
+
77
101
  private
78
102
 
103
+ def boundary
104
+ return unless multipart?
105
+ Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
106
+ end
107
+
108
+ def parse_body
109
+ http.body.force_encoding Encoding::ASCII_8BIT
110
+ parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
111
+ parts[1..-1].to_a.each_with_index do |part, index|
112
+ header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
113
+ section = Mail::Part.new(
114
+ body: body_part
115
+ )
116
+ section.header = header_part
117
+ if index == 0
118
+ @xml = section.body.to_s
119
+ else
120
+ @attachments << section
121
+ end
122
+ end
123
+ @has_parsed_body = true
124
+ end
125
+
79
126
  def build_soap_and_http_errors!
80
127
  @soap_fault = SOAPFault.new(@http, nori, xml) if soap_fault?
81
128
  @http_error = HTTPError.new(@http) if http_error?
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module Savon
2
3
  class SOAPFault < Error
3
4
 
4
5
  def self.present?(http, xml = nil)
5
6
  xml ||= http.body
6
7
  fault_node = xml.include?("Fault>")
7
- soap1_fault = xml.include?("faultcode>") && xml.include?("faultstring>")
8
+ soap1_fault = xml.match(/faultcode\/?\>/) && xml.match(/faultstring\/?\>/)
8
9
  soap2_fault = xml.include?("Code>") && xml.include?("Reason>")
9
10
 
10
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.12.0'
3
+ VERSION = '2.13.1'
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.7.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
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", "~> 3.0"
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,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,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "rack"
2
3
  require "json"
3
4
 
@@ -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)