savon-ng-1.6 2.4.0
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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +15 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +1024 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +8 -0
- data/LICENSE +20 -0
- data/README.md +81 -0
- data/Rakefile +14 -0
- data/donate.png +0 -0
- data/lib/savon/block_interface.rb +26 -0
- data/lib/savon/builder.rb +166 -0
- data/lib/savon/client.rb +88 -0
- data/lib/savon/core_ext/string.rb +29 -0
- data/lib/savon/header.rb +70 -0
- data/lib/savon/http_error.rb +27 -0
- data/lib/savon/log_message.rb +48 -0
- data/lib/savon/message.rb +36 -0
- data/lib/savon/mock/expectation.rb +71 -0
- data/lib/savon/mock/spec_helper.rb +62 -0
- data/lib/savon/mock.rb +5 -0
- data/lib/savon/model.rb +80 -0
- data/lib/savon/operation.rb +127 -0
- data/lib/savon/options.rb +330 -0
- data/lib/savon/qualified_message.rb +49 -0
- data/lib/savon/request.rb +89 -0
- data/lib/savon/request_logger.rb +48 -0
- data/lib/savon/response.rb +112 -0
- data/lib/savon/soap_fault.rb +48 -0
- data/lib/savon/version.rb +5 -0
- data/lib/savon.rb +27 -0
- data/savon.gemspec +47 -0
- data/spec/fixtures/gzip/message.gz +0 -0
- data/spec/fixtures/response/another_soap_fault.xml +14 -0
- data/spec/fixtures/response/authentication.xml +14 -0
- data/spec/fixtures/response/header.xml +13 -0
- data/spec/fixtures/response/list.xml +18 -0
- data/spec/fixtures/response/multi_ref.xml +39 -0
- data/spec/fixtures/response/soap_fault.xml +8 -0
- data/spec/fixtures/response/soap_fault12.xml +18 -0
- data/spec/fixtures/response/taxcloud.xml +1 -0
- data/spec/fixtures/ssl/client_cert.pem +16 -0
- data/spec/fixtures/ssl/client_encrypted_key.pem +30 -0
- data/spec/fixtures/ssl/client_encrypted_key_cert.pem +24 -0
- data/spec/fixtures/ssl/client_key.pem +15 -0
- data/spec/fixtures/wsdl/authentication.xml +63 -0
- data/spec/fixtures/wsdl/betfair.xml +2981 -0
- data/spec/fixtures/wsdl/edialog.xml +15416 -0
- data/spec/fixtures/wsdl/interhome.xml +2137 -0
- data/spec/fixtures/wsdl/lower_camel.xml +52 -0
- data/spec/fixtures/wsdl/multiple_namespaces.xml +92 -0
- data/spec/fixtures/wsdl/multiple_types.xml +60 -0
- data/spec/fixtures/wsdl/taxcloud.xml +934 -0
- data/spec/fixtures/wsdl/team_software.xml +1 -0
- data/spec/fixtures/wsdl/vies.xml +176 -0
- data/spec/fixtures/wsdl/wasmuth.xml +153 -0
- data/spec/integration/email_example_spec.rb +32 -0
- data/spec/integration/ratp_example_spec.rb +28 -0
- data/spec/integration/stockquote_example_spec.rb +28 -0
- data/spec/integration/support/application.rb +82 -0
- data/spec/integration/support/server.rb +84 -0
- data/spec/integration/temperature_example_spec.rb +46 -0
- data/spec/integration/zipcode_example_spec.rb +42 -0
- data/spec/savon/builder_spec.rb +86 -0
- data/spec/savon/client_spec.rb +193 -0
- data/spec/savon/core_ext/string_spec.rb +37 -0
- data/spec/savon/features/message_tag_spec.rb +61 -0
- data/spec/savon/http_error_spec.rb +49 -0
- data/spec/savon/log_message_spec.rb +33 -0
- data/spec/savon/message_spec.rb +40 -0
- data/spec/savon/mock_spec.rb +157 -0
- data/spec/savon/model_spec.rb +154 -0
- data/spec/savon/observers_spec.rb +92 -0
- data/spec/savon/operation_spec.rb +211 -0
- data/spec/savon/options_spec.rb +772 -0
- data/spec/savon/request_spec.rb +493 -0
- data/spec/savon/response_spec.rb +258 -0
- data/spec/savon/soap_fault_spec.rb +126 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/endpoint.rb +25 -0
- data/spec/support/fixture.rb +39 -0
- data/spec/support/integration.rb +9 -0
- data/spec/support/stdout.rb +25 -0
- metadata +308 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require "puma"
|
2
|
+
require "puma/minissl"
|
3
|
+
|
4
|
+
require "integration/support/application"
|
5
|
+
|
6
|
+
class IntegrationServer
|
7
|
+
|
8
|
+
def self.run(options = {})
|
9
|
+
server = new(options)
|
10
|
+
server.run
|
11
|
+
server
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.ssl_ca_file; integration_fixture("ca_all.pem") end
|
15
|
+
def self.ssl_key_file; integration_fixture("server.key") end
|
16
|
+
def self.ssl_cert_file; integration_fixture("server.cert") end
|
17
|
+
|
18
|
+
def self.integration_fixture(file)
|
19
|
+
file = File.expand_path("../../fixtures/#{file}", __FILE__)
|
20
|
+
raise "No such file '#{file}'" unless File.exist? file
|
21
|
+
file
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(options = {})
|
25
|
+
@app = Application
|
26
|
+
@host = options.fetch(:host, "localhost")
|
27
|
+
@port = options.fetch(:port, 17172)
|
28
|
+
@ssl = options.fetch(:ssl, false)
|
29
|
+
|
30
|
+
@server = Puma::Server.new(app, events)
|
31
|
+
|
32
|
+
if ssl?
|
33
|
+
add_ssl_listener
|
34
|
+
else
|
35
|
+
add_tcp_listener
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :app, :host, :port, :server
|
40
|
+
|
41
|
+
def url(path = "")
|
42
|
+
protocol = ssl? ? "https" : "http"
|
43
|
+
File.join "#{protocol}://#{host}:#{port}/", path.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def ssl?
|
47
|
+
@ssl
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
server.run
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
server.stop(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def events
|
61
|
+
Puma::Events.new($stdout, $stderr)
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_tcp_listener
|
65
|
+
server.add_tcp_listener(host, port)
|
66
|
+
rescue Errno::EADDRINUSE
|
67
|
+
raise "Panther is already running at #{url}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_ssl_listener
|
71
|
+
server.add_ssl_listener(host, port, ssl_context)
|
72
|
+
end
|
73
|
+
|
74
|
+
def ssl_context
|
75
|
+
context = Puma::MiniSSL::Context.new
|
76
|
+
|
77
|
+
context.key = IntegrationServer.ssl_key_file
|
78
|
+
context.cert = IntegrationServer.ssl_cert_file
|
79
|
+
context.verify_mode = Puma::MiniSSL::VERIFY_PEER
|
80
|
+
|
81
|
+
context
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Temperature example" do
|
4
|
+
|
5
|
+
it "converts 30 degrees celsius to 86 degrees fahrenheit" do
|
6
|
+
client = Savon.client do
|
7
|
+
# The WSDL document provided by the service.
|
8
|
+
wsdl "http://www.webservicex.net/ConvertTemperature.asmx?WSDL"
|
9
|
+
|
10
|
+
# Needed because (up until now), Savon doesn't match XS types to Hash keys,
|
11
|
+
# but defaults to convert Hash message Symbols (like :from_unit) to lowerCamelCase.
|
12
|
+
# The service expects these to be CamelCase instead. Look at Savon's log output
|
13
|
+
# and compare it with an example request generated by soapUI.
|
14
|
+
convert_request_keys_to :camelcase
|
15
|
+
|
16
|
+
# Lower timeouts so these specs don't take forever when the service is not available.
|
17
|
+
open_timeout 10
|
18
|
+
read_timeout 10
|
19
|
+
|
20
|
+
# Disable logging for cleaner spec output.
|
21
|
+
log false
|
22
|
+
end
|
23
|
+
|
24
|
+
response = call_and_fail_gracefully(client, :convert_temp) do
|
25
|
+
# For the corrent values to pass for :from_unit and :to_unit, I searched the WSDL for
|
26
|
+
# the "FromUnit" type which is a "TemperatureUnit" enumeration that looks like this:
|
27
|
+
#
|
28
|
+
# <s:simpleType name="TemperatureUnit">
|
29
|
+
# <s:restriction base="s:string">
|
30
|
+
# <s:enumeration value="degreeCelsius"/>
|
31
|
+
# <s:enumeration value="degreeFahrenheit"/>
|
32
|
+
# <s:enumeration value="degreeRankine"/>
|
33
|
+
# <s:enumeration value="degreeReaumur"/>
|
34
|
+
# <s:enumeration value="kelvin"/>
|
35
|
+
# </s:restriction>
|
36
|
+
# </s:simpleType>
|
37
|
+
#
|
38
|
+
# Support for XS schema types needs to be improved.
|
39
|
+
message(:temperature => 30, :from_unit => "degreeCelsius", :to_unit => "degreeFahrenheit")
|
40
|
+
end
|
41
|
+
|
42
|
+
fahrenheit = response.body[:convert_temp_response][:convert_temp_result]
|
43
|
+
expect(fahrenheit).to eq("86")
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "ZIP code example" do
|
4
|
+
|
5
|
+
it "supports threads making requests simultaneously" do
|
6
|
+
client = Savon.client(
|
7
|
+
# The WSDL document provided by the service.
|
8
|
+
:wsdl => "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl",
|
9
|
+
|
10
|
+
# Lower timeouts so these specs don't take forever when the service is not available.
|
11
|
+
:open_timeout => 10,
|
12
|
+
:read_timeout => 10,
|
13
|
+
|
14
|
+
# Disable logging for cleaner spec output.
|
15
|
+
:log => false
|
16
|
+
)
|
17
|
+
|
18
|
+
mutex = Mutex.new
|
19
|
+
|
20
|
+
request_data = [70070010, 24050110, 20050550]
|
21
|
+
threads_waiting = request_data.size
|
22
|
+
|
23
|
+
threads = request_data.map do |blz|
|
24
|
+
thread = Thread.new do
|
25
|
+
response = call_and_fail_gracefully client, :get_bank, :message => { :blz => blz }
|
26
|
+
Thread.current[:value] = response.body[:get_bank_response][:details]
|
27
|
+
mutex.synchronize { threads_waiting -= 1 }
|
28
|
+
end
|
29
|
+
|
30
|
+
thread.abort_on_exception = true
|
31
|
+
thread
|
32
|
+
end
|
33
|
+
|
34
|
+
sleep(1) until threads_waiting == 0
|
35
|
+
|
36
|
+
threads.each(&:kill)
|
37
|
+
values = threads.map { |thr| thr[:value] }.compact
|
38
|
+
|
39
|
+
values.uniq.size.should == values.size
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Savon::Builder do
|
4
|
+
|
5
|
+
subject(:builder) { Savon::Builder.new(:authenticate, wsdl, globals, locals) }
|
6
|
+
|
7
|
+
let(:globals) { Savon::GlobalOptions.new }
|
8
|
+
let(:locals) { Savon::LocalOptions.new }
|
9
|
+
let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:authentication) }
|
10
|
+
let(:no_wsdl) { Wasabi::Document.new }
|
11
|
+
|
12
|
+
describe "#pretty" do
|
13
|
+
it "returns the pretty printed request" do
|
14
|
+
expect(builder.pretty).to include("<env:Body>\n <tns:authenticate/>")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#to_s" do
|
19
|
+
it "includes the global :env_namespace if it's available" do
|
20
|
+
globals[:env_namespace] = :soapenv
|
21
|
+
expect(builder.to_s).to include("<soapenv:Envelope")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "defaults to include the default envelope namespace of :env" do
|
25
|
+
expect(builder.to_s).to include("<env:Envelope")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "includes the target namespace from the WSDL" do
|
29
|
+
expect(builder.to_s).to include('xmlns:tns="http://v1_0.ws.auth.order.example.com/"')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "includes the target namespace from the global :namespace if it's available" do
|
33
|
+
globals[:namespace] = "http://v1.example.com"
|
34
|
+
expect(builder.to_s).to include('xmlns:tns="http://v1.example.com"')
|
35
|
+
end
|
36
|
+
|
37
|
+
it "includes the local :message_tag if available" do
|
38
|
+
locals[:message_tag] = "doAuthenticate"
|
39
|
+
expect(builder.to_s).to include("<tns:doAuthenticate>")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "includes the message tag from the WSDL if its available" do
|
43
|
+
expect(builder.to_s).to include("<tns:authenticate>")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "includes a message tag created by Gyoku if both option and WSDL are missing" do
|
47
|
+
globals[:namespace] = "http://v1.example.com"
|
48
|
+
|
49
|
+
locals = Savon::LocalOptions.new
|
50
|
+
builder = Savon::Builder.new(:authenticate, no_wsdl, globals, locals)
|
51
|
+
|
52
|
+
expect(builder.to_s).to include("<wsdl:authenticate>")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "uses the global :namespace_identifier option if it's available" do
|
56
|
+
globals[:namespace_identifier] = :v1
|
57
|
+
expect(builder.to_s).to include("<v1:authenticate>")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "uses the WSDL's namespace_identifier if the global option was not specified" do
|
61
|
+
expect(builder.to_s).to include("<tns:authenticate>")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "uses the default :wsdl identifier if both option and WSDL were not specified" do
|
65
|
+
globals[:namespace] = "http://v1.example.com"
|
66
|
+
|
67
|
+
builder = Savon::Builder.new(:authenticate, no_wsdl, globals, locals)
|
68
|
+
expect(builder.to_s).to include("<wsdl:authenticate>")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "uses the global :element_form_default option if it's available " do
|
72
|
+
globals[:element_form_default] = :qualified
|
73
|
+
locals[:message] = { :username => "luke", :password => "secret" }
|
74
|
+
|
75
|
+
expect(builder.to_s).to include("<tns:username>luke</tns:username>")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "uses the WSDL's element_form_default value if the global option was set specified" do
|
79
|
+
locals[:message] = { :username => "luke", :password => "secret" }
|
80
|
+
wsdl.element_form_default = :qualified
|
81
|
+
|
82
|
+
expect(builder.to_s).to include("<tns:username>luke</tns:username>")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe Savon::Client do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
@server = IntegrationServer.run
|
8
|
+
end
|
9
|
+
|
10
|
+
after :all do
|
11
|
+
@server.stop
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".new" do
|
15
|
+
it "supports a block without arguments to create a client with global options" do
|
16
|
+
client = Savon.client do
|
17
|
+
wsdl Fixture.wsdl(:authentication)
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(client.globals[:wsdl]).to eq(Fixture.wsdl(:authentication))
|
21
|
+
end
|
22
|
+
|
23
|
+
it "supports a block with one argument to create a client with global options" do
|
24
|
+
client = Savon.client do |globals|
|
25
|
+
globals.wsdl Fixture.wsdl(:authentication)
|
26
|
+
end
|
27
|
+
|
28
|
+
expect(client.globals[:wsdl]).to eq(Fixture.wsdl(:authentication))
|
29
|
+
end
|
30
|
+
|
31
|
+
it "builds an HTTPI request for Wasabi" do
|
32
|
+
http_request = mock
|
33
|
+
wsdl_request = mock(:build => http_request)
|
34
|
+
Savon::WSDLRequest.expects(:new).with(instance_of(Savon::GlobalOptions)).returns(wsdl_request)
|
35
|
+
|
36
|
+
Wasabi::Document.any_instance.expects(:request=).with(http_request)
|
37
|
+
Savon.client(:wsdl => "http://example.com")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises if initialized with anything other than a Hash" do
|
41
|
+
expect { Savon.client("http://example.com") }.
|
42
|
+
to raise_error(Savon::InitializationError, /Some code tries to initialize Savon with the "http:\/\/example\.com" \(String\)/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises if not initialized with either a :wsdl or both :endpoint and :namespace options" do
|
46
|
+
expect { Savon.client(:endpoint => "http://example.com") }.
|
47
|
+
to raise_error(Savon::InitializationError, /Expected either a WSDL document or the SOAP endpoint and target namespace options/)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "raises a when given an unknown option via the Hash syntax" do
|
51
|
+
expect { Savon.client(:invalid_global_option => true) }.
|
52
|
+
to raise_error(Savon::UnknownOptionError, "Unknown global option: :invalid_global_option")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "raises a when given an unknown option via the block syntax" do
|
56
|
+
expect { Savon.client { another_invalid_global_option true } }.
|
57
|
+
to raise_error(Savon::UnknownOptionError, "Unknown global option: :another_invalid_global_option")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#globals" do
|
62
|
+
it "returns the current set of global options" do
|
63
|
+
expect(new_client.globals).to be_an_instance_of(Savon::GlobalOptions)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#service_name" do
|
68
|
+
it "returns the name of the service" do
|
69
|
+
expect(new_client.service_name).to eq('AuthenticationWebServiceImplService')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#operations" do
|
74
|
+
it "returns all operation names" do
|
75
|
+
operations = new_client.operations
|
76
|
+
expect(operations).to eq([:authenticate])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises when there is no WSDL document" do
|
80
|
+
expect { new_client_without_wsdl.operations }.to raise_error("Unable to inspect the service without a WSDL document.")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#operation" do
|
85
|
+
it "returns a new SOAP operation" do
|
86
|
+
operation = new_client.operation(:authenticate)
|
87
|
+
expect(operation).to be_a(Savon::Operation)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "raises if there's no such SOAP operation" do
|
91
|
+
expect { new_client.operation(:does_not_exist) }.
|
92
|
+
to raise_error(Savon::UnknownOperationError)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "does not raise when there is no WSDL document" do
|
96
|
+
new_client_without_wsdl.operation(:does_not_exist)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#call" do
|
101
|
+
it "calls a new SOAP operation" do
|
102
|
+
locals = { :message => { :symbol => "AAPL" } }
|
103
|
+
soap_response = new_soap_response
|
104
|
+
|
105
|
+
wsdl = Wasabi::Document.new('http://example.com')
|
106
|
+
operation = Savon::Operation.new(:authenticate, wsdl, Savon::GlobalOptions.new)
|
107
|
+
operation.expects(:call).with(locals).returns(soap_response)
|
108
|
+
|
109
|
+
Savon::Operation.expects(:create).with(
|
110
|
+
:authenticate,
|
111
|
+
instance_of(Wasabi::Document),
|
112
|
+
instance_of(Savon::GlobalOptions)
|
113
|
+
).returns(operation)
|
114
|
+
|
115
|
+
response = new_client.call(:authenticate, locals)
|
116
|
+
expect(response).to eq(soap_response)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "supports a block without arguments to call an operation with local options" do
|
120
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
121
|
+
|
122
|
+
response = client.call(:authenticate) do
|
123
|
+
message(:symbol => "AAPL" )
|
124
|
+
end
|
125
|
+
|
126
|
+
expect(response.http.body).to include("<symbol>AAPL</symbol>")
|
127
|
+
end
|
128
|
+
|
129
|
+
it "supports a block with one argument to call an operation with local options" do
|
130
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
131
|
+
|
132
|
+
# supports instance variables!
|
133
|
+
@instance_variable = { :symbol => "AAPL" }
|
134
|
+
|
135
|
+
response = client.call(:authenticate) do |locals|
|
136
|
+
locals.message(@instance_variable)
|
137
|
+
end
|
138
|
+
|
139
|
+
expect(response.http.body).to include("<symbol>AAPL</symbol>")
|
140
|
+
end
|
141
|
+
|
142
|
+
it "accepts arguments for the message tag" do
|
143
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
144
|
+
response = client.call(:authenticate, :attributes => { "ID" => "ABC321"})
|
145
|
+
|
146
|
+
expect(response.http.body).to include('<tns:authenticate ID="ABC321">')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "raises when the operation name is not a symbol" do
|
150
|
+
expect { new_client.call("not a symbol") }.to raise_error(
|
151
|
+
ArgumentError,
|
152
|
+
"Expected the first parameter (the name of the operation to call) to be a symbol\n" \
|
153
|
+
"Actual: \"not a symbol\" (String)"
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "raises a when given an unknown option via the Hash syntax" do
|
158
|
+
expect { new_client.call(:authenticate, :invalid_local_option => true) }.
|
159
|
+
to raise_error(Savon::UnknownOptionError, "Unknown local option: :invalid_local_option")
|
160
|
+
end
|
161
|
+
|
162
|
+
it "raises a when given an unknown option via the block syntax" do
|
163
|
+
expect { new_client.call(:authenticate) { another_invalid_local_option true } }.
|
164
|
+
to raise_error(Savon::UnknownOptionError, "Unknown local option: :another_invalid_local_option")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def new_http_response(options = {})
|
169
|
+
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
|
170
|
+
response = defaults.merge options
|
171
|
+
|
172
|
+
HTTPI::Response.new response[:code], response[:headers], response[:body]
|
173
|
+
end
|
174
|
+
|
175
|
+
def new_soap_response(options = {})
|
176
|
+
http = new_http_response(options)
|
177
|
+
globals = Savon::GlobalOptions.new
|
178
|
+
locals = Savon::LocalOptions.new
|
179
|
+
|
180
|
+
Savon::Response.new(http, globals, locals)
|
181
|
+
end
|
182
|
+
|
183
|
+
def new_client(globals = {})
|
184
|
+
globals = { :wsdl => Fixture.wsdl(:authentication), :log => false }.merge(globals)
|
185
|
+
Savon.client(globals)
|
186
|
+
end
|
187
|
+
|
188
|
+
def new_client_without_wsdl(globals = {})
|
189
|
+
globals = { :endpoint => "http://example.co", :namespace => "http://v1.example.com", :log => false }.merge(globals)
|
190
|
+
Savon.client(globals)
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
|
5
|
+
describe "snakecase" do
|
6
|
+
it "lowercases one word CamelCase" do
|
7
|
+
"Merb".snakecase.should == "merb"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "makes one underscore snakecase two word CamelCase" do
|
11
|
+
"MerbCore".snakecase.should == "merb_core"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "handles CamelCase with more than 2 words" do
|
15
|
+
"SoYouWantContributeToMerbCore".snakecase.should == "so_you_want_contribute_to_merb_core"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "handles CamelCase with more than 2 capital letter in a row" do
|
19
|
+
"CNN".snakecase.should == "cnn"
|
20
|
+
"CNNNews".snakecase.should == "cnn_news"
|
21
|
+
"HeadlineCNNNews".snakecase.should == "headline_cnn_news"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "does NOT change one word lowercase" do
|
25
|
+
"merb".snakecase.should == "merb"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "leaves snake_case as is" do
|
29
|
+
"merb_core".snakecase.should == "merb_core"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "converts period characters to underscores" do
|
33
|
+
"User.GetEmail".snakecase.should == "user_get_email"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Savon do
|
4
|
+
|
5
|
+
it 'knows the message tag for :authentication' do
|
6
|
+
message_tag = message_tag_for(:authentication, :authenticate)
|
7
|
+
expect(message_tag).to eq(['http://v1_0.ws.auth.order.example.com/', 'authenticate'])
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'knows the message tag for :taxcloud' do
|
11
|
+
message_tag = message_tag_for(:taxcloud, :verify_address)
|
12
|
+
expect(message_tag).to eq(['http://taxcloud.net', 'VerifyAddress'])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'knows the message tag for :team_software' do
|
16
|
+
message_tag = message_tag_for(:team_software, :login)
|
17
|
+
expect(message_tag).to eq(['http://tempuri.org/', 'Login'])
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'knows the message tag for :interhome' do
|
21
|
+
message_tag = message_tag_for(:interhome, :price_list)
|
22
|
+
expect(message_tag).to eq(['http://www.interhome.com/webservice', 'PriceList'])
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'knows the message tag for :betfair' do
|
26
|
+
message_tag = message_tag_for(:betfair, :get_bet)
|
27
|
+
expect(message_tag).to eq(['http://www.betfair.com/publicapi/v5/BFExchangeService/', 'getBet'])
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'knows the message tag for :vies' do
|
31
|
+
message_tag = message_tag_for(:vies, :check_vat)
|
32
|
+
expect(message_tag).to eq(['urn:ec.europa.eu:taxud:vies:services:checkVat:types', 'checkVat'])
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'knows the message tag for :wasmuth' do
|
36
|
+
message_tag = message_tag_for(:wasmuth, :get_st_tables)
|
37
|
+
expect(message_tag).to eq(['http://ws.online.msw/', 'getStTables'])
|
38
|
+
end
|
39
|
+
|
40
|
+
def message_tag_for(fixture, operation_name)
|
41
|
+
globals = Savon::GlobalOptions.new(:log => false)
|
42
|
+
wsdl = Wasabi::Document.new Fixture.wsdl(fixture)
|
43
|
+
operation = Savon::Operation.create(operation_name, wsdl, globals)
|
44
|
+
request_xml = operation.build.to_s
|
45
|
+
|
46
|
+
nsid, local = extract_message_tag_from_request(request_xml)
|
47
|
+
namespace = extract_namespace_from_request(nsid, request_xml)
|
48
|
+
|
49
|
+
[namespace, local]
|
50
|
+
end
|
51
|
+
|
52
|
+
def extract_message_tag_from_request(xml)
|
53
|
+
match = xml.match(/<\w+?:Body><(.+?):(.+?)>/)
|
54
|
+
[ match[1], match[2] ]
|
55
|
+
end
|
56
|
+
|
57
|
+
def extract_namespace_from_request(nsid, xml)
|
58
|
+
xml.match(/xmlns:#{nsid}="(.+?)"/)[1]
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Savon::HTTPError do
|
4
|
+
let(:http_error) { Savon::HTTPError.new new_response(:code => 404, :body => "Not Found") }
|
5
|
+
let(:no_error) { Savon::HTTPError.new new_response }
|
6
|
+
|
7
|
+
it "inherits from Savon::Error" do
|
8
|
+
expect(Savon::HTTPError.ancestors).to include(Savon::Error)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".present?" do
|
12
|
+
it "returns true if there was an HTTP error" do
|
13
|
+
http = new_response(:code => 404, :body => "Not Found")
|
14
|
+
expect(Savon::HTTPError.present? http).to be_true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns false unless there was an HTTP error" do
|
18
|
+
expect(Savon::HTTPError.present? new_response).to be_false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#http" do
|
23
|
+
it "returns the HTTPI::Response" do
|
24
|
+
expect(http_error.http).to be_a(HTTPI::Response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
[:message, :to_s].each do |method|
|
29
|
+
describe "##{method}" do
|
30
|
+
it "returns the HTTP error message" do
|
31
|
+
expect(http_error.send method).to eq("HTTP error (404): Not Found")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#to_hash" do
|
37
|
+
it "returns the HTTP response details as a Hash" do
|
38
|
+
expect(http_error.to_hash).to eq(:code => 404, :headers => {}, :body => "Not Found")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def new_response(options = {})
|
43
|
+
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
|
44
|
+
response = defaults.merge options
|
45
|
+
|
46
|
+
HTTPI::Response.new response[:code], response[:headers], response[:body]
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Savon::LogMessage do
|
4
|
+
|
5
|
+
it "returns the message if it's not XML" do
|
6
|
+
message = log_message("hello", [:password], :pretty_print).to_s
|
7
|
+
expect(message).to eq("hello")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "returns the message if it shouldn't be filtered or pretty printed" do
|
11
|
+
Nokogiri.expects(:XML).never
|
12
|
+
|
13
|
+
message = log_message("<hello/>", [], false).to_s
|
14
|
+
expect(message).to eq("<hello/>")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "pretty prints a given message" do
|
18
|
+
message = log_message("<envelope><body>hello</body></envelope>", [], :pretty_print).to_s
|
19
|
+
|
20
|
+
expect(message).to include("\n<envelope>")
|
21
|
+
expect(message).to include("\n <body>")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "filters tags in a given message" do
|
25
|
+
message = log_message("<root><password>secret</password></root>", [:password], false).to_s
|
26
|
+
expect(message).to include("<password>***FILTERED***</password>")
|
27
|
+
end
|
28
|
+
|
29
|
+
def log_message(*args)
|
30
|
+
Savon::LogMessage.new(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
3
|
+
|
4
|
+
describe Savon::Message do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@server = IntegrationServer.run
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
@server.stop
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with a qualified message" do
|
15
|
+
it "converts request Hash keys for which there is not namespace" do
|
16
|
+
client = Savon.client(
|
17
|
+
:endpoint => @server.url(:repeat),
|
18
|
+
:namespace => 'http://example.com',
|
19
|
+
:log => false,
|
20
|
+
|
21
|
+
:element_form_default => :qualified,
|
22
|
+
:convert_request_keys_to => :camelcase,
|
23
|
+
|
24
|
+
:convert_response_tags_to => nil
|
25
|
+
)
|
26
|
+
|
27
|
+
message = {
|
28
|
+
:email_count => 3,
|
29
|
+
:user_name => 'josh',
|
30
|
+
:order! => [:user_name, :email_count]
|
31
|
+
}
|
32
|
+
|
33
|
+
response = client.call(:something, :message => message)
|
34
|
+
body = response.hash['Envelope']['Body']
|
35
|
+
|
36
|
+
expect(response.xml).to include('<wsdl:UserName>josh</wsdl:UserName><wsdl:EmailCount>3</wsdl:EmailCount>')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|