savon 2.12.0 → 2.13.1

Sign up to get free protection for your applications and to get access to all the features.
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)